UWSCでWin32 Crypto APIを使う
Crypto APIをラップするモジュールを書いてみた。
Crypto APIはVISTA以降Crypto Next Generationに更新されていますが、とりあえず旧版で。
スクリプト
Crypto.uws
OPTION EXPLICIT IFB GET_UWSC_NAME = "Crypto.uws" THEN DIM pass = "password", text = "original text 日本語", i, res DIM iv[] = $EB,$F5,$F2,$08,$35,$9C,$F1,$D9,$1B,$9D,$62,$86,$CF,$C6,$34,$61 DIM encdata = "oWsLLxr8ZxdFVZqWzwts5YrZGujDxPkE04q4n3MPqMM=" Crypto.GenKey(pass) DIM blockSize = Crypto.GetBlockSize() //DIM iv = Crypto.GenRandom(blockSize / 8) //PRINT Crypto.BinToString(iv, -1, 1) Crypto.SetKeyParam(1, iv) // KP_IV IFB MSGBOX("Test Encrypt?", BTN_YES OR BTN_NO) = BTN_YES THEN res = TRIM(Crypto.EncryptStrToString(text)) i = (res = encdata) ELSE res = TRIM(Crypto.DecryptStrToString(encdata)) i = (res = text) ENDIF IF i THEN PRINT "OK: " + res ELSE PRINT "NG: " + res ENDIF Crypto.DestroyKey() Crypto.ReleaseProvider() // 最後に引数なしのReleaseProviderを呼ぶこと ENDIF MODULE Crypto DEF_DLL GetLastError(): DWORD: kernel32 DEF_DLL CryptGenRandom(DWORD,DWORD,BYTE[]): bool: advapi32 DEF_DLL CryptBinaryToStringA(BYTE[],DWORD,DWORD,var string,var DWORD): bool: crypt32 DEF_DLL CryptStringToBinaryA(string,DWORD,DWORD,var BYTE[],var DWORD,DWORD,var DWORD): bool: crypt32 DEF_DLL CryptEncrypt(DWORD,DWORD,bool,DWORD,var BYTE[],var DWORD,DWORD): bool: advapi32 DEF_DLL CryptDecrypt(DWORD,DWORD,bool,DWORD,var BYTE[],var DWORD): bool: advapi32 DIM _hProv = 0, _hKey = 0 PROCEDURE Crypto _hProv = GetProvider() FEND FUNCTION GetProvider(provTypeGet=24) // プロバイダーの取得(初期値は RSA Full and AES) RESULT = 0 DEF_DLL CryptEnumProvidersW(DWORD,DWORD,DWORD,var DWORD,var wstring,var DWORD): bool: advapi32 DEF_DLL CryptAcquireContextW(var DWORD,DWORD,wstring,DWORD,DWORD): bool: advapi32 DIM i = 0, provType, provName, provNameLen, loop = TRUE WHILE loop loop = CryptEnumProvidersW(i, 0, 0, provType, NULL, provNameLen) IFB loop THEN provName = FORMAT(CHR(0), provNameLen) loop = CryptEnumProvidersW(i, 0, 0, provType, provName, provNameLen) IFB loop AND provType = provTypeGet THEN loop = !CryptAcquireContextW(RESULT, 0, provName, provType, $F0000000) IFB loop THEN RESULT = 0 Log("CryptAcquireContext Error " + GetLastError()) ENDIF ENDIF ENDIF i = i + 1 WEND FEND FUNCTION GenRandom(len, hProv=0) RESULT = SPLIT(EMPTY) IF hProv = 0 THEN hProv = _hProv IFB len > 0 THEN DIM buf[len-1] IFB CryptGenRandom(hProv, len, buf) THEN RESULT = SetResult(buf) ELSE RESULT = GetLastError() Log("CryptGenRandom Error " + RESULT) IF RESULT = 0 THEN RESULT = -1 ENDIF ENDIF FEND FUNCTION BinToString(data[], len=-1, flags=11) // CRYPT_STRING_HEXASCIIADDR IF len < 0 THEN len = LENGTH(data) DIM l = 0 IFB CryptBinaryToStringA(data, len, flags, NULL, l) THEN RESULT = FORMAT(CHR(0), l) IFB !CryptBinaryToStringA(data, len, flags, RESULT, l) THEN RESULT = GetLastError() Log("CryptBinaryToString Error " + RESULT) IF RESULT = 0 THEN RESULT = -1 ENDIF ELSE RESULT = GetLastError() Log("CryptBinaryToString calc Error " + RESULT) IF RESULT = 0 THEN RESULT = -1 ENDIF FEND FUNCTION StrToBinary(data, flags=8) DIM size = 0 IFB CryptStringToBinaryA(data, 0, flags, NULL, size, 0, flags) AND size > 0 THEN DIM res[size-1] IFB CryptStringToBinaryA(data, 0, flags, res, size, 0, flags) THEN RESULT = SetResult(res) ELSE RESULT = GetLastError() Log("CryptStringToBinary Error " + RESULT) IF RESULT = 0 THEN RESULT = -1 ENDIF ELSE RESULT = GetLastError() Log("CryptStringToBinary calc Error " + RESULT) IF RESULT = 0 THEN RESULT = -1 ENDIF FEND FUNCTION GenKey(pass, hashAlg=$8004, keyAlg=$6610, keyLen=256, hProv=0) // 鍵の取得(初期値は、CALG_SHA,CALG_AES_256) RESULT = 0 IF hProv = 0 THEN hProv = _hProv // ハッシュの取得 DIM hHash = 0 DEF_DLL CryptCreateHash(DWORD,DWORD,DWORD,DWORD,var DWORD): bool: advapi32 IFB !CryptCreateHash(hProv, hashAlg, 0, 0, hHash) THEN Log("CryptCreateHash Error " + GetLastError()) ENDIF // ハッシュの計算 DEF_DLL CryptHashData(DWORD,string,DWORD,DWORD): bool: advapi32 IFB !CryptHashData(hHash, pass, LENGTHB(pass), 0) THEN Log("CryptHashData Error " + GetLastError()) ENDIF // 鍵の生成 DEF_DLL CryptDeriveKey(DWORD,DWORD,DWORD,DWORD,var DWORD): bool: advapi32 IFB !CryptDeriveKey(hProv, keyAlg, hHash, keyLen*$10000, RESULT) THEN Log("CryptDeriveKey Error " + GetLastError()) ENDIF // ハッシュの破棄 DEF_DLL CryptDestroyHash(DWORD): bool: advapi32 IFB hHash <> 0 AND !CryptDestroyHash(hHash) THEN Log("CryptDestroyHash Error " + GetLastError()) ENDIF // パラメーターの設定 SetKeyParamDword(3, 1, RESULT) // KP_PADDING,PKCS5_PADDING SetKeyParamDword(4, 4, RESULT) // KP_MODE,CRYPT_MODE_CFB IFB RESULT THEN IF _hKey THEN DestroyKey(_hKey) _hKey = RESULT ENDIF FEND FUNCTION SetKeyParam(param,data[],hKey=0) RESULT = 0 IF hKey = 0 THEN hKey = _hKey DEF_DLL CryptSetKeyParam(DWORD,DWORD,BYTE[],DWORD): bool: advapi32 IFB !CryptSetKeyParam(hKey, param, data, 0) THEN RESULT = GetLastError() Log("CryptSetKeyParam Error " + RESULT) IF RESULT = 0 THEN RESULT = -1 ENDIF FEND FUNCTION SetKeyParamDword(param,data,hKey=0) RESULT = 0 IF hKey = 0 THEN hKey = _hKey DEF_DLL CryptSetKeyParam(DWORD,DWORD,var DWORD,DWORD): bool: advapi32 IFB !CryptSetKeyParam(hKey, param, data, 0) THEN RESULT = GetLastError() Log("CryptSetKeyParam DWORD Error " + RESULT) IF RESULT = 0 THEN RESULT = -1 ENDIF FEND FUNCTION GetBlockSize(hKey=0) // ブロックサイズ取得 RESULT = 0 IF hKey = 0 THEN hKey = _hKey DIM i = 4 DEF_DLL CryptGetKeyParam(DWORD,DWORD,var DWORD,var DWORD,DWORD): bool: advapi32 IFB !CryptGetKeyParam(hKey, 8, RESULT, i, 0) THEN // KP_BLOCKLEN Log("CryptGetKeyParam Error " + GetLastError()) ENDIF FEND FUNCTION Encrypt(var data[], var len, final=TRUE, hKey=0) RESULT = 0 IF hKey = 0 THEN hKey = _hKey DIM bufLen = LENGTH(data) IFB !CryptEncrypt(hKey, 0, final, 0, data, len, bufLen) THEN RESULT = GetLastError() Log("CryptEncrypt Error " + RESULT) IF RESULT = 0 THEN RESULT = -1 ENDIF FEND FUNCTION EncryptToString(data[], len=-1, final=TRUE, flags=1, hKey=0) RESULT = EMPTY IF hKey = 0 THEN hKey = _hKey IF len < 0 THEN len = LENGTH(data) // 必要なサイズを用意する。共通鍵暗号化はブロックサイズに切り上げで良いはず DIM blockSize = GetBlockSize(hKey) / 8, m = len MOD blockSize blockSize = len IF m > 0 THEN blockSize = len + (blockSize - m) DIM res[blockSize] DEF_DLL RtlMoveMemory(var BYTE[], BYTE[], DWORD): kernel32 RtlMoveMemory(res, data, len) IFB Encrypt(res, len, final, hKey) = 0 THEN RESULT = BinToString(res, len, flags) ENDIF FEND FUNCTION EncryptStrToString(text, final=TRUE, flags=1, hKey=0) IF hKey = 0 THEN hKey = _hKey DIM m = LENGTHB(text), i, data[m] DEF_DLL RtlMoveMemory(var BYTE[], string, DWORD): kernel32 RtlMoveMemory(data, text, m) RESULT = EncryptToString(data, m, final, flags, hKey) FEND FUNCTION Decrypt(var data[], var len, final=TRUE, hKey=0) RESULT = 0 IF hKey = 0 THEN hKey = _hKey IFB !CryptDecrypt(hKey, 0, final, 0, data, len) THEN RESULT = GetLastError() Log("CryptDecrypt Error " + RESULT) IF RESULT = 0 THEN RESULT = -1 ENDIF FEND FUNCTION DecryptToString(data[], len=-1, final=TRUE, hKey=0) RESULT = EMPTY IF hKey = 0 THEN hKey = _hKey IF len < 0 THEN len = LENGTH(data) IFB Decrypt(data, len, final, hKey) = 0 THEN RESULT = FORMAT(CHR(0), len) DEF_DLL RtlMoveMemory(var string, BYTE[], DWORD): kernel32 RtlMoveMemory(RESULT, data, len) ENDIF FEND FUNCTION DecryptStrToString(encText, final=TRUE, flags=1, hKey=0) RESULT = EMPTY IF hKey = 0 THEN hKey = _hKey DIM data = StrToBinary(encText, flags) IFB VARTYPE(data) >= VAR_ARRAY THEN RESULT = DecryptToString(data, LENGTH(data), final, hKey) ENDIF FEND FUNCTION DestroyKey(hKey=0) // 鍵の破棄 もしかして不要? RESULT = 0 IF hKey = 0 THEN hKey = _hKey DEF_DLL CryptDestroyKey(DWORD): bool: advapi32 IFB hKey <> 0 AND !CryptDestroyKey(hKey) THEN RESULT = GetLastError() Log("CryptDestroyKey Error " + RESULT) IF RESULT = 0 THEN RESULT = -1 ENDIF IF hKey = _hKey THEN _hKey = 0 FEND FUNCTION ReleaseProvider(hProv=0) // プロバイダーの解放 RESULT = 0 IF hProv = 0 THEN hProv = _hProv DEF_DLL CryptReleaseContext(DWORD,DWORD): bool: advapi32 IFB hProv <> 0 AND !CryptReleaseContext(hProv, 0) THEN RESULT = GetLastError() Log("CryptReleaseContext Error " + RESULT) IF RESULT = 0 THEN RESULT = -1 ENDIF IF hProv = _hProv THEN _hProv = 0 FEND PROCEDURE Log(msg) PRINT msg FEND FUNCTION SetResult(data[], len=-1, offset=0, i=0) IF len < 0 THEN len = LENGTH(data) len = len - 1 RESULT = SPLIT(EMPTY) IFB len >= 0 THEN RESULT = SPLIT(" ") RESIZE(RESULT, len) FOR i = 0 TO len RESULT[i] = data[i + offset] NEXT ENDIF FEND ENDMODULE
GenRandom関数は、数学的により安全な乱数が生成できます。
基本的には、例にあるように、
パスワードでGenKeyして、SetKeyParamで適当に初期化ベクター(IV)を設定して、暗号化(Encrypt)か複合化(Decrypt)して、DestroyKey、ReleaseProviderする。
初期状態でのGenKeyは、AES暗号化なので、充分な強度があります。
AES暗号化では、暗号キー(パスワード)と初期化ベクターが重要なので、その二つがキーと思うと良いでしょう。
サンプルには初期化ベクターをRandomで生成している例がコメントとして書かれていますが、それを使うと暗号化時と複合化時で初期化ベクターが異なるため、複合化できなくなります。
雑感
つい最近、SafeArray関数に気づきました。
SPLIT(" ")のトリックはいらないですね、、、。