PBKDF2 Excel UDF以及如何连接INT(i) [英] PBKDF2 Excel UDF and how to concatenate INT(i)
问题描述
最近我一直在研究加密技术,并在Excel中使用散列和加密函数,这些函数可以在我正在研究的项目中使用。
Recently I have been digging into cryptography and getting hashing and encryption functions working in Excel which I might use in a project I am working on.
我得到了简单的散列使用的函数,例如:
I got simple hashing functions working using, for example:
Function Hash(ByVal plainText As String)
Dim utf8Encoding As Object
Dim hashManager As Object
Dim hashBytes() As Byte
Set utf8Encoding = CreateObject("System.Text.UTF8Encoding")
Set hashManager = CreateObject("System.Security.Cryptography.SHA512Managed")
hashBytes = utf8Encoding.GetBytes_4(plainText)
hashBytes = hashManager.ComputeHash_2(hashBytes)
Hash = Encode(hashBytes, edHex)
Set utf8Encoding = Nothing
Set hashManager = Nothing
End Function
要对结果进行编码,我创建了一个函数:
To encode the result I have a created a function:
Function Encode(ByRef arrData() As Byte, ByVal dataType As endecodeDataType) As String
Dim domDoc As Object
Set domDoc = CreateObject("MSXML2.DOMDocument")
With domDoc
.LoadXML "<root />"
Select Case dataType
Case edBase64
.DocumentElement.dataType = "bin.base64"
Case edHex
.DocumentElement.dataType = "bin.hex"
End Select
.DocumentElement.nodeTypedValue = arrData
End With
Encode = domDoc.DocumentElement.Text
Set domDoc = Nothing
End Function
这些结合在一起使我得到了可验证的结果。
经过更多研究,我现在正在使用PBKDF2函数:
These combined gives me perfectly verifiable results. After more research I am now working on a PBKDF2 function:
- Specs: https://tools.ietf.org/html/rfc2898
- Test vectors: https://tools.ietf.org/html/rfc6070
我的第一次尝试是按如下方式查看 Rfc2898DeriveBytes:
My first attempt was to look into 'Rfc2898DeriveBytes' as follows:
Dim hashManager As Object
Set hashManager = CreateObject("System.Security.Cryptography.Rfc2898DeriveBytes")
但是这给出了一个错误,指出无法创建ActiveX组件。
However this gives an error stating that the ActiveX component cannot be created.
除了该错误之外,为了尝试理解PBKDF2的基础,并学习使用位/字节,我创建了以下函数:
Besides the error and for the sake of trying to understand the basics of PBKDF2, and learning to work with bits/bytes I have created the following function:
编辑:现在,我仅关注dkLen< = hLen
For now I'm only focusing on dkLen <= hLen
Function PBKDF2(ByVal password As String, _
ByVal hashIterations As Long, _
ByVal salt As String, _
Optional ByVal encodeHash As hashEncoding = heBase64) As Variant
Dim utf8Encoding As Object
Dim hashManager As Object
Dim hmacKeyBytes() As Byte
Dim saltBytes() As Byte
Dim hmacBytes() As Byte
Dim tempBytes() As Byte
Dim i As Long
'Create encoding and crypto objects
Set utf8Encoding = CreateObject("System.Text.UTF8Encoding")
Set hashManager = CreateObject("System.Security.Cryptography.HMACSHA1")
'Encode the key and salt to bytes
hmacKeyBytes = utf8Encoding.GetBytes_4(password)
saltBytes = utf8Encoding.GetBytes_4(salt)
'Concatenate salt and INT(i) - INT (i) is a four-octet encoding of the integer i, most significant octet first.
'Set the key in the crypto class
hashManager.key = hmacKeyBytes
'Compute HMAC from salt
hmacBytes = hashManager.ComputeHash_2(saltBytes)
tempBytes = hmacBytes
'HMAC iterations
For i = 1 To hashIterations
tempBytes = hashManager.ComputeHash_2(tempBytes)
hmacBytes = XorBytes(tempBytes, hmacBytes)
Next i
'ToDo: extract the first dkLen octets to produce a derived key DK
'Base64, Hex, or Byte() output
If encodeHash = heBase64 Then
PBKDF2 = Encode(hmacBytes, edBase64)
ElseIf encodeHash = heHex Then
PBKDF2 = Encode(hmacBytes, edHex)
End If
Set hashManager = Nothing
Set utf8Encoding = Nothing
End Function
我将XorBytes定义为:
Where I defined XorBytes as:
Function XorBytes(ByRef byte1() As Byte, ByRef byte2() As Byte) As Byte()
Dim tempBytes() As Byte
Dim len1 As Long
Dim i As Long
len1 = UBound(byte1)
ReDim tempBytes(len1)
For i = 0 To len1
tempBytes(i) = byte1(i) Xor byte2(i)
Next i
XorBytes = tempBytes
End Function
我相信我的基本知识正确。我不知道如何解决的一件事是如何将INT(i)连接到盐上。规范指出:
I believe I have the basics correct. One thing I don't know how to solve is how to concatenate INT(i) to the salt. The specs state:
U_1 = PRF(P,S || INT(i))
U_1 = PRF (P, S || INT (i))
此处,INT(i)是整数i的四字节编码,最高有效八位字节在前。
Here, INT (i) is a four-octet encoding of the integer i, most significant octet first.
如何在我的VBA代码中实现这一点?我希望这使我更接近此测试向量:
How do I implement this in my VBA code? I hope this gets me closer to this test vector:
- 输入
- P =密码(8个八位字节)
- S =盐(4个八位字节)
- c = 1
- dkLen = 20
- Input
- P = "password" (8 octets)
- S = "salt" (4 octets)
- c = 1
- dkLen = 20
- DK = 0c 60 c8 0f 96 1f 0e 71 f3 a9 b5 24 af 60 12 06 2f e0 37 a6(20个八位位组)
推荐答案
经过进一步摆弄之后,下面的函数返回了我可以验证的输出:
After some more fiddling the function below returns output that I can verify with:
https://tools.ietf.org/html/rfc6070
枚举
Enum hmacAlgorithm HMAC_MD5 HMAC_SHA1 HMAC_SHA256 HMAC_SHA384 HMAC_SHA512 End Enum Enum hashEncoding heBase64 heHex heNone_Bytes End Enum
PBKDF2函数
Function PBKDF2(ByVal password As String, _ ByVal salt As String, _ ByVal hashIterations As Long, _ ByVal algoritm As hmacAlgorithm, _ Optional ByVal dkLen As Long, _ Optional ByVal encodeHash As hashEncoding = heBase64) As Variant 'https://tools.ietf.org/html/rfc2898 - PKCS #5: Password-Based Cryptography Specification Version 2.0 'https://tools.ietf.org/html/rfc6070 - PKCS #5: Password-Based Key Derivation Function 2 (PBKDF2) Test Vectors 'https://en.wikipedia.org/wiki/PBKDF2 'DK = T1 || T2 || ... || Tdklen/hlen 'Ti = F(password, salt, c, i) ' 'F(Password, Salt, c, i) = U1 ^ U2 ^ ... ^ Uc ' 'U_1 = PRF (P, S || INT (i)) (INT (i) is a four-octet encoding of the integer i, most significant octet first.) 'U_2 = PRF (P, U_1) '... 'U_c = PRF (P, U_{c-1}) Dim utf8Encoding As Object Dim hashManager As Object Dim hLen As Long Dim noBlocks As Long Dim noBlock As Long Dim hmacKeyBytes() As Byte Dim saltBytes() As Byte Dim uboundSaltBytes As Long Dim hmacBytes() As Byte Dim tempBytes() As Byte Dim outputBytes() As Byte Dim i As Long Dim j As Long 'Create utf8-encoding object Set utf8Encoding = CreateObject("System.Text.UTF8Encoding") 'Create hmac object Select Case algoritm Case HMAC_MD5 Set hashManager = CreateObject("System.Security.Cryptography.HMACMD5") Case HMAC_SHA1 Set hashManager = CreateObject("System.Security.Cryptography.HMACSHA1") Case HMAC_SHA256 Set hashManager = CreateObject("System.Security.Cryptography.HMACSHA256") Case HMAC_SHA384 Set hashManager = CreateObject("System.Security.Cryptography.HMACSHA384") Case HMAC_SHA512 Set hashManager = CreateObject("System.Security.Cryptography.HMACSHA512") End Select 'Check the length of the blocks to be generated hLen = hashManager.HashSize / 8 'Calculate amount of blocks 'T' If dkLen = 0 Then dkLen = hLen noBlocks = Application.WorksheetFunction.Ceiling(dkLen / hLen, 1) 'Encode the key and salt to bytes hmacKeyBytes = utf8Encoding.GetBytes_4(password) saltBytes = utf8Encoding.GetBytes_4(salt) 'Set the key in the crypto class hashManager.key = hmacKeyBytes 'Get the length of the salt, add 4 to concatenate INT(I) uboundSaltBytes = UBound(saltBytes) + 4 'Loop T1 || T2 || ... || Tdklen/hlen For i = 1 To noBlocks 'Salt || INT(i) 'INT (i) is a four-octet encoding of the integer i, most significant octet first. tempBytes = saltBytes ReDim Preserve tempBytes(uboundSaltBytes) noBlock = i 'Calculate INT(i) of Salt || INT(i) For j = 3 To 0 Step -1 tempBytes(uboundSaltBytes - j) = Int(noBlock / (255 ^ j)) noBlock = noBlock - Int(noBlock / (255 ^ j)) * 255 ^ j Next j 'Hash U1: Salt || INT(i) hmacBytes = hashManager.ComputeHash_2(tempBytes) tempBytes = hmacBytes 'Hash, Xor: U1 ^ U2 ^ ... ^ Uc For j = 1 To hashIterations - 1 hmacBytes = hashManager.ComputeHash_2(hmacBytes) tempBytes = XorBytes(tempBytes, hmacBytes) Next j 'For the first block outputBytes() is empty If i = 1 Then outputBytes = tempBytes Else ConcatenateArrayInPlace outputBytes, tempBytes End If Next i 'Extract the first dkLen octets to produce a derived key DK: ReDim Preserve outputBytes(dkLen - 1) 'Base64, Hex, or Byte() output If encodeHash = heBase64 Then PBKDF2 = Encode(outputBytes, edBase64) ElseIf encodeHash = heHex Then PBKDF2 = Encode(outputBytes, edHex) Else PBKDF2 = outputBytes End If Set hashManager = Nothing Set utf8Encoding = Nothing End Function
HMAC函数
Function HMAC(ByVal plainText As String, _ ByVal algoritm As hmacAlgorithm, _ Optional ByVal key As String, _ Optional ByVal decodeKey As keyDecoding = kdNone_String, _ Optional ByVal encodeHash As hashEncoding = heBase64) As Variant Dim hashManager As Object Dim hashBytes() As Byte Dim hmacKeyBytes() As Byte 'Create the specific hash manager based on the hash algoritm Select Case algoritm Case HMAC_MD5 Set hashManager = CreateObject("System.Security.Cryptography.HMACMD5") 'Returns 128 bits, 16 bytes Case HMAC_SHA1 Set hashManager = CreateObject("System.Security.Cryptography.HMACSHA1") 'Returns 160 bits, 20 bytes Case HMAC_SHA256 Set hashManager = CreateObject("System.Security.Cryptography.HMACSHA256") 'Returns 256 bits, 32 bytes Case HMAC_SHA384 Set hashManager = CreateObject("System.Security.Cryptography.HMACSHA384") 'Returns 384 bits, 48 bytes Case HMAC_SHA512 Set hashManager = CreateObject("System.Security.Cryptography.HMACSHA512") 'Returns 512 bits, 64 bytes End Select 'Encode the plaintText to bytes hashBytes = UTF8_GetBytes(plainText) If key = vbNullString Then 'Get the key generated by the hashManager hmacKeyBytes = hashManager.key 'Calculate the hash hashBytes = hashManager.ComputeHash_2(hashBytes) 'Return encoded result If encodeHash = heBase64 Then HMAC = "<Key>" & Encode(hmacKeyBytes, edBase64) & "<Key>" & vbCrLf & Encode(hashBytes, edBase64) ElseIf encodeHash = heHex Then HMAC = "<Key>" & Encode(hmacKeyBytes, edHex) & "<Key>" & vbCrLf & Encode(hashBytes, edHex) End If Else 'Decode and set the key Select Case decodeKey Case kdBase64 hashManager.key = Decode(key, edBase64) Case kdHex hashManager.key = Decode(key, edHex) Case Else hashManager.key = UTF8_GetBytes(key) End Select 'Calculate the hash hashBytes = hashManager.ComputeHash_2(hashBytes) 'Return encoded result If encodeHash = heBase64 Then HMAC = Encode(hashBytes, edBase64) ElseIf encodeHash = heHex Then HMAC = Encode(hashBytes, edHex) End If End If Set hashManager = Nothing End Function
测试子例程:
Sub PBKDF2_Test() Dim testvector As String Dim pbkdf2_result As String pbkdf2_result = PBKDF2("password", "salt", 1, HMAC_SHA1, 20, heHex) testvector = "0c60c80f961f0e71f3a9b524af6012062fe037a6" If pbkdf2_result = testvector Then Debug.Print "TV1: OK" Else Debug.Print "TV1: FAULT" pbkdf2_result = PBKDF2("password", "salt", 2, HMAC_SHA1, 20, heHex) testvector = "ea6c014dc72d6f8ccd1ed92ace1d41f0d8de8957" If pbkdf2_result = testvector Then Debug.Print "TV2: OK" Else Debug.Print "TV2: FAULT" pbkdf2_result = PBKDF2("password", "salt", 4096, HMAC_SHA1, 20, heHex) testvector = "4b007901b765489abead49d926f721d065a429c1" If pbkdf2_result = testvector Then Debug.Print "TV3: OK" Else Debug.Print "TV3: FAULT" pbkdf2_result = PBKDF2("passwordPASSWORDpassword", "saltSALTsaltSALTsaltSALTsaltSALTsalt", 4096, HMAC_SHA1, 25, heHex) testvector = "3d2eec4fe41c849b80c8d83662c0e44a8b291a964cf2f07038" If pbkdf2_result = testvector Then Debug.Print "TV4: OK" Else Debug.Print "TV4: FAULT" End Sub
我想不是他最漂亮的代码,但这是向前的一步。随时改进!
I guess not the prettiest code, but it's a step forward. Feel free to improve!
这篇关于PBKDF2 Excel UDF以及如何连接INT(i)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!