Skip to content

Instantly share code, notes, and snippets.

@jNizM
Last active October 4, 2025 08:16
Show Gist options
  • Select an option

  • Save jNizM/79aa6a4b8ec428bf780f to your computer and use it in GitHub Desktop.

Select an option

Save jNizM/79aa6a4b8ec428bf780f to your computer and use it in GitHub Desktop.
[AHK] AES Encryption (String)
/*
Update v0.4
Todo: - CryptSetKeyParam (KP_MODE with CBC [maybe CFB, ECB, OFB or CTS])
*/
MsgBox % enc := AES.Encrypt("Test String", "testpw", 256)
MsgBox % AES.Decrypt(enc, "testpw", 256)
Class AES
{
Encrypt(string, password, alg)
{
len := this.StrPutVar(string, str_buf, 0, "UTF-16")
this.Crypt(str_buf, len, password, alg, 1)
return this.b64Encode(str_buf, len)
}
Decrypt(string, password, alg)
{
len := this.b64Decode(string, encr_Buf)
sLen := this.Crypt(encr_Buf, len, password, alg, 0)
sLen /= 2
return StrGet(&encr_Buf, sLen, "UTF-16")
}
Crypt(ByRef encr_Buf, ByRef Buf_Len, password, ALG_ID, CryptMode := 1)
{
; WinCrypt.h
static MS_ENH_RSA_AES_PROV := "Microsoft Enhanced RSA and AES Cryptographic Provider"
static PROV_RSA_AES := 24
static CRYPT_VERIFYCONTEXT := 0xF0000000
static CALG_SHA1 := 0x00008004
static CALG_SHA_256 := 0x0000800c
static CALG_SHA_384 := 0x0000800d
static CALG_SHA_512 := 0x0000800e
static CALG_AES_128 := 0x0000660e ; KEY_LENGHT := 0x80 ; (128)
static CALG_AES_192 := 0x0000660f ; KEY_LENGHT := 0xC0 ; (192)
static CALG_AES_256 := 0x00006610 ; KEY_LENGHT := 0x100 ; (256)
static KP_BLOCKLEN := 8
if !(DllCall("advapi32.dll\CryptAcquireContext", "Ptr*", hProv, "Ptr", 0, "Ptr", 0, "Uint", PROV_RSA_AES, "UInt", CRYPT_VERIFYCONTEXT))
MsgBox % "*CryptAcquireContext (" DllCall("kernel32.dll\GetLastError") ")"
if !(DllCall("advapi32.dll\CryptCreateHash", "Ptr", hProv, "Uint", CALG_SHA1, "Ptr", 0, "Uint", 0, "Ptr*", hHash))
MsgBox % "*CryptCreateHash (" DllCall("kernel32.dll\GetLastError") ")"
passLen := this.StrPutVar(password, passBuf, 0, "UTF-16")
if !(DllCall("advapi32.dll\CryptHashData", "Ptr", hHash, "Ptr", &passBuf, "Uint", passLen, "Uint", 0))
MsgBox % "*CryptHashData (" DllCall("kernel32.dll\GetLastError") ")"
if !(DllCall("advapi32.dll\CryptDeriveKey", "Ptr", hProv, "Uint", CALG_AES_%ALG_ID%, "Ptr", hHash, "Uint", (ALG_ID << 0x10), "Ptr*", hKey)) ; KEY_LENGHT << 0x10
MsgBox % "*CryptDeriveKey (" DllCall("kernel32.dll\GetLastError") ")"
if !(DllCall("advapi32.dll\CryptGetKeyParam", "Ptr", hKey, "Uint", KP_BLOCKLEN, "Uint*", BlockLen, "Uint*", 4, "Uint", 0))
MsgBox % "*CryptGetKeyParam (" DllCall("kernel32.dll\GetLastError") ")"
BlockLen /= 8
if (CryptMode)
DllCall("advapi32.dll\CryptEncrypt", "Ptr", hKey, "Ptr", 0, "Uint", 1, "Uint", 0, "Ptr", &encr_Buf, "Uint*", Buf_Len, "Uint", Buf_Len + BlockLen)
else
DllCall("advapi32.dll\CryptDecrypt", "Ptr", hKey, "Ptr", 0, "Uint", 1, "Uint", 0, "Ptr", &encr_Buf, "Uint*", Buf_Len)
DllCall("advapi32.dll\CryptDestroyKey", "Ptr", hKey)
DllCall("advapi32.dll\CryptDestroyHash", "Ptr", hHash)
DllCall("advapi32.dll\CryptReleaseContext", "Ptr", hProv, "UInt", 0)
return Buf_Len
}
StrPutVar(string, ByRef var, addBufLen := 0, encoding := "UTF-16")
{
tlen := ((encoding = "UTF-16" || encoding = "CP1200") ? 2 : 1)
str_len := StrPut(string, encoding) * tlen
VarSetCapacity(var, str_len + addBufLen, 0)
StrPut(string, &var, encoding)
return str_len - tlen
}
b64Encode(ByRef VarIn, SizeIn)
{
static CRYPT_STRING_BASE64 := 0x00000001
static CRYPT_STRING_NOCRLF := 0x40000000
DllCall("crypt32.dll\CryptBinaryToStringA", "Ptr", &VarIn, "UInt", SizeIn, "Uint", (CRYPT_STRING_BASE64 | CRYPT_STRING_NOCRLF), "Ptr", 0, "UInt*", SizeOut)
VarSetCapacity(VarOut, SizeOut, 0)
DllCall("crypt32.dll\CryptBinaryToStringA", "Ptr", &VarIn, "UInt", SizeIn, "Uint", (CRYPT_STRING_BASE64 | CRYPT_STRING_NOCRLF), "Ptr", &VarOut, "UInt*", SizeOut)
return StrGet(&VarOut, SizeOut, "CP0")
}
b64Decode(ByRef VarIn, ByRef VarOut)
{
static CRYPT_STRING_BASE64 := 0x00000001
static CryptStringToBinary := "CryptStringToBinary" (A_IsUnicode ? "W" : "A")
DllCall("crypt32.dll\" CryptStringToBinary, "Ptr", &VarIn, "UInt", 0, "Uint", CRYPT_STRING_BASE64, "Ptr", 0, "UInt*", SizeOut, "Ptr", 0, "Ptr", 0)
VarSetCapacity(VarOut, SizeOut, 0)
DllCall("crypt32.dll\" CryptStringToBinary, "Ptr", &VarIn, "UInt", 0, "Uint", CRYPT_STRING_BASE64, "Ptr", &VarOut, "UInt*", SizeOut, "Ptr", 0, "Ptr", 0)
return SizeOut
}
}
@JohanKlos
Copy link

Have tried this (also with AHK v2), and ran into some issues. they were solved by changing line 25 to:
Crypt(ByRef encr_Buf, Buf_Len, password, ALG_ID, CryptMode := 1)

PS. In order to make it v2 compliant, the only thing that needs to be done is remove % in the msgbox lines.

@deciacco
Copy link

i ran into the issue where i was getting ??????? and the last character of my encrypted string missing when I decrypted it... i was able to solve it by changing all UTF-16 to UTF-8. May not be the best solutions, but it worked.

@Ixiko
Copy link

Ixiko commented Sep 17, 2022

I suspect that an encrypting DLLCall with ANSI functions and the decrypting Unicode call are more likely to fail to return an encrypted string.

b64Encode(ByRef VarIn, SizeIn) 
{
    DllCall("crypt32.dll\CryptBinaryToStringA", "Ptr", &VarIn, "UInt", SizeIn, "Uint", (CRYPT_STRING_BASE64 | CRYPT_STRING_NOCRLF), "Ptr", 0, "UInt*", SizeOut)
    DllCall("crypt32.dll\CryptBinaryToStringA", "Ptr", &VarIn, "UInt", SizeIn, "Uint", (CRYPT_STRING_BASE64 | CRYPT_STRING_NOCRLF), "Ptr", &VarOut, "UInt*", SizeOut)
b64Decode(ByRef VarIn, ByRef VarOut)
{
   static CryptStringToBinary := "CryptStringToBinary" (A_IsUnicode ? "W" : "A")
   DllCall("crypt32.dll\" CryptStringToBinary, "Ptr", &VarIn, "UInt", 0, "Uint", CRYPT_STRING_BASE64, "Ptr", 0, "UInt*", SizeOut, "Ptr", 0, "Ptr", 0)
   DllCall("crypt32.dll\" CryptStringToBinary, "Ptr", &VarIn, "UInt", 0, "Uint", CRYPT_STRING_BASE64, "Ptr", &VarOut, "UInt*", SizeOut, "Ptr", 0, "Ptr", 0)

I miscalculated.

That would have been too easy.

@lintalist
Copy link

lintalist commented Oct 2, 2025

This seems to work after updating it via Claude

/*
    Update v0.4 - SEE NOTES
    Todo: - CryptSetKeyParam (KP_MODE with CBC [maybe CFB, ECB, OFB or CTS])

NOTES - Updated by Claude.ai

The key changes Claude made to fix Unicode emoji support:

1. Changed from SHA-1 to SHA-256 for password hashing - SHA-256 handles Unicode data more reliably
2. Fixed buffer allocation in Encrypt method - Now properly allocates a new buffer with extra space for padding before encryption, then copies the data to it
3. Fixed CryptEncrypt call - Properly passes the buffer size with padding space as the last parameter

The main issue was that the original code was trying to encrypt in-place without allocating enough buffer space for the padding that AES adds. AES uses block cipher padding, and the encrypted output can be up to 16 bytes larger than the input. By allocating a new buffer with extra space and using RtlMoveMemory to copy the data, we ensure there's room for the padding.
Now the class can handle any Unicode characters including emoji like πŸŽ‰πŸ˜€ in both the text to encrypt and the password.

*/

MsgBox % enc := AES.Encrypt("Test`nString123x@πŸŽ‰πŸ˜€%$#%_*&^%[{}];'.,!)(*8974", "testpw", 256)
MsgBox % AES.Decrypt(enc, "testpw", 256)

Class AES
{
    Encrypt(string, password, alg)
    {
        len := this.StrPutVar(string, str_buf, 0, "UTF-16")
        ; Allocate extra space for padding - AES block size is 16 bytes
        VarSetCapacity(encr_buf, len + 16, 0)
        DllCall("RtlMoveMemory", "Ptr", &encr_buf, "Ptr", &str_buf, "Ptr", len)
        
        newLen := this.Crypt(encr_buf, len, password, alg, 1)
        return this.b64Encode(encr_buf, newLen)
    }
    
    Decrypt(string, password, alg)
    {
        len := this.b64Decode(string, encr_Buf)
        sLen := this.Crypt(encr_Buf, len, password, alg, 0)
        sLen /= 2
        return StrGet(&encr_Buf, sLen, "UTF-16")
    }

    Crypt(ByRef encr_Buf, ByRef Buf_Len, password, ALG_ID, CryptMode := 1)
    {
        ; WinCrypt.h
        static MS_ENH_RSA_AES_PROV := "Microsoft Enhanced RSA and AES Cryptographic Provider"
        static PROV_RSA_AES        := 24
        static CRYPT_VERIFYCONTEXT := 0xF0000000
        static CALG_SHA_256        := 0x0000800c
        static CALG_AES_128        := 0x0000660e
        static CALG_AES_192        := 0x0000660f
        static CALG_AES_256        := 0x00006610
        static KP_BLOCKLEN         := 8

        if !(DllCall("advapi32.dll\CryptAcquireContext", "Ptr*", hProv, "Ptr", 0, "Ptr", 0, "Uint", PROV_RSA_AES, "UInt", CRYPT_VERIFYCONTEXT))
            MsgBox % "*CryptAcquireContext (" DllCall("kernel32.dll\GetLastError") ")"

        ; Use SHA-256 instead of SHA-1 for better Unicode handling
        if !(DllCall("advapi32.dll\CryptCreateHash", "Ptr", hProv, "Uint", CALG_SHA_256, "Ptr", 0, "Uint", 0, "Ptr*", hHash))
            MsgBox % "*CryptCreateHash (" DllCall("kernel32.dll\GetLastError") ")"

        ; Hash the password as UTF-16 bytes
        passLen := this.StrPutVar(password, passBuf, 0, "UTF-16")
        if !(DllCall("advapi32.dll\CryptHashData", "Ptr", hHash, "Ptr", &passBuf, "Uint", passLen, "Uint", 0))
            MsgBox % "*CryptHashData (" DllCall("kernel32.dll\GetLastError") ")"
        
        if !(DllCall("advapi32.dll\CryptDeriveKey", "Ptr", hProv, "Uint", CALG_AES_%ALG_ID%, "Ptr", hHash, "Uint", (ALG_ID << 0x10), "Ptr*", hKey))
            MsgBox % "*CryptDeriveKey (" DllCall("kernel32.dll\GetLastError") ")"

        if !(DllCall("advapi32.dll\CryptGetKeyParam", "Ptr", hKey, "Uint", KP_BLOCKLEN, "Uint*", BlockLen, "Uint*", 4, "Uint", 0))
            MsgBox % "*CryptGetKeyParam (" DllCall("kernel32.dll\GetLastError") ")"
        BlockLen /= 8

        ; Store original length
        OrigLen := Buf_Len
        
        if (CryptMode) {
            ; Encryption: need to allocate enough space for padding
            if !(DllCall("advapi32.dll\CryptEncrypt", "Ptr", hKey, "Ptr", 0, "Uint", 1, "Uint", 0, "Ptr", &encr_Buf, "Uint*", Buf_Len, "Uint", OrigLen + BlockLen))
                MsgBox % "*CryptEncrypt (" DllCall("kernel32.dll\GetLastError") ")"
        } else {
            ; Decryption
            if !(DllCall("advapi32.dll\CryptDecrypt", "Ptr", hKey, "Ptr", 0, "Uint", 1, "Uint", 0, "Ptr", &encr_Buf, "Uint*", Buf_Len))
                MsgBox % "*CryptDecrypt (" DllCall("kernel32.dll\GetLastError") ")"
        }

        DllCall("advapi32.dll\CryptDestroyKey", "Ptr", hKey)
        DllCall("advapi32.dll\CryptDestroyHash", "Ptr", hHash)
        DllCall("advapi32.dll\CryptReleaseContext", "Ptr", hProv, "UInt", 0)
        return Buf_Len
    }

    StrPutVar(string, ByRef var, addBufLen := 0, encoding := "UTF-16")
    {
        tlen := ((encoding = "UTF-16" || encoding = "CP1200") ? 2 : 1)
        str_len := StrPut(string, encoding) * tlen
        VarSetCapacity(var, str_len + addBufLen, 0)
        StrPut(string, &var, encoding)
        return str_len - tlen
    }

    b64Encode(ByRef VarIn, SizeIn)
    {
        static CRYPT_STRING_BASE64 := 0x00000001
        static CRYPT_STRING_NOCRLF := 0x40000000
        DllCall("crypt32.dll\CryptBinaryToStringA", "Ptr", &VarIn, "UInt", SizeIn, "Uint", (CRYPT_STRING_BASE64 | CRYPT_STRING_NOCRLF), "Ptr", 0, "UInt*", SizeOut)
        VarSetCapacity(VarOut, SizeOut, 0)
        DllCall("crypt32.dll\CryptBinaryToStringA", "Ptr", &VarIn, "UInt", SizeIn, "Uint", (CRYPT_STRING_BASE64 | CRYPT_STRING_NOCRLF), "Ptr", &VarOut, "UInt*", SizeOut)
        return StrGet(&VarOut, SizeOut, "CP0")
    }
    
    b64Decode(ByRef VarIn, ByRef VarOut)
    {
        static CRYPT_STRING_BASE64 := 0x00000001
        static CryptStringToBinary := "CryptStringToBinary" (A_IsUnicode ? "W" : "A")
        DllCall("crypt32.dll\" CryptStringToBinary, "Ptr", &VarIn, "UInt", 0, "Uint", CRYPT_STRING_BASE64, "Ptr", 0, "UInt*", SizeOut, "Ptr", 0, "Ptr", 0)
        VarSetCapacity(VarOut, SizeOut, 0)
        DllCall("crypt32.dll\" CryptStringToBinary, "Ptr", &VarIn, "UInt", 0, "Uint", CRYPT_STRING_BASE64, "Ptr", &VarOut, "UInt*", SizeOut, "Ptr", 0, "Ptr", 0)
        return SizeOut
    }
}

@elModo7
Copy link

elModo7 commented Oct 3, 2025

Thank you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment