1- # Create a function which computes a password hash compatible with WebCTRL (salted Sha512)
1+ # Create a function which computes a password hash compatible with WebCTRL (salted Sha512 or PBKDF2)
2+ # WebCTRL8.0+ is compatible with SSHA512
3+ # WebCTRL10.0+ is compatible with PBKDF2
24function Get-PasswordHash {
35 param (
46 [Parameter (Mandatory = $true )]
5- [string ]$Password
7+ [string ]$Password ,
8+ [Parameter (Mandatory = $false )]
9+ [ValidateSet (' SSHA512' , ' PBKDF2' )]
10+ [string ]$HashType = ' SSHA512'
611 )
712
813 # Generate 8 bytes of random salt
@@ -11,29 +16,48 @@ function Get-PasswordHash {
1116 $rng.GetBytes ($Salt )
1217 $rng.Dispose ()
1318
14- # Convert password to UTF-8 bytes
15- $passwordBytes = [System.Text.Encoding ]::UTF8.GetBytes($Password )
16-
17- # Concatenate password and salt
18- $data = New-Object byte[] ($passwordBytes.Length + $Salt.Length )
19- [System.Buffer ]::BlockCopy($passwordBytes , 0 , $data , 0 , $passwordBytes.Length )
20- [System.Buffer ]::BlockCopy($Salt , 0 , $data , $passwordBytes.Length , $Salt.Length )
21-
22- # Compute SHA-512 hash
23- $sha512 = [System.Security.Cryptography.SHA512 ]::Create()
24- $hashBytes = $sha512.ComputeHash ($data )
25- $sha512.Dispose ()
26-
27- # Concatenate hash and salt
28- $result = New-Object byte[] ($hashBytes.Length + $Salt.Length )
29- [System.Buffer ]::BlockCopy($hashBytes , 0 , $result , 0 , $hashBytes.Length )
30- [System.Buffer ]::BlockCopy($Salt , 0 , $result , $hashBytes.Length , $Salt.Length )
31-
32- # Convert to base64
33- $base64 = [System.Convert ]::ToBase64String($result )
34-
35- # Return with {SSHA512} prefix
36- return ' {SSHA512}' + $base64
19+ if ($HashType -eq ' PBKDF2' ) {
20+ # PBKDF2WithHmacSHA512: 210000 iterations, 512-bit hash
21+ $pbkdf2 = New-Object System.Security.Cryptography.Rfc2898DeriveBytes($Password , $Salt , 210000 , [System.Security.Cryptography.HashAlgorithmName ]::SHA512)
22+ $hashBytes = $pbkdf2.GetBytes (64 ) # 512 bits = 64 bytes
23+ $pbkdf2.Dispose ()
24+
25+ # Concatenate hash and salt
26+ $result = New-Object byte[] ($hashBytes.Length + $Salt.Length )
27+ [System.Buffer ]::BlockCopy($hashBytes , 0 , $result , 0 , $hashBytes.Length )
28+ [System.Buffer ]::BlockCopy($Salt , 0 , $result , $hashBytes.Length , $Salt.Length )
29+
30+ # Convert to base64
31+ $base64 = [System.Convert ]::ToBase64String($result )
32+
33+ # Return with {PBKDF2} prefix
34+ return ' {PBKDF2}' + $base64
35+ }
36+ else {
37+ # Convert password to UTF-8 bytes
38+ $passwordBytes = [System.Text.Encoding ]::UTF8.GetBytes($Password )
39+
40+ # Concatenate password and salt
41+ $data = New-Object byte[] ($passwordBytes.Length + $Salt.Length )
42+ [System.Buffer ]::BlockCopy($passwordBytes , 0 , $data , 0 , $passwordBytes.Length )
43+ [System.Buffer ]::BlockCopy($Salt , 0 , $data , $passwordBytes.Length , $Salt.Length )
44+
45+ # Compute SHA-512 hash
46+ $sha512 = [System.Security.Cryptography.SHA512 ]::Create()
47+ $hashBytes = $sha512.ComputeHash ($data )
48+ $sha512.Dispose ()
49+
50+ # Concatenate hash and salt
51+ $result = New-Object byte[] ($hashBytes.Length + $Salt.Length )
52+ [System.Buffer ]::BlockCopy($hashBytes , 0 , $result , 0 , $hashBytes.Length )
53+ [System.Buffer ]::BlockCopy($Salt , 0 , $result , $hashBytes.Length , $Salt.Length )
54+
55+ # Convert to base64
56+ $base64 = [System.Convert ]::ToBase64String($result )
57+
58+ # Return with {SSHA512} prefix
59+ return ' {SSHA512}' + $base64
60+ }
3761}
3862
3963# Create a function to validate a given password against the stored hash value
@@ -45,35 +69,64 @@ function Confirm-PasswordHash {
4569 [string ]$Hash
4670 )
4771
48- # Remove the {SSHA512} prefix and decode base64
49- $base64Data = $Hash.Substring (9 ) # Remove '{SSHA512}' prefix (9 characters)
50- $data = [System.Convert ]::FromBase64String($base64Data )
51-
52- # Extract salt (last 8 bytes) and hash (first len-8 bytes)
53- $len = $data.Length
54- $salt = New-Object byte[] 8
55- $storedHash = New-Object byte[] ($len - 8 )
56-
57- # Use BlockCopy to extract salt (last 8 bytes)
58- [System.Buffer ]::BlockCopy($data , $len - 8 , $salt , 0 , 8 )
59- # Use BlockCopy to extract hash (first len-8 bytes)
60- [System.Buffer ]::BlockCopy($data , 0 , $storedHash , 0 , $len - 8 )
61-
62- # Convert password to UTF-8 bytes
63- $passwordBytes = [System.Text.Encoding ]::UTF8.GetBytes($Password )
64-
65- # Concatenate password and salt
66- $dataToHash = New-Object byte[] ($passwordBytes.Length + $salt.Length )
67- [System.Buffer ]::BlockCopy($passwordBytes , 0 , $dataToHash , 0 , $passwordBytes.Length )
68- [System.Buffer ]::BlockCopy($salt , 0 , $dataToHash , $passwordBytes.Length , $salt.Length )
69-
70- # Compute SHA-512 hash
71- $sha512 = [System.Security.Cryptography.SHA512 ]::Create()
72- $computedHash = $sha512.ComputeHash ($dataToHash )
73- $sha512.Dispose ()
74-
75- # Compare the computed hash with the stored hash
76- return Compare-ByteArrays $computedHash $storedHash
72+ # Detect hash type based on prefix
73+ if ($Hash.StartsWith (' {PBKDF2}' )) {
74+ # Remove the {PBKDF2} prefix and decode base64
75+ $base64Data = $Hash.Substring (8 ) # Remove '{PBKDF2}' prefix (8 characters)
76+ $data = [System.Convert ]::FromBase64String($base64Data )
77+
78+ # Extract salt (last 8 bytes) and hash (first len-8 bytes)
79+ $len = $data.Length
80+ $salt = New-Object byte[] 8
81+ $storedHash = New-Object byte[] ($len - 8 )
82+
83+ # Use BlockCopy to extract salt (last 8 bytes)
84+ [System.Buffer ]::BlockCopy($data , $len - 8 , $salt , 0 , 8 )
85+ # Use BlockCopy to extract hash (first len-8 bytes)
86+ [System.Buffer ]::BlockCopy($data , 0 , $storedHash , 0 , $len - 8 )
87+
88+ # Compute PBKDF2 hash with same parameters
89+ $pbkdf2 = New-Object System.Security.Cryptography.Rfc2898DeriveBytes($Password , $salt , 210000 , [System.Security.Cryptography.HashAlgorithmName ]::SHA512)
90+ $computedHash = $pbkdf2.GetBytes (64 ) # 512 bits = 64 bytes
91+ $pbkdf2.Dispose ()
92+
93+ # Compare the computed hash with the stored hash
94+ return Compare-ByteArrays $computedHash $storedHash
95+ }
96+ elseif ($Hash.StartsWith (' {SSHA512}' )) {
97+ # Remove the {SSHA512} prefix and decode base64
98+ $base64Data = $Hash.Substring (9 ) # Remove '{SSHA512}' prefix (9 characters)
99+ $data = [System.Convert ]::FromBase64String($base64Data )
100+
101+ # Extract salt (last 8 bytes) and hash (first len-8 bytes)
102+ $len = $data.Length
103+ $salt = New-Object byte[] 8
104+ $storedHash = New-Object byte[] ($len - 8 )
105+
106+ # Use BlockCopy to extract salt (last 8 bytes)
107+ [System.Buffer ]::BlockCopy($data , $len - 8 , $salt , 0 , 8 )
108+ # Use BlockCopy to extract hash (first len-8 bytes)
109+ [System.Buffer ]::BlockCopy($data , 0 , $storedHash , 0 , $len - 8 )
110+
111+ # Convert password to UTF-8 bytes
112+ $passwordBytes = [System.Text.Encoding ]::UTF8.GetBytes($Password )
113+
114+ # Concatenate password and salt
115+ $dataToHash = New-Object byte[] ($passwordBytes.Length + $salt.Length )
116+ [System.Buffer ]::BlockCopy($passwordBytes , 0 , $dataToHash , 0 , $passwordBytes.Length )
117+ [System.Buffer ]::BlockCopy($salt , 0 , $dataToHash , $passwordBytes.Length , $salt.Length )
118+
119+ # Compute SHA-512 hash
120+ $sha512 = [System.Security.Cryptography.SHA512 ]::Create()
121+ $computedHash = $sha512.ComputeHash ($dataToHash )
122+ $sha512.Dispose ()
123+
124+ # Compare the computed hash with the stored hash
125+ return Compare-ByteArrays $computedHash $storedHash
126+ }
127+ else {
128+ throw " Unsupported hash format. Expected {SSHA512} or {PBKDF2} prefix."
129+ }
77130}
78131
79132# Helper function to compare two byte arrays
0 commit comments