PBKDF2 как функция в Excel

Мне удалось заставить хеширование SHA256 работать в Excel, но я искал что-то более безопасное, здесь я наткнулся на генерацию хэшей PBKDF2: excel-udf-and-how-to-concatenate-inti">PBKDF2 Excel UDF и как объединить INT(i) Но код в решении не запускается:

Ошибка компиляции: определяемый пользователем тип не определен

Выделив следующее:

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

Моя конечная надежда состоит в том, чтобы иметь функцию, аналогичную SHA256. У меня есть =SHA256hash(A2) с длиной ключа PBKDF2, солью и итерациями в качестве параметров =PBKDF2(A2,512,saltysalt,5000) Например, так что это что-то простое для команды здесь реализовать, как мы все знаем, чем сложнее это реализовать, тем меньше вероятность того, что люди будут это делать.

Код по ссылке выше:

Перечисления

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

Будем очень благодарны любой помощи. Поиск в Google не дает много информации об использовании PBKDF2 в Excel.


person le__bon    schedule 01.05.2019    source источник
comment
Вам не хватает перечисления keyDecoding я думаю в ByVal decodeKey As keyDecoding - либо вы неправильно скопировали ядро, либо его не существует и вам нужно написать свое   -  person Greedo    schedule 01.05.2019
comment
Спасибо - все справился из первоисточника (перепроверил) может пропустили в своем постинге? (ссылка в оригинальном посте) Спасибо за вашу помощь, но я не совсем уверен, как бы я написал свой собственный? знак равно   -  person le__bon    schedule 01.05.2019
comment
Я быстро просмотрел сообщение SO, на которое вы ссылаетесь; функция Decode не включена, поэтому, боюсь, вам придется найти свою собственную. Функция Encode находится в теле вопроса. Enums отсутствуют, но их не так сложно написать. Я предлагаю прочитать, что делает функция Decode, попытаться написать свою собственную, и если у вас возникнут трудности, опубликуйте это в новом вопросе здесь. PS вам нужно прокомментировать только один раз, чтобы отметить кого-то в сообщении, сделайте @username в начале (см. ссылку справки внизу окна добавления комментария)   -  person Greedo    schedule 01.05.2019


Ответы (1)


Во всем этом разделе вашей функции HMAC отсутствуют некоторые определения перечисления:

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

decodeKey объявлен в подписи вашей функции с типом keyDecoding, который, как я полагаю, является перечислением:

Optional ByVal decodeKey As keyDecoding = kdNone_String

Вот что выдает вашу ошибку, это перечисление keyDecoding нигде не определено. Однако похоже, что в нем всего 3 участника, так что вы, вероятно, могли бы создать свой собственный.

Тем временем функции Decode и Encode принимают некоторое перечисление с элементами edBase64 и edHex. Это перечисление также нигде не определено (хотя это не то, что дает вам ошибку, это первое, что я упомянул).

Вы все равно не включили эти две функции, поэтому я не могу сказать больше, но я полагаю, что их подписи скажут вам, как называется ваше второе отсутствующее перечисление. Могут быть и другие проблемы с кодом, но это только то, что мне запомнилось. Убедитесь, что вы правильно скопировали его из источника :)

person Greedo    schedule 01.05.2019
comment
Спасибо - все справился из первоисточника (перепроверил) может пропустили в своем постинге? (ссылка в оригинальном посте) Спасибо за вашу помощь, но я не совсем уверен, как бы я написал свой собственный? знак равно - person le__bon; 01.05.2019