2011/06/18

vbaでhmac その2

前に書いたけど、今回は一発で行けます。
'http://msdn.microsoft.com/en-us/library/aa382379(v=vs.85).aspx 参考

Option Explicit

Private Declare Function CryptAcquireContext Lib "advapi32.dll" Alias "CryptAcquireContextA" _
  (ByRef phProv As Long, ByVal pszContainer As String, ByVal pszProvider As String, ByVal dwProvType As Long, ByVal dwFlags As Long) As Long
Private Declare Function CryptReleaseContext Lib "advapi32.dll" _
  (ByVal hProv As Long, ByVal dwFlags As Long) As Long
Private Declare Function CryptCreateHash Lib "advapi32.dll" _
  (ByVal hProv As Long, ByVal algid As Long, ByVal hKey As Long, ByVal dwFlags As Long, ByRef phHash As Long) As Long
Private Declare Function CryptDestroyHash Lib "advapi32.dll" _
  (ByVal hHash As Long) As Long
Private Declare Function CryptHashData Lib "advapi32.dll" _
  (ByVal hHash As Long, ByRef pbData As Any, ByVal dwDataLen As Long, ByVal dwFlags As Long) As Long
Private Declare Function CryptGetHashParam Lib "advapi32.dll" _
  (ByVal hHash As Long, ByVal dwParam As Long, ByRef pbData As Any, ByRef pdwDataLen As Long, ByVal dwFlags As Integer) As Long
Private Declare Function CryptDeriveKey Lib "advapi32.dll" _
  (ByVal hProv As Long, ByVal algid As Long, ByVal hBaseData As Long, ByVal dwFlags As Long, ByRef phKey As Long) As Long
Private Declare Function CryptDestroyKey Lib "advapi32.dll" _
  (ByVal hKey As Long) As Long
Private Declare Function CryptSetHashParam Lib "advapi32.dll" _
  (ByVal hHash As Long, ByVal dwParam As Long, ByRef pbData As Any, ByVal dwFlags As Integer) As Long
Private Declare Function CryptImportKey Lib "advapi32.dll" _
  (ByVal hProv As Long, ByRef pbData As Any, ByVal dwDataLen As Long, ByVal hPubKey As Long, ByVal dwFlags As Long, ByRef phKey As Long) As Long
Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" _
  (Destination As Any, Source As Any, ByVal Length As Long)
Private Declare Sub ZeroMemory Lib "kernel32" Alias "RtlZeroMemory" _
  (Destination As Any, ByVal Length As Long)
Private Const CRYPT_VERIFYCONTEXT As Long = &HF0000000
Private Const MS_DEF_PROV As String = "Microsoft Base Cryptographic Provider v1.0"
Private Const ALG_TYPE_ANY As Long = 0
Private Const ALG_CLASS_HASH As Long = 32768
Private Const ALG_TYPE_BLOCK As Long = 1536
Private Const ALG_SID_SHA As Long = 4
Private Const ALG_SID_SHA1 As Long = ALG_SID_SHA
Private Const ALG_CLASS_DATA_ENCRYPT As Long = 24576
Private Const ALG_TYPE_STREAM As Long = 2048
Private Const ALG_SID_RC4 As Long = 1
Private Const ALG_SID_RC2 As Long = 2
Private Const ALG_SID_HMAC As Long = 9
Private Const CALG_SHA As Long = ALG_CLASS_HASH Or ALG_TYPE_ANY Or ALG_SID_SHA
Private Const CALG_SHA1 As Long = CALG_SHA
Private Const CALG_RC2 As Long = ALG_CLASS_DATA_ENCRYPT Or ALG_TYPE_BLOCK Or ALG_SID_RC2
Private Const CALG_RC4 As Long = ALG_CLASS_DATA_ENCRYPT Or ALG_TYPE_STREAM Or ALG_SID_RC4
Private Const CALG_HMAC As Long = ALG_CLASS_HASH Or ALG_TYPE_ANY Or ALG_SID_HMAC
Private Const HP_HMAC_INFO = &H5
Private Const HP_HASHVAL As Long = 2
Private Const PROV_RSA_FULL As Long = 1
Private Const PLAINTEXTKEYBLOB As Long = 8
Private Const CUR_BLOB_VERSION As Long = 2
Private Const CRYPT_IPSEC_HMAC_KEY = &H100
Private Type HMAC_Info
  HashAlgid As Long
  pbInnerString As Byte
  cbInnerString As Long
  pbOuterString As Byte
  cbOuterString As Long
