AD LDS WDigest Hash Calculation

This commit is contained in:
Michael Grafnetter 2019-05-26 18:34:38 +02:00
parent 12a31dc238
commit 28544d7f8a
2 changed files with 35 additions and 6 deletions

View File

@ -1,6 +1,7 @@
namespace DSInternals.Common.Cryptography.Test
{
using DSInternals.Common;
using DSInternals.Common.Data;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
@ -94,6 +95,21 @@
Assert.AreEqual(WDigestHash.HashCount, hashes.Length);
}
[TestMethod]
public void WDigestHash_Compute_LDS()
{
// Property value taken from AD LDS
string expectedBlob = "3100011d000000000000000000000000af4156909297baece4e553b731d9d552da58c12e5880cad54a3f909c07b0792eec9d262783c811ad9cc7aeb1ab019fe0af4156909297baece4e553b731d9d552af4156909297baece4e553b731d9d552ec9d262783c811ad9cc7aeb1ab019fe0da58c12e5880cad54a3f909c07b0792e95e11e920c7405ca6639d932888cc11c53d9e953141b322f29eb1a02798c299042eea90067734b2a81abbc79618cc1519d9aac28e63107a495266e755f7a2babac6b5dfb3bfa3b7039058310d5b36ccc7c97824dd59b9a6a053b5fae3d8603f20a5e2bc3111c759efb3a91e3740633e83b12daad09e83cb92477ac59993c5a843b12daad09e83cb92477ac59993c5a843b12daad09e83cb92477ac59993c5a8471e7228faba51df979beef06b34f791820aa1475f40540f0db59f9d2d93fc3da5f88f6d82d185741a82c7fa79e801c6d8eae592a0ce55271a17de0b39cf1e5e9923354fb99887724cf2b10e88108776b427804b8a6d6ed92c11ecb94e40da72c0c997bc1906b1f0092d61bd0efee428b0c997bc1906b1f0092d61bd0efee428b0c997bc1906b1f0092d61bd0efee428bd93e9a9a7452dff036a67a5d5d333d7c4effe21b0afc142382943ef26024649aaa6a6a95c237f2b8efa2f13ecb0cb220";
string password = @"Pa$$w0rd";
string userDN = "CN=john,DC=dsinternals,DC=com";
string namingContext = "DC=dsinternals,DC=com";
// Compute and compare
byte[][] hashes = WDigestHash.ComputeHash(password.ToSecureString(), userDN, namingContext);
byte[] blob = WDigestHash.Encode(hashes);
Assert.AreEqual(expectedBlob, blob.ToHex());
}
[TestMethod]
public void WDigestHash_EncodeProperty()
{
@ -104,4 +120,4 @@
Assert.AreEqual(blob.ToHex(), newBlob.ToHex());
}
}
}
}

View File

@ -33,7 +33,7 @@
private const byte DefaultReserved1Value = (byte)'1';
/// <summary>
/// Calculates WDigest hashes of a password.
/// Calculates WDigest hashes of a password, as used by AD DS.
/// </summary>
/// <param name="password">User's password.</param>
/// <param name="userPrincipalName">The userPrincipalName attribute value.</param>
@ -47,11 +47,11 @@
// Validate the input
Validator.AssertNotNull(password, "password");
Validator.AssertNotNullOrWhiteSpace(samAccountName, "samAccountName");
Validator.AssertNotNullOrWhiteSpace(netBiosDomainName, "netBiosDomainName");
Validator.AssertNotNullOrWhiteSpace(dnsDomainName, "dnsDomainName");
Validator.AssertNotNull(netBiosDomainName, "netBiosDomainName");
Validator.AssertNotNull(dnsDomainName, "dnsDomainName");
// Note that a user does not need to have a UPN.
if(String.IsNullOrEmpty(userPrincipalName))
if(userPrincipalName == null)
{
// Construct the UPN as samAccountName@dnsDomainName
userPrincipalName = String.Format(@"{0}@{1}", samAccountName, dnsDomainName);
@ -155,6 +155,19 @@
return result;
}
/// <summary>
/// Calculates WDigest hashes of a password, as used by AD LDS.
/// </summary>
/// <param name="password">Account's password.</param>
/// <param name="accountDN">Distinguished name of the account.</param>
/// <param name="namingContext">Distinguished name of the account's naming context.</param>
/// <returns>29 MD5 hashes.</returns>
/// <remarks>SecureString is copied into managed memory while calculating the hashes, which is not the best way to deal with it.</remarks>
public static byte[][] ComputeHash(SecureString password, string accountDN, string namingContext)
{
return ComputeHash(password, String.Empty, accountDN, String.Empty, namingContext);
}
/// <summary>
/// Parses the WDIGEST_CREDENTIALS structure within the supplementalCredentials attribute.
/// </summary>
@ -242,4 +255,4 @@
return Encoding.UTF8.GetBytes(concatenatedString);
}
}
}
}