文件的哈希值校验算法

文件MD5 CRC32.ahk

MsgBox % FileCRC32(A_AhkPath)

MsgBox % MD5_File(A_AhkPath)


; ************  MD5 hashing functions by Laszlo  *******************

FileCRC32( sFile="",cSz=4 ) { ; by SKAN 10-Oct-2009  www.autohotkey.com/community/viewtopic.php?t=64211
  cSz := (cSz<0||cSz>8) ? 2**22 : 2**(18+cSz), VarSetCapacity(Buffer,cSz,0)
  , hFil := DllCall("CreateFile", "Str", sFile, "UInt", 0x80000000, "Int", 3, "Int", 0, "Int", 3, "Int", 0, "Int", 0)
  IfLess,hFil,1, Return,hFil
  hMod := DllCall("LoadLibrary", "Str", "ntdll.dll"), CRC32 := 0
  , DllCall("GetFileSizeEx", "UInt",hFil, "UInt",&Buffer), fSz := NumGet(Buffer, 0,"Int64")
  Loop % ( fSz//cSz + !!Mod( fSz,cSz ) )
    DllCall("ReadFile", "UInt", hFil, "UInt", &Buffer, "UInt", cSz, "UIntP",Bytes, "UInt",0)
    , CRC32 := DllCall("NTDLL\RtlComputeCrc32", "UInt", CRC32, "UInt", &Buffer, "UInt", Bytes, "UInt")
  DllCall("CloseHandle", "UInt", hFil)
  SetFormat, Integer, % SubStr((AFI := A_FormatInteger) "H", 0)
  CRC32 := SubStr(CRC32 + 0x1000000000, -7), DllCall("CharUpper", "Str", CRC32)
  SetFormat, Integer, %AFI%
  Return CRC32, DllCall("FreeLibrary", "UInt", hMod)
}

MD5_File(sFile="", cSz=4) { ; www.autohotkey.com/forum/viewtopic.php?p=275910#275910
  cSz := (cSz<0||cSz>8) ? 2**22 : 2**(18+cSz), VarSetCapacity(Buffer, cSz, 0)
  hFil := DllCall("CreateFile", "Str", sFile, "UInt", 0x80000000, "Int", 1,"Int", 0,"Int", 3,"Int", 0,"Int", 0)
  IfLess,hFil,1, Return,hFil
  DllCall("GetFileSizeEx", "UInt", hFil, "Str", Buffer), fSz := NumGet(Buffer, 0, "Int64")
  VarSetCapacity(MD5_CTX, 104, 0), DllCall("advapi32\MD5Init", "Str", MD5_CTX)
    LoopNum := fSz//cSz
  Loop % (LoopNum +!!Mod(fSz,cSz))
  {
    if (LoopNum > 125)
      ToolTip % (A_index * cSz *100) / fSz "%"
    DllCall("ReadFile", "UInt", hFil, "Str", Buffer, "UInt", cSz, "UIntP", bytesRead, "UInt", 0)
    , DllCall("advapi32\MD5Update", "Str", MD5_CTX, "Str", Buffer, "UInt", bytesRead)
  }
  if (LoopNum > 125)
    ToolTip
  DllCall("advapi32\MD5Final", "Str", MD5_CTX), DllCall("CloseHandle", "UInt", hFil)
  Loop % StrLen(Hex:="123456789ABCDEF0")
    N := NumGet(MD5_CTX, 87+A_Index, "Char"), MD5 .= SubStr(Hex,N>>4,1) . SubStr(Hex,N&15,1)
  Return MD5
}

MD5(ByRef V, L=0) { ; www.autohotkey.com/forum/viewtopic.php?p=275910#275910
  VarSetCapacity( MD5_CTX,104,0 ), DllCall( "advapi32\MD5Init", "Str",MD5_CTX )
  DllCall("advapi32\MD5Update", "Str", MD5_CTX, "Str", V, "UInt", L ? L : VarSetCapacity(V) )
  DllCall("advapi32\MD5Final", "Str", MD5_CTX )
  Loop % StrLen(Hex:="123456789ABCDEF0")
    N := NumGet(MD5_CTX,87+A_Index, "Char"), MD5 .= SubStr(Hex,N>>4,1) . SubStr(Hex,N&15,1)
  Return MD5
}

 

Hash哈希检验.ahk

MsgBox % Hash("", A_AhkPath)                        ; 默认SHA256哈希格式为十六进制(默认为大写)
MsgBox % Hash("Upper:0",  A_AhkPath)                ; 与上面相同,但哈希将使用小写形式
MsgBox % Hash("Base64:1", A_AhkPath)                ; 默认SHA256哈希为Base64文本
MsgBox % Hash("alg:SHA512 Upper:0",   A_AhkPath)    ; SHA512哈希格式为十六进制(小写)
MsgBox % Hash("alg:SHA512 Base64:1",  A_AhkPath)    ; 格式化为Base64文本的SHA512哈希

Hash(Options, ByRef Var, nBytes:="") {  ; Hash() v0.37 by SKAN on D444/D445 @ tiny.cc/hashit
  Local
  HA := {"ALG":"SHA256","BAS":0, "UPP":1, "ENC":"UTF-8"}
  Loop, Parse, % Format("{:U}", Options), %A_Space%, +
    A := StrSplit(A_LoopField, ":", "+"), HA[ SubStr(A[1], 1, 3) ] := A[2]

  HA.X := ( HA.ENC="UTF-16" ? 2 : 1)
  OK1  := { "SHA1":1, "SHA256":1, "SHA384":1, "SHA512":1, "MD2":1, "MD4":1, "MD5":1 }[ HA.ALG ]
  OK2  := { "CP0":1, "UTF-8":1, "UTF-16":1}[ HA.ENC ]
  NaN  := ( StrLen(nBytes) And (nBytes != Round(nBytes)) ),                    lVar := StrLen(Var)
  pNum := ( lVar And [var].GetCapacity(1)="" And (Var = Abs(Round(Var))) ),    nVar := VarSetCapacity(Var)

  If ( OK1="" Or OK2="" Or NaN=1 Or lVar<1 Or (pNum=1 And nBytes<1) Or (pNum=0 And nVar<nBytes))
     Return ( 0, ErrorLevel := OK1="" ? "Algorithm not known.`n=> MD2 MD4 MD5 SHA1 SHA256 SHA384 SHA512`nDefault: SHA256"
                            :  OK2="" ? "Codepage incorrect.`n=> CP0 UTF-16 UTF-8`nDefault: UTF-8"
                            :  NaN=1  ? "nBytes in incorrect format"
                            :  lVar<1 ? "Var is empty. Nothing to hash."
              : (pNum=1 And nBytes<1) ? "Pointer requires nBytes greater than 0."
           : (pNum=0 And nVar<nBytes) ? "Var's capacity is lesser than nBytes." : "" )

  hBcrypt := DllCall("Kernel32.dll\LoadLibrary", "Str","Bcrypt.dll", "Ptr")
  DllCall("Bcrypt.dll\BCryptOpenAlgorithmProvider", "PtrP",hAlg:=0, "WStr",HA.ALG, "Ptr",0, "Int",0, "UInt")
  DllCall("Bcrypt.dll\BCryptCreateHash", "Ptr",hAlg, "PtrP",hHash:=0, "Ptr", 0, "Int", 0, "Ptr",0, "Int",0, "Int", 0)

  nLen := 0, FileLen := File := rBytes := sStr := nErr := ""
  If ( nBytes!="" And (pBuf:=pNum ? Var+0 : &Var) ) {
    If ( nBytes<=0  )
         nBytes := StrPut(Var, HA.ENC)
       , VarSetCapacity(sStr, nBytes * HA.X)
       , nBytes := ( StrPut(Var, pBuf := &sStr, nBytes, HA.ENC) - 1 ) * HA.X
    nErr := DllCall("Bcrypt.dll\BCryptHashData", "Ptr",hHash, "Ptr",pBuf, "Int",nBytes, "Int", 0, "UInt")
  } Else {
    File := FileOpen(Var, "r -rwd")
    If  ( (FileLen := File.Length) And VarSetCapacity(Bin, 65536) )
      Loop
        If ( rBytes := File.RawRead(&Bin, 65536) )
          nErr   := DllCall("Bcrypt.dll\BCryptHashData", "Ptr",hHash, "Ptr",&Bin, "Int",rBytes, "Int", 0, "Uint")
        Until ( nErr Or File.AtEOF Or !rBytes )
    File := ( FileLen="" ? 0 : File.Close() )
  }

  DllCall("Bcrypt.dll\BCryptGetProperty", "Ptr",hAlg, "WStr", "HashDigestLength", "UIntP",nLen, "Int",4, "PtrP",0, "Int",0)
  VarSetCapacity(Hash, nLen)
  DllCall("Bcrypt.dll\BCryptFinishHash", "Ptr",hHash, "Ptr",&Hash, "Int",nLen, "Int", 0)
  DllCall("Bcrypt.dll\BCryptDestroyHash", "Ptr",hHash)
  DllCall("Bcrypt.dll\BCryptCloseAlgorithmProvider", "Ptr",hAlg, "Int",0)
  DllCall("Kernel32.dll\FreeLibrary", "Ptr",hBCrypt)

  If ( nErr=0 )
     VarSetCapacity(sStr, 260, 0),  nFlags := HA.BAS ? 0x40000001 : 0x4000000C
   , DllCall("Crypt32\CryptBinaryToString", "Ptr",&Hash, "Int",nLen, "Int",nFlags, "Str",sStr, "UIntP",130)
   , sStr := ( nFlags=0x4000000C And HA.UPP ? Format("{:U}", sStr) : sStr )

  Return ( sStr, ErrorLevel := File=0    ? ( FileExist(Var) ? "Open file error. File in use." : "File does not exist." )
                           : FileLen=0 ? "Zero byte file. Nothing to hash."
                : (FileLen & rBytes=0) ? "Read file error."
                                : nErr ? Format("Bcrypt error. 0x{:08X}", nErr)
                             : nErr="" ? "Unknown error." : "" )
}

 

字符串哈希值校验算法 - 单函数实现

MD5.ahk

; MD5算法是一种广泛使用的散列函数,产生128位散列值
MsgBox % MD5("Hello World") "`n"
     . MD5("Hello World", 1)

MD5(string, case := 0) {
  static MD5_DIGEST_LENGTH := 16
  hModule := DllCall("LoadLibrary", "Str", "advapi32.dll", "Ptr")
  , VarSetCapacity(MD5_CTX, 104, 0), DllCall("advapi32\MD5Init", "Ptr", &MD5_CTX)
  , DllCall("advapi32\MD5Update", "Ptr", &MD5_CTX, "AStr", string, "UInt", StrLen(string))
  , DllCall("advapi32\MD5Final", "Ptr", &MD5_CTX)
  loop % MD5_DIGEST_LENGTH
    o .= Format("{:02" (case ? "X" : "x") "}", NumGet(MD5_CTX, 87 + A_Index, "UChar"))
  return o, DllCall("FreeLibrary", "Ptr", hModule)
}

 

CRC32.ahk

; CRC32在AutoHotkey中的实现
CRC32(str) {
  static table := []
  loop 256 {
    crc := A_Index - 1
    loop 8
      crc := (crc & 1) ? (crc >> 1) ^ 0xEDB88320 : (crc >> 1)
    table[A_Index - 1] := crc
  }
  crc := ~0
  loop, parse, str
    crc := table[(crc & 0xFF) ^ Asc(A_LoopField)] ^ (crc >> 8)
  return Format("{:#x}", ~crc)
}

MsgBox % CRC32("The quick brown fox jumps over the lazy dog")  ; -> 0x414fa339


; CRC32通过DllCall(WinAPI)
CRC32WinAPI(str, enc = "UTF-8") {
  l := (enc = "CP1200" || enc = "UTF-16") ? 2 : 1, s := (StrPut(str, enc) - 1) * l
  VarSetCapacity(b, s, 0) && StrPut(str, &b, floor(s / l), enc)
  CRC32 := DllCall("ntdll.dll\RtlComputeCrc32", "UInt", 0, "Ptr", &b, "UInt", s)
  return Format("{:#x}", CRC32)
}

MsgBox % CRC32WinAPI("The quick brown fox jumps over the lazy dog")  ; -> 0x414fa339


; CRC32文件通过DllCall(WinAPI)
CRC32_File(filename) {
  if !(f := FileOpen(filename, "r", "UTF-8"))
    throw Exception("Failed to open file: " filename, -1)
  f.Seek(0)
  while (dataread := f.RawRead(data, 262144))
    crc := DllCall("ntdll.dll\RtlComputeCrc32", "uint", crc, "ptr", &data, "uint", dataread, "uint")
  f.Close()
  return Format("{:#x}", crc)
}

MsgBox % CRC32_File(A_AhkPath)

 

SHA1.ahk

; 在密码学中,SHA-1(安全哈希算法1)是一种加密哈希函数

MsgBox % SHA("Hello World") "`n"
       . SHA("Hello World", 1)

SHA(string, case := 0) {
  static SHA_DIGEST_LENGTH := 20
  hModule := DllCall("LoadLibrary", "Str", "advapi32.dll", "Ptr")
  , VarSetCapacity(SHA_CTX, 136, 0), DllCall("advapi32\A_SHAInit", "Ptr", &SHA_CTX)
  , DllCall("advapi32\A_SHAUpdate", "Ptr", &SHA_CTX, "AStr", string, "UInt", StrLen(string))
  , DllCall("advapi32\A_SHAFinal", "Ptr", &SHA_CTX, "UInt", &SHA_CTX + 116)
  loop % SHA_DIGEST_LENGTH
    o .= Format("{:02" (case ? "X" : "x") "}", NumGet(SHA_CTX, 115 + A_Index, "UChar"))
  return o, DllCall("FreeLibrary", "Ptr", hModule)
}

 

MD4.ahk

; MD4消息摘要算法是一个加密散列函数

MsgBox % MD4("Hello World") "`n"
       . MD4("Hello World", 1)

MD4(string, case := 0) {
  static MD4_DIGEST_LENGTH := 16
  hModule := DllCall("LoadLibrary", "Str", "advapi32.dll", "Ptr")
  , VarSetCapacity(MD4_CTX, 104, 0), DllCall("advapi32\MD4Init", "Ptr", &MD4_CTX)
  , DllCall("advapi32\MD4Update", "Ptr", &MD4_CTX, "AStr", string, "UInt", StrLen(string))
  , DllCall("advapi32\MD4Final", "Ptr", &MD4_CTX)
  loop % MD4_DIGEST_LENGTH
    o .= Format("{:02" (case ? "X" : "x") "}", NumGet(MD4_CTX, 87 + A_Index, "UChar"))
  return o, DllCall("FreeLibrary", "Ptr", hModule)
}

 

Adler32.ahk

; Adler-32是一种校验和算法

MsgBox % Adler32("Hello World")                                 ; <== 0x180b041d
MsgBox % Adler32("The quick brown fox jumps over the lazy dog") ; <== 0x5bdc0fda

Adler32(str) {
  a := 1, b := 0
  loop, parse, str
    b := Mod(b + (a := Mod(a + Asc(A_LoopField), 0xFFF1)), 0xFFF1)
  return Format("{:#x}", (b << 16) | a)
}

 

综合的哈希值校验算法实现类库

; AES 的密码长度必须为 128位、192位、256位 也就是 16字节、24字节、32字节 中的一个,并且 IV 长度也要对应,否则将报错。

; 从字符串创建 SHA-1 哈希值
MsgBox % Crypt.Hash.String("SHA1", "The quick brown fox jumps over the lazy dog")
; -> 2fd4e1c67a2d28fced849ee1bb76e7391b93eb12

; 使用HMAC从字符串创建SHA-256哈希
MsgBox % Crypt.Hash.HMAC("SHA256", "The quick brown fox jumps over the lazy dog", "Secret Salt")
; -> 68dba4b3a6d5c36b6e3567e1a925fe87c7386162e8fb6e2e9f17ade4aa7dc262

; 从文件创建SHA-256哈希
MsgBox % Crypt.Hash.File("SHA256", "C:\Program Files\AutoHotkey\AutoHotkey.exe")
; -> 0a9964fe0e0fb3f0679df317a65f9945c474dab8c4370b45b93da64a8b201b9f

; 使用SHA-1创建PBKDF2哈希,1500次迭代,从字符串中创建密钥大小为192
MsgBox % Crypt.Hash.PBKDF2("SHA1", "The quick brown fox jumps over the lazy dog", "Secret Salt", 1500, 192)
; -> 531c1bbae7c3de019d1f53adcac7d85bf2b04caba9d6d6d1

; 更多示例详见:https://www.autohotkey.com/boards/viewtopic.php?p=109958#p109958

; ==============================================================================================
; AutoHotkey wrapper for Cryptography API: Next Generation
;
; Author ....: jNizM
; Released ..: 2016-09-15
; Modified ..: 2021-01-04
; Github ....: https://github.com/jNizM/AHK_CNG
; Forum .....: https://www.autohotkey.com/boards/viewtopic.php?f=6&t=23413
; ==============================================================================================


class Crypt
{

  ; ===== PUBLIC CLASS / METHODS ==============================================================================================

  class Encrypt
  {

    String(AlgId, Mode := "", String := "", Key := "", IV := "", Encoding := "utf-8", Output := "BASE64")
    {
      try
      {
        ; verify the encryption algorithm
        if !(ALGORITHM_IDENTIFIER := Crypt.Verify.EncryptionAlgorithm(AlgId))
          throw Exception("Wrong ALGORITHM_IDENTIFIER", -1)

        ; open an algorithm handle.
        if !(ALG_HANDLE := Crypt.BCrypt.OpenAlgorithmProvider(ALGORITHM_IDENTIFIER))
          throw Exception("BCryptOpenAlgorithmProvider failed", -1)

        ; verify the chaining mode
        if (CHAINING_MODE := Crypt.Verify.ChainingMode(Mode))
          ; set chaining mode property.
          if !(Crypt.BCrypt.SetProperty(ALG_HANDLE, Crypt.Constants.BCRYPT_CHAINING_MODE, CHAINING_MODE))
            throw Exception("SetProperty failed", -1)

        ; generate the key from supplied input key bytes.
        if !(KEY_HANDLE := Crypt.BCrypt.GenerateSymmetricKey(ALG_HANDLE, Key, Encoding))
          throw Exception("GenerateSymmetricKey failed", -1)

        ; calculate the block length for the IV.
        if !(BLOCK_LENGTH := Crypt.BCrypt.GetProperty(ALG_HANDLE, Crypt.Constants.BCRYPT_BLOCK_LENGTH, 4))
          throw Exception("GetProperty failed", -1)

        ; use the key to encrypt the plaintext buffer. for block sized messages, block padding will add an extra block.
        cbInput := Crypt.Helper.StrPutVar(String, pbInput, Encoding)
        if !(CIPHER_LENGTH := Crypt.BCrypt.Encrypt(KEY_HANDLE, pbInput, cbInput, IV, BLOCK_LENGTH, CIPHER_DATA, Crypt.Constants.BCRYPT_BLOCK_PADDING))
          throw Exception("Encrypt failed", -1)

        ; convert binary data to string (base64 / hex / hexraw)
        if !(ENCRYPT := Crypt.Helper.CryptBinaryToString(CIPHER_DATA, CIPHER_LENGTH, Output))
          throw Exception("CryptBinaryToString failed", -1)
      }
      catch Exception
      {
        ; represents errors that occur during application execution
        throw Exception
      }
      finally
      {
        ; cleaning up resources
        if (KEY_HANDLE)
          Crypt.BCrypt.DestroyKey(KEY_HANDLE)

        if (ALG_HANDLE)
          Crypt.BCrypt.CloseAlgorithmProvider(ALG_HANDLE)
      }
      return ENCRYPT
    }
  }


  class Decrypt
  {

    String(AlgId, Mode := "", String := "", Key := "", IV := "", Encoding := "utf-8", Input := "BASE64")
    {
      try
      {
        ; verify the encryption algorithm
        if !(ALGORITHM_IDENTIFIER := Crypt.Verify.EncryptionAlgorithm(AlgId))
          throw Exception("Wrong ALGORITHM_IDENTIFIER", -1)

        ; open an algorithm handle.
        if !(ALG_HANDLE := Crypt.BCrypt.OpenAlgorithmProvider(ALGORITHM_IDENTIFIER))
          throw Exception("BCryptOpenAlgorithmProvider failed", -1)

        ; verify the chaining mode
        if (CHAINING_MODE := Crypt.Verify.ChainingMode(Mode))
          ; set chaining mode property.
          if !(Crypt.BCrypt.SetProperty(ALG_HANDLE, Crypt.Constants.BCRYPT_CHAINING_MODE, CHAINING_MODE))
            throw Exception("SetProperty failed", -1)

        ; generate the key from supplied input key bytes.
        if !(KEY_HANDLE := Crypt.BCrypt.GenerateSymmetricKey(ALG_HANDLE, Key, Encoding))
          throw Exception("GenerateSymmetricKey failed", -1)

        ; convert encrypted string (base64 / hex / hexraw) to binary data
        if !(CIPHER_LENGTH := Crypt.Helper.CryptStringToBinary(String, CIPHER_DATA, Input))
          throw Exception("CryptStringToBinary failed", -1)

        ; calculate the block length for the IV.
        if !(BLOCK_LENGTH := Crypt.BCrypt.GetProperty(ALG_HANDLE, Crypt.Constants.BCRYPT_BLOCK_LENGTH, 4))
          throw Exception("GetProperty failed", -1)

        ; use the key to decrypt the data to plaintext buffer
        if !(DECRYPT_LENGTH := Crypt.BCrypt.Decrypt(KEY_HANDLE, CIPHER_DATA, CIPHER_LENGTH, IV, BLOCK_LENGTH, DECRYPT_DATA, Crypt.Constants.BCRYPT_BLOCK_PADDING))
          throw Exception("Decrypt failed", -1)

        ; receive the decrypted plaintext
        DECRYPT := StrGet(&DECRYPT_DATA, DECRYPT_LENGTH, Encoding)
      }
      catch Exception
      {
        ; represents errors that occur during application execution
        throw Exception
      }
      finally
      {
        ; cleaning up resources
        if (KEY_HANDLE)
          Crypt.BCrypt.DestroyKey(KEY_HANDLE)

        if (ALG_HANDLE)
          Crypt.BCrypt.CloseAlgorithmProvider(ALG_HANDLE)
      }
      return DECRYPT
    }

  }


  class Hash
  {

    String(AlgId, String, Encoding := "utf-8", Output := "HEXRAW")
    {
      try
      {
        ; verify the hash algorithm
        if !(ALGORITHM_IDENTIFIER := Crypt.Verify.HashAlgorithm(AlgId))
          throw Exception("Wrong ALGORITHM_IDENTIFIER", -1)

        ; open an algorithm handle
        if !(ALG_HANDLE := Crypt.BCrypt.OpenAlgorithmProvider(ALGORITHM_IDENTIFIER))
          throw Exception("BCryptOpenAlgorithmProvider failed", -1)

        ; create a hash
        if !(HASH_HANDLE := Crypt.BCrypt.CreateHash(ALG_HANDLE))
          throw Exception("CreateHash failed", -1)

        ; hash some data
        cbInput := Crypt.Helper.StrPutVar(String, pbInput, Encoding)
        if !(Crypt.BCrypt.HashData(HASH_HANDLE, pbInput, cbInput))
          throw Exception("HashData failed", -1)

        ; calculate the length of the hash
        if !(HASH_LENGTH := Crypt.BCrypt.GetProperty(ALG_HANDLE, Crypt.Constants.BCRYPT_HASH_LENGTH, 4))
          throw Exception("GetProperty failed", -1)

        ; close the hash
        if !(Crypt.BCrypt.FinishHash(HASH_HANDLE, HASH_DATA, HASH_LENGTH))
          throw Exception("FinishHash failed", -1)

        ; convert bin to string (base64 / hex)
        if !(HASH := Crypt.Helper.CryptBinaryToString(HASH_DATA, HASH_LENGTH, Output))
          throw Exception("CryptBinaryToString failed", -1)
      }
      catch Exception
      {
        ; represents errors that occur during application execution
        throw Exception
      }
      finally
      {
        ; cleaning up resources
        if (HASH_HANDLE)
          Crypt.BCrypt.DestroyHash(HASH_HANDLE)

        if (ALG_HANDLE)
          Crypt.BCrypt.CloseAlgorithmProvider(ALG_HANDLE)
      }
      return HASH
    }


    File(AlgId, FileName, Bytes := 1048576, Offset := 0, Length := -1, Encoding := "utf-8", Output := "HEXRAW")
    {
      try
      {
        ; verify the hash algorithm
        if !(ALGORITHM_IDENTIFIER := Crypt.Verify.HashAlgorithm(AlgId))
          throw Exception("Wrong ALGORITHM_IDENTIFIER", -1)

        ; open an algorithm handle
        if !(ALG_HANDLE := Crypt.BCrypt.OpenAlgorithmProvider(ALGORITHM_IDENTIFIER))
          throw Exception("BCryptOpenAlgorithmProvider failed", -1)

        ; create a hash
        if !(HASH_HANDLE := Crypt.BCrypt.CreateHash(ALG_HANDLE))
          throw Exception("CreateHash failed", -1)

        ; hash some data
        if !(IsObject(File := FileOpen(FileName, "r", Encoding)))
          throw Exception("Failed to open file: " FileName, -1)
        Length := Length < 0 ? File.Length - Offset : Length
        if ((Offset + Length) > File.Length)
          throw Exception("Invalid parameters offset / length!", -1)
        while (Length > Bytes) && (Dataread := File.RawRead(Data, Bytes))
        {
          if !(Crypt.BCrypt.HashData(HASH_HANDLE, Data, Dataread))
            throw Exception("HashData failed", -1)
          Length -= Dataread
        }
        if (Length > 0)
        {
          if (Dataread := File.RawRead(Data, Length))
          {
            if !(Crypt.BCrypt.HashData(HASH_HANDLE, Data, Dataread))
              throw Exception("HashData failed", -1)
          }
        }

        ; calculate the length of the hash
        if !(HASH_LENGTH := Crypt.BCrypt.GetProperty(ALG_HANDLE, Crypt.Constants.BCRYPT_HASH_LENGTH, 4))
          throw Exception("GetProperty failed", -1)

        ; close the hash
        if !(Crypt.BCrypt.FinishHash(HASH_HANDLE, HASH_DATA, HASH_LENGTH))
          throw Exception("FinishHash failed", -1)

        ; convert bin to string (base64 / hex)
        if !(HASH := Crypt.Helper.CryptBinaryToString(HASH_DATA, HASH_LENGTH, Output))
          throw Exception("CryptBinaryToString failed", -1)
      }
      catch Exception
      {
        ; represents errors that occur during application execution
        throw Exception
      }
      finally
      {
        ; cleaning up resources
        if (File)
          File.Close()

        if (HASH_HANDLE)
          Crypt.BCrypt.DestroyHash(HASH_HANDLE)

        if (ALG_HANDLE)
          Crypt.BCrypt.CloseAlgorithmProvider(ALG_HANDLE)
      }
      return HASH
    }


    HMAC(AlgId, String, Hmac, Encoding := "utf-8", Output := "HEXRAW")
    {
      try
      {
        ; verify the hash algorithm
        if !(ALGORITHM_IDENTIFIER := Crypt.Verify.HashAlgorithm(AlgId))
          throw Exception("Wrong ALGORITHM_IDENTIFIER", -1)

        ; open an algorithm handle
        if !(ALG_HANDLE := Crypt.BCrypt.OpenAlgorithmProvider(ALGORITHM_IDENTIFIER, Crypt.Constants.BCRYPT_ALG_HANDLE_HMAC_FLAG))
          throw Exception("BCryptOpenAlgorithmProvider failed", -1)

        ; create a hash
        if !(HASH_HANDLE := Crypt.BCrypt.CreateHash(ALG_HANDLE, Hmac, Encoding))
          throw Exception("CreateHash failed", -1)

        ; hash some data
        cbInput := Crypt.helper.StrPutVar(String, pbInput, Encoding)
        if !(Crypt.BCrypt.HashData(HASH_HANDLE, pbInput, cbInput))
          throw Exception("HashData failed", -1)

        ; calculate the length of the hash
        if !(HASH_LENGTH := Crypt.BCrypt.GetProperty(ALG_HANDLE, Crypt.Constants.BCRYPT_HASH_LENGTH, 4))
          throw Exception("GetProperty failed", -1)

        ; close the hash
        if !(Crypt.BCrypt.FinishHash(HASH_HANDLE, HASH_DATA, HASH_LENGTH))
          throw Exception("FinishHash failed", -1)

        ; convert bin to string (base64 / hex)
        if !(HMAC := Crypt.Helper.CryptBinaryToString(HASH_DATA, HASH_LENGTH, Output))
          throw Exception("CryptBinaryToString failed", -1)
      }
      catch Exception
      {
        ; represents errors that occur during application execution
        throw Exception
      }
      finally
      {
        ; cleaning up resources
        if (HASH_HANDLE)
          Crypt.BCrypt.DestroyHash(HASH_HANDLE)

        if (ALG_HANDLE)
          Crypt.BCrypt.CloseAlgorithmProvider(ALG_HANDLE)
      }
      return HMAC
    }


    PBKDF2(AlgId, Password, Salt, Iterations := 4096, KeySize := 256, Encoding := "utf-8", Output := "HEXRAW")
    {
      try
      {
        ; verify the hash algorithm
        if !(ALGORITHM_IDENTIFIER := Crypt.Verify.HashAlgorithm(AlgId))
          throw Exception("Wrong ALGORITHM_IDENTIFIER", -1)

        ; open an algorithm handle
        if !(ALG_HANDLE := Crypt.BCrypt.OpenAlgorithmProvider(ALGORITHM_IDENTIFIER, Crypt.Constants.BCRYPT_ALG_HANDLE_HMAC_FLAG))
          throw Exception("BCryptOpenAlgorithmProvider failed", -1)

        ; derives a key from a hash value
        if !(Crypt.BCrypt.DeriveKeyPBKDF2(ALG_HANDLE, Password, Salt, Iterations, PBKDF2_DATA, KeySize / 8, Encoding))
          throw Exception("CreateHash failed", -1)

        ; convert bin to string (base64 / hex)
        if !(PBKDF2 := Crypt.Helper.CryptBinaryToString(PBKDF2_DATA , KeySize / 8, Output))
          throw Exception("CryptBinaryToString failed", -1)
      }
      catch Exception
      {
        ; represents errors that occur during application execution
        throw Exception
      }
      finally
      {
        ; cleaning up resources
        if (ALG_HANDLE)
          Crypt.BCrypt.CloseAlgorithmProvider(ALG_HANDLE)
      }
      return PBKDF2
    }

  }



  ; ===== PRIVATE CLASS / METHODS =============================================================================================


  /*
    CNG BCrypt Functions
    https://docs.microsoft.com/en-us/windows/win32/api/bcrypt/
  */
  class BCrypt
  {
    static hBCRYPT := DllCall("LoadLibrary", "str", "bcrypt.dll", "ptr")
    static STATUS_SUCCESS := 0


    CloseAlgorithmProvider(hAlgorithm)
    {
      DllCall("bcrypt\BCryptCloseAlgorithmProvider", "ptr", hAlgorithm, "uint", 0)
    }


    CreateHash(hAlgorithm, hmac := 0, encoding := "utf-8")
    {
      if (hmac)
        cbSecret := Crypt.helper.StrPutVar(hmac, pbSecret, encoding)
      NT_STATUS := DllCall("bcrypt\BCryptCreateHash", "ptr",  hAlgorithm
                                                    , "ptr*", phHash
                                                    , "ptr",  pbHashObject := 0
                                                    , "uint", cbHashObject := 0
                                                    , "ptr",  (pbSecret ? &pbSecret : 0)
                                                    , "uint", (cbSecret ? cbSecret : 0)
                                                    , "uint", dwFlags := 0)

      if (NT_STATUS = this.STATUS_SUCCESS)
        return phHash
      return false
    }


    DeriveKeyPBKDF2(hPrf, Password, Salt, cIterations, ByRef pbDerivedKey, cbDerivedKey, Encoding := "utf-8")
    {
      cbPassword := Crypt.Helper.StrPutVar(Password, pbPassword, Encoding)
      cbSalt := Crypt.Helper.StrPutVar(Salt, pbSalt, Encoding)
    
      VarSetCapacity(pbDerivedKey, cbDerivedKey, 0)
      NT_STATUS := DllCall("bcrypt\BCryptDeriveKeyPBKDF2", "ptr",   hPrf
                                                         , "ptr",   &pbPassword
                                                         , "uint",  cbPassword
                                                         , "ptr",   &pbSalt
                                                         , "uint",  cbSalt
                                                         , "int64", cIterations
                                                         , "ptr",   &pbDerivedKey
                                                         , "uint",  cbDerivedKey
                                                         , "uint",  dwFlags := 0)

      if (NT_STATUS = this.STATUS_SUCCESS)
        return true
      return false
    }


    DestroyHash(hHash)
    {
      DllCall("bcrypt\BCryptDestroyHash", "ptr", hHash)
    }


    DestroyKey(hKey)
    {
      DllCall("bcrypt\BCryptDestroyKey", "ptr", hKey)
    }


    Decrypt(hKey, ByRef String, cbInput, IV, BCRYPT_BLOCK_LENGTH, ByRef pbOutput, dwFlags)
    {
      VarSetCapacity(pbInput, cbInput, 0)
      DllCall("msvcrt\memcpy", "ptr", &pbInput, "ptr", &String, "ptr", cbInput)

      if (IV != "")
      {
        cbIV := VarSetCapacity(pbIV, BCRYPT_BLOCK_LENGTH, 0)
        StrPut(IV, &pbIV, BCRYPT_BLOCK_LENGTH, Encoding)
      }

      NT_STATUS := DllCall("bcrypt\BCryptDecrypt", "ptr",   hKey
                                                 , "ptr",   &pbInput
                                                 , "uint",  cbInput
                                                 , "ptr",   0
                                                 , "ptr",   (pbIV ? &pbIV : 0)
                                                 , "uint",  (cbIV ? &cbIV : 0)
                                                 , "ptr",   0
                                                 , "uint",  0
                                                 , "uint*", cbOutput
                                                 , "uint",  dwFlags)
      if (NT_STATUS = this.STATUS_SUCCESS)
      {
        VarSetCapacity(pbOutput, cbOutput, 0)
        NT_STATUS := DllCall("bcrypt\BCryptDecrypt", "ptr",   hKey
                                                     , "ptr",   &pbInput
                                                     , "uint",  cbInput
                                                     , "ptr",   0
                                                     , "ptr",   (pbIV ? &pbIV : 0)
                                                     , "uint",  (cbIV ? &cbIV : 0)
                                                     , "ptr",   &pbOutput
                                                     , "uint",  cbOutput
                                                     , "uint*", cbOutput
                                                     , "uint",  dwFlags)
        if (NT_STATUS = this.STATUS_SUCCESS)
        {
          return cbOutput
        }
      }
      return false
    }


    Encrypt(hKey, ByRef pbInput, cbInput, IV, BCRYPT_BLOCK_LENGTH, ByRef pbOutput, dwFlags := 0)
    {
      ;cbInput := Crypt.Helper.StrPutVar(String, pbInput, Encoding)

      if (IV != "")
      {
        cbIV := VarSetCapacity(pbIV, BCRYPT_BLOCK_LENGTH, 0)
        StrPut(IV, &pbIV, BCRYPT_BLOCK_LENGTH, Encoding)
      }

      NT_STATUS := DllCall("bcrypt\BCryptEncrypt", "ptr",   hKey
                                                 , "ptr",   &pbInput
                                                 , "uint",  cbInput
                                                 , "ptr",   0
                                                 , "ptr",   (pbIV ? &pbIV : 0)
                                                 , "uint",  (cbIV ? &cbIV : 0)
                                                 , "ptr",   0
                                                 , "uint",  0
                                                 , "uint*", cbOutput
                                                 , "uint",  dwFlags)
      if (NT_STATUS = this.STATUS_SUCCESS)
      {
        VarSetCapacity(pbOutput, cbOutput, 0)
        NT_STATUS := DllCall("bcrypt\BCryptEncrypt", "ptr",   hKey
                                                     , "ptr",   &pbInput
                                                     , "uint",  cbInput
                                                     , "ptr",   0
                                                     , "ptr",   (pbIV ? &pbIV : 0)
                                                     , "uint",  (cbIV ? &cbIV : 0)
                                                     , "ptr",   &pbOutput
                                                     , "uint",  cbOutput
                                                     , "uint*", cbOutput
                                                     , "uint",  dwFlags)
        if (NT_STATUS = this.STATUS_SUCCESS)
        {
          return cbOutput
        }
      }
      return false
    }


    EnumAlgorithms(dwAlgOperations)
    {
      NT_STATUS := DllCall("bcrypt\BCryptEnumAlgorithms", "uint",  dwAlgOperations
                                                        , "uint*", pAlgCount
                                                        , "ptr*",  ppAlgList
                                                        , "uint",  dwFlags := 0)

      if (NT_STATUS = this.STATUS_SUCCESS)
      {
        addr := ppAlgList, BCRYPT_ALGORITHM_IDENTIFIER := []
        loop % pAlgCount
        {
          BCRYPT_ALGORITHM_IDENTIFIER[A_Index, "Name"]  := StrGet(NumGet(addr + A_PtrSize * 0, "uptr"), "utf-16")
          BCRYPT_ALGORITHM_IDENTIFIER[A_Index, "Class"] := NumGet(addr + A_PtrSize * 1, "uint")
          BCRYPT_ALGORITHM_IDENTIFIER[A_Index, "Flags"] := NumGet(addr + A_PtrSize * 1 + 4, "uint")
          addr += A_PtrSize * 2
        }
        return BCRYPT_ALGORITHM_IDENTIFIER
      }
      return false
    }


    EnumProviders(pszAlgId)
    {
      NT_STATUS := DllCall("bcrypt\BCryptEnumProviders", "ptr",   pszAlgId
                                                       , "uint*", pImplCount
                                                       , "ptr*",  ppImplList
                                                       , "uint",  dwFlags := 0)

      if (NT_STATUS = this.STATUS_SUCCESS)
      {
        addr := ppImplList, BCRYPT_PROVIDER_NAME := []
        loop % pImplCount
        {
          BCRYPT_PROVIDER_NAME.Push(StrGet(NumGet(addr + A_PtrSize * 0, "uptr"), "utf-16"))
          addr += A_PtrSize
        }
        return BCRYPT_PROVIDER_NAME
      }
      return false
    }


    FinishHash(hHash, ByRef pbOutput, cbOutput)
    {
      VarSetCapacity(pbOutput, cbOutput, 0)
      NT_STATUS := DllCall("bcrypt\BCryptFinishHash", "ptr",  hHash
                                                    , "ptr",  &pbOutput
                                                    , "uint", cbOutput
                                                    , "uint", dwFlags := 0)

      if (NT_STATUS = this.STATUS_SUCCESS)
        return cbOutput
      return false
    }


    GenerateSymmetricKey(hAlgorithm, Key, Encoding := "utf-8")
    {
      cbSecret := Crypt.Helper.StrPutVar(Key, pbSecret, Encoding)
      NT_STATUS := DllCall("bcrypt\BCryptGenerateSymmetricKey", "ptr",  hAlgorithm
                                                              , "ptr*", phKey
                                                              , "ptr",  0
                                                              , "uint", 0
                                                              , "ptr",  &pbSecret
                                                              , "uint", cbSecret
                                                              , "uint", dwFlags := 0)

      if (NT_STATUS = this.STATUS_SUCCESS)
        return phKey
      return false
    }


    GetProperty(hObject, pszProperty, cbOutput)
    {
      NT_STATUS := DllCall("bcrypt\BCryptGetProperty", "ptr",   hObject
                                                     , "ptr",   &pszProperty
                                                     , "uint*", pbOutput
                                                     , "uint",  cbOutput
                                                     , "uint*", pcbResult
                                                     , "uint",  dwFlags := 0)

      if (NT_STATUS = this.STATUS_SUCCESS)
        return pbOutput
      return false
    }


    HashData(hHash, ByRef pbInput, cbInput)
    {
      NT_STATUS := DllCall("bcrypt\BCryptHashData", "ptr",  hHash
                                                  , "ptr",  &pbInput
                                                  , "uint", cbInput
                                                  , "uint", dwFlags := 0)

      if (NT_STATUS = this.STATUS_SUCCESS)
        return true
      return false
    }


    OpenAlgorithmProvider(pszAlgId, dwFlags := 0, pszImplementation := 0)
    {
      NT_STATUS := DllCall("bcrypt\BCryptOpenAlgorithmProvider", "ptr*", phAlgorithm
                                                               , "ptr",  &pszAlgId
                                                               , "ptr",  pszImplementation
                                                               , "uint", dwFlags)

      if (NT_STATUS = this.STATUS_SUCCESS)
        return phAlgorithm
      return false
    }


    SetProperty(hObject, pszProperty, pbInput)
    {
      bInput := StrLen(pbInput)
      NT_STATUS := DllCall("bcrypt\BCryptSetProperty", "ptr",   hObject
                                                     , "ptr",   &pszProperty
                                                     , "ptr",   &pbInput
                                                     , "uint",  bInput
                                                     , "uint",  dwFlags := 0)

      if (NT_STATUS = this.STATUS_SUCCESS)
        return true
      return false
    }
  }


  class Helper
  {
    static hCRYPT32 := DllCall("LoadLibrary", "str", "crypt32.dll", "ptr")

    CryptBinaryToString(ByRef pbBinary, cbBinary, dwFlags := "BASE64")
    {
      static CRYPT_STRING := { "BASE64": 0x1, "BINARY": 0x2, "HEX": 0x4, "HEXRAW": 0xc }
      static CRYPT_STRING_NOCRLF := 0x40000000

      if (DllCall("crypt32\CryptBinaryToString", "ptr",   &pbBinary
                                               , "uint",  cbBinary
                                               , "uint",  (CRYPT_STRING[dwFlags] | CRYPT_STRING_NOCRLF)
                                               , "ptr",   0
                                               , "uint*", pcchString))
      {
        VarSetCapacity(pszString, pcchString << !!A_IsUnicode, 0)
        if (DllCall("crypt32\CryptBinaryToString", "ptr",   &pbBinary
                                                   , "uint",  cbBinary
                                                   , "uint",  (CRYPT_STRING[dwFlags] | CRYPT_STRING_NOCRLF)
                                                   , "ptr",   &pszString
                                                   , "uint*", pcchString))
        {
          return StrGet(&pszString)
        }
      }
      return false
    }


    CryptStringToBinary(pszString, ByRef pbBinary, dwFlags := "BASE64")
    {
      static CRYPT_STRING := { "BASE64": 0x1, "BINARY": 0x2, "HEX": 0x4, "HEXRAW": 0xc }

      if (DllCall("crypt32\CryptStringToBinary", "ptr",   &pszString
                                               , "uint",  0
                                               , "uint",  CRYPT_STRING[dwFlags]
                                               , "ptr",   0
                                               , "uint*", pcbBinary
                                               , "ptr",   0
                                               , "ptr",   0))
      {
        VarSetCapacity(pbBinary, pcbBinary, 0)
        if (DllCall("crypt32\CryptStringToBinary", "ptr",   &pszString
                                                 , "uint",  0
                                                 , "uint",  CRYPT_STRING[dwFlags]
                                                 , "ptr",   &pbBinary
                                                 , "uint*", pcbBinary
                                                 , "ptr",   0
                                                 , "ptr",   0))
        {
          return pcbBinary
        }
      }
      return false
    }


    StrPutVar(String, ByRef Data, Encoding)
    {
      if (Encoding = "hex")
      {
        String := InStr(String, "0x") ? SubStr(String, 3) : String
        VarSetCapacity(Data, (Length := StrLen(String) // 2), 0)
        loop % Length
          NumPut("0x" SubStr(String, 2 * A_Index - 1, 2), Data, A_Index - 1, "char")
        return Length
      }
      else
      {
        VarSetCapacity(Data, Length := StrPut(String, Encoding) * ((Encoding = "utf-16" || Encoding = "cp1200") ? 2 : 1) - 1)
        return StrPut(String, &Data, Length, Encoding)
      }
    }

  }


  class Verify
  {

    ChainingMode(ChainMode)
    {
      switch ChainMode
      {
        case "CBC", "ChainingModeCBC": return Crypt.Constants.BCRYPT_CHAIN_MODE_CBC
        case "CFB", "ChainingModeCFB": return Crypt.Constants.BCRYPT_CHAIN_MODE_CFB
        case "ECB", "ChainingModeECB": return Crypt.Constants.BCRYPT_CHAIN_MODE_ECB
        default: return ""
      }
    }


    EncryptionAlgorithm(Algorithm)
    {
      switch Algorithm
      {
        case "AES":                return Crypt.Constants.BCRYPT_AES_ALGORITHM
        case "DES":                return Crypt.Constants.BCRYPT_DES_ALGORITHM
        case "RC2":                return Crypt.Constants.BCRYPT_RC2_ALGORITHM
        case "RC4":                return Crypt.Constants.BCRYPT_RC4_ALGORITHM
        default: return ""
      }
    }


    HashAlgorithm(Algorithm)
    {
      switch Algorithm
      {
        case "MD2":               return Crypt.Constants.BCRYPT_MD2_ALGORITHM
        case "MD4":               return Crypt.Constants.BCRYPT_MD4_ALGORITHM
        case "MD5":               return Crypt.Constants.BCRYPT_MD5_ALGORITHM
        case "SHA1", "SHA-1":     return Crypt.Constants.BCRYPT_SHA1_ALGORITHM
        case "SHA256", "SHA-256": return Crypt.Constants.BCRYPT_SHA256_ALGORITHM
        case "SHA384", "SHA-384": return Crypt.Constants.BCRYPT_SHA384_ALGORITHM
        case "SHA512", "SHA-512": return Crypt.Constants.BCRYPT_SHA512_ALGORITHM
        default: return ""
      }
    }

  }



  ; ===== CONSTANTS =====================================================================

  class Constants
  {
    static BCRYPT_ALG_HANDLE_HMAC_FLAG            := 0x00000008
    static BCRYPT_BLOCK_PADDING                   := 0x00000001


    ; AlgOperations flags for use with BCryptEnumAlgorithms()
    static BCRYPT_CIPHER_OPERATION                := 0x00000001
    static BCRYPT_HASH_OPERATION                  := 0x00000002
    static BCRYPT_ASYMMETRIC_ENCRYPTION_OPERATION := 0x00000004
    static BCRYPT_SECRET_AGREEMENT_OPERATION      := 0x00000008
    static BCRYPT_SIGNATURE_OPERATION             := 0x00000010
    static BCRYPT_RNG_OPERATION                   := 0x00000020
    static BCRYPT_KEY_DERIVATION_OPERATION        := 0x00000040


    ; https://docs.microsoft.com/en-us/windows/win32/seccng/cng-algorithm-identifiers
    static BCRYPT_3DES_ALGORITHM                  := "3DES"
    static BCRYPT_3DES_112_ALGORITHM              := "3DES_112"
    static BCRYPT_AES_ALGORITHM                   := "AES"
    static BCRYPT_AES_CMAC_ALGORITHM              := "AES-CMAC"
    static BCRYPT_AES_GMAC_ALGORITHM              := "AES-GMAC"
    static BCRYPT_DES_ALGORITHM                   := "DES"
    static BCRYPT_DESX_ALGORITHM                  := "DESX"
    static BCRYPT_MD2_ALGORITHM                   := "MD2"
    static BCRYPT_MD4_ALGORITHM                   := "MD4"
    static BCRYPT_MD5_ALGORITHM                   := "MD5"
    static BCRYPT_RC2_ALGORITHM                   := "RC2"
    static BCRYPT_RC4_ALGORITHM                   := "RC4"
    static BCRYPT_RNG_ALGORITHM                   := "RNG"
    static BCRYPT_SHA1_ALGORITHM                  := "SHA1"
    static BCRYPT_SHA256_ALGORITHM                := "SHA256"
    static BCRYPT_SHA384_ALGORITHM                := "SHA384"
    static BCRYPT_SHA512_ALGORITHM                := "SHA512"
    static BCRYPT_PBKDF2_ALGORITHM                := "PBKDF2"
    static BCRYPT_XTS_AES_ALGORITHM               := "XTS-AES"


    ; https://docs.microsoft.com/en-us/windows/win32/seccng/cng-property-identifiers
    static BCRYPT_BLOCK_LENGTH                    := "BlockLength"
    static BCRYPT_CHAINING_MODE                   := "ChainingMode"
    static BCRYPT_CHAIN_MODE_CBC                  := "ChainingModeCBC"
    static BCRYPT_CHAIN_MODE_CCM                  := "ChainingModeCCM"
    static BCRYPT_CHAIN_MODE_CFB                  := "ChainingModeCFB"
    static BCRYPT_CHAIN_MODE_ECB                  := "ChainingModeECB"
    static BCRYPT_CHAIN_MODE_GCM                  := "ChainingModeGCM"
    static BCRYPT_HASH_LENGTH                     := "HashDigestLength"
    static BCRYPT_OBJECT_LENGTH                   := "ObjectLength"
  }
}

 

声明:站内资源为整理优化好的代码上传分享与学习研究,如果是开源代码基本都会标明出处,方便大家扩展学习路径。请不要恶意搬运,破坏站长辛苦整理维护的劳动成果。本站为爱好者分享站点,所有内容不作为商业行为。如若本站上传内容侵犯了原著者的合法权益,请联系我们进行删除下架。