End Type
Private Type BLOBHEADER
  bType As Byte
  bVersion As Byte
  reserved As Integer
  aiKeyAlg As Long
End Type
Private Type key_blob
  hdr As BLOBHEADER
  len As Long
  key(1024) As Byte '// TODO might want to dynamically allocate this, Should Be Fine though
End Type

Private Function hmac(ByVal strKey As String, ByVal strData As String) As String
  Dim bytKey() As Byte
  Dim bytData() As Byte
  Dim ret As Long
  Dim lngProv As Long       'コンテナオブジェクト
  Dim lngHash As Long       'ハッシュオブジェクト
  Dim lngHmacHash As Long   'ハッシュオブジェクト
  Dim lngHashSize As Long   'ハッシュサイズ
  Dim lngKey As Long        'キーオブジェクト
  Dim bytBuff() As Byte     'ハッシュが格納されるエリア
  Dim strHex As String      '16進数文字列
  Dim i As Long
  Dim HmacInfo As HMAC_Info
  Dim keyblob As key_blob
  Dim key_len As Long
  
  hmac = ""
  strHex = ""
  
  'バイト配列へ
  bytKey = StrConv(strKey, vbFromUnicode)
  bytData = StrConv(strData, vbFromUnicode)

  '1024バイトチェック
  key_len = UBound(bytKey) + 1
  If key_len > 1024 Then
    hmac = ""
    Exit Function
  End If

  'キーコンテナの作成
  ret = CryptAcquireContext(lngProv, vbNullString, vbNullString, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)
  If ret = False Then
    GoTo ExitHandler
  End If

'  '鍵作り
'  ret = CryptDeriveKey(lngProv, CALG_RC2, lngHash, 0, lngKey)
'  If ret = False Then
'    Call CryptDestroyKey(lngKey)
'    GoTo ExitHandler
'  End If

  '// key creation based on
  '// http://mirror.leaseweb.com/NetBSD/NetBSD-release-5-0/src/dist/wpa/src/crypto/crypto_cryptoapi.c
  keyblob.hdr.bType = PLAINTEXTKEYBLOB
  keyblob.hdr.bVersion = CUR_BLOB_VERSION
  keyblob.hdr.reserved = 0
  '/*
  '* Note: RC2 is not really used, but that can be used to
  '* import HMAC keys of up to 16 byte long.
  '* CRYPT_IPSEC_HMAC_KEY flag for CryptImportKey() is needed to
  '* be able to import longer keys (HMAC-SHA1 uses 20-byte key).
  '*/
  keyblob.hdr.aiKeyAlg = CALG_RC2
  keyblob.len = key_len
  Call ZeroMemory(keyblob.key(0), key_len)
  Call CopyMemory(keyblob.key(0), bytKey(0), key_len)
  ret = CryptImportKey(lngProv, keyblob, 12 + key_len, 0, CRYPT_IPSEC_HMAC_KEY, lngKey)
  If ret = False Then
    GoTo ExitHandler
  End If
  
  'ハッシュオブジェクトの作成
  ret = CryptCreateHash(lngProv, CALG_HMAC, lngKey, 0, lngHmacHash)
  If ret = False Then
    GoTo ExitHandler
  End If
  
  'パラメータセット
  HmacInfo.HashAlgid = CALG_SHA1
  ret = CryptSetHashParam(lngHmacHash, HP_HMAC_INFO, HmacInfo, 0)
  If ret = False Then
    GoTo ExitHandler
  End If

  'ハッシュデータを作る
  ret = CryptHashData(lngHmacHash, bytData(0), UBound(bytData) + 1, 0)
  If ret = False Then
    GoTo ExitHandler
  End If

  '必要なサイズを取得
  ret = CryptGetHashParam(lngHmacHash, HP_HASHVAL, ByVal 0, lngHashSize, 0)
  If ret = False Then
    GoTo ExitHandler
  End If
  
  'ハッシュを取り出す
  ReDim bytBuff(lngHashSize - 1)
  For i = 0 To UBound(bytBuff)
    bytBuff(i) = 0
  Next
  ret = CryptGetHashParam(lngHmacHash, HP_HASHVAL, bytBuff(0), lngHashSize, 0)
  If ret = False Then
    GoTo ExitHandler
  End If

  'HEX文字列へ
  For i = 0 To UBound(bytBuff)
    strHex = strHex & Right("0" & LCase(Hex(bytBuff(i))), 2)
  Next
  
ExitHandler:
  If (lngHmacHash) Then
    CryptDestroyHash (lngHmacHash)
  End If
  If (lngKey) Then
    Call CryptDestroyKey(lngKey)
  End If
  If (lngHash) Then
    Call CryptDestroyHash(lngHash)
  End If
  If (lngProv) Then
    Call CryptReleaseContext(lngProv, 0)
  End If
  hmac = strHex
End Function

Private Sub hmac_test()
  Debug.Print hmac("key", "data")
End Sub

win32apiを使っているので、こっちが早いと思います。
Posted at 02:19 in program::vba | WriteBacks (11) | Edit
Tagged as: , ,

2011/02/12

vbaでhmac

とある事情からvbaのみでhmacができないか調べました。
で、できました。
スーの道具箱/気まぐれ日記/2007-03-08

VBでハッシュを求める *
MD5をVBで処理すると遅くなってしまうので、advapi32.dllを使うと簡単だし速い。
あまりサンプルが見当たらなかったので、書いてみた
ここからコピペして標準モジュールへ貼り付け。
おそらくExcel2000以上で動くと思います。
(vba6なら動くと思います)

Public Function hmac(ByVal key As String, ByVal data As String) As String
  Dim i As Integer
  Dim hash As String
  Dim key_byte() As Byte
  Dim key_len As Long
  Dim data_len As Long
  Dim ipad(63) As Byte
  Dim opad(63) As Byte
  Dim key_hash() As Byte
  Dim data_hash As String

  If key = "" And data = "" Then Exit Function

  key_len = Len(key)

  key_byte = StrConv(key, vbFromUnicode)
  If key_len > 64 Then
      key_hash = StrToBynary(CreateSHA1Hash(key_byte))
      key_len = 20
  Else
      key_hash = key_byte
  End If
  
  ReDim Preserve key_hash(63)
  For i = key_len To 63
    key_hash(i) = 0
  Next

  For i = 0 To 63
    ipad(i) = 0
    opad(i) = 0
  Next

  For i = 0 To 63
    ipad(i) = key_hash(i) Xor &H36
    opad(i) = key_hash(i) Xor &H5C
  Next

  data_hash = CreateSHA1Hash(CStr(ipad) & StrConv(data, vbFromUnicode))

  hash = CreateSHA1Hash(CStr(opad) & CStr(StrToBynary(data_hash)))

  hmac = hash
End Function

Private Function StrToBynary(strHexString As String) As Byte()
  Dim buf() As Byte
  Dim i As Long
  
  ReDim Preserve buf(Len(CStr(strHexString)) \ 2 - 1)
  For i = 0 To Len(CStr(strHexString)) \ 2 - 1
    buf(i) = CByte("&h" & Mid(CStr(strHexString), i * 2 + 1, 2))
  Next
  StrToBynary = buf
End Function


ここを参考にしました。
【Access】vbaでhmacが正しく計算できた!! | プラプラ式技術系 Access流!
HMAC SHA256 BASE64: 逢魔時 ~トワイライト~

Posted at 23:16 in program::vba | WriteBacks (814) | Edit
Tagged as: , ,