做微信的支付接口,现在必须要使用到AEAD_AES_256_GCM加解密。在微信的文档中提供了其他开发语言的示例代码,但因为某些大家都知道的原因,没有delphi的示例。
而在网上去找一圈,你可能会更加的蒙圈,一是几乎只有其他语言的文章,二是delphi有提到过的要么太复杂要么没法使用。
今天这里就来详细讲一讲Delphi(我使用的版本的D10.3)处理微信AEAD_AES_256_GCM解密,我们实际需要的代码并不多,很多主要的代码delphi已给我们准备好了。下面就是最终代码:
function Decrypt_AEAD_AES_256_GCM(const AData, AKey, AIV, AAD: TBytes; out ADeCryptData: TBytes): Boolean;
var
Ctx: PEVP_CIPHER_CTX;
vLen: Integer;
vDataLen: Integer;
vTAG: TBytes;
begin
Result := False;
vLen := 0;
vDataLen := 0;
SetLength(ADeCryptData, Length(AData));
//取TAG ,这个是一个解密后的验证数据
Move(AData[Length(AData)-16],vTag[0],16);
SetLength(vTAG,16);
//Load;//初始化SSL <--源代码中带的,我给注销了
Ctx := EVP_CIPHER_CTX_new(); //初始化Ctx 后面必须使用到的
try
EVP_DecryptInit_ex(Ctx, EVP_aes_256_gcm, nil, nil, nil);//初始化解密数据
EVP_CIPHER_CTX_ctrl(Ctx, EVP_CTRL_GCM_SET_IVLEN, Length(AIV), nil);//设置IV长度
EVP_DecryptInit_ex(Ctx, nil, nil, @AKey[0], @AIV[0]);//设置解决用的KEY及IV
if EVP_CIPHER_CTX_set_padding(Ctx,0) <> 1 then//这个其实可以不用的,这是配置非填充模式
Exit(False);
if EVP_DecryptUpdate(Ctx, nil, @vLen, @AAD[0], Length(AAD)) <> 1 then
Exit(False);
//下面这条,其实就是在进行解密了
if EVP_DecryptUpdate(Ctx, @ADeCryptData[0], @vDataLen, @AData[0], Length(AData)-16) <> 1 then
Exit(False);
//设置 tag
if EVP_CIPHER_CTX_ctrl(Ctx, EVP_CTRL_GCM_SET_TAG, 16, @vTag[0]) <> 1 then
Exit(False);
//最后进行解密验证
if EVP_DecryptFinal_ex(Ctx, @ADeCryptData[vDataLen], @vLen) <> 1 then
Exit(False);
SetLength(ADeCryptData, vDataLen);//返回解决的数据
Result := True;
finally
EVP_CIPHER_CTX_free(Ctx);
end;
end;
这段代码需要引用系统提供的 IdSSLOpenSSL,IdSSLOpenSSLHeaders,<–我直接引用并不能运行,下面有说明
由这文件的引用,大家可能已知道,这其实就是使用了Id的SSL库,其根本也就是OpenSSL,所以运行的时候需要有对应的两个动态库文件(libeay32.dll、ssleay32.dll),在Delphi安装目录里可以找到的哈,没必要网上去搜。
现在来说一下这个函数的使用:
函数的调用很简单:Decrypt_AEAD_AES_256_GCM(vData,vKey,vIV,vAD,vDeCryptData),执行完成后返回的是一个Boolean,为True时说明解密正确,为False时为解密错误(失败),另外还有一个返回的变量vDeCryptData,这里是解决后的明文,这个变量通常都会有一个返回数据,但需要根据函数返回来确定是否是正确的明文。请一定注意,即便你是随意设置的解密参数,vDeCryptData都可能会返回数据的。
具体调用代码:
procedure TForm8.Button4Click(Sender: TObject);
var
vDeCryptData: TBytes;
vData, vKey, vIV, vAD, vTag: TBytes;
vv:string;
reb:Boolean;
begin
vv:= 'eELiRmT/laQs='; //这里是微信返回的密码 对应JSON中的ciphertext
vData:= TNetEncoding.Base64.DecodeStringToBytes(vv);//需要先解Base64
vKey:=TEncoding.Default.GetBytes('wwwl.............86v3');//这个是微信中设置的APIV3密钥 Key
vIV:=TEncoding.Default.GetBytes('8Ty0LWf6Opfm');//这里是微信返回的加密使用的随机串 对应JSON中nonce
vAD:=TEncoding.Default.GetBytes('transaction');//这里是微信返回的附加数据 对应JSON中的associated_data
reb:=vSHA256WithRSA.Decrypt_AEAD_AES_256_GCM(vData,vKey,vIV,vAD,vDeCryptData);
if reb then
Memo4.Lines.Text:= UTF8ToString(TEncoding.Default.GetString(vDeCryptData))
else
Memo4.Lines.Text:='解密失败!';
end;
下面这段是微信支付通知返回的JSON数据格式(摘自微信文档:https://pay.weixin.qq.com/wiki/doc/apiv3/wechatpay/wechatpay4_2.shtml):
{
"original_type": "transaction", // 加密前的对象类型
"algorithm": "AEAD_AES_256_GCM", // 加密算法
// Base64编码后的密文
"ciphertext": "...",
// 加密使用的随机串初始化向量)
"nonce": "...",
// 附加数据包(可能为空)
"associated_data": ""
}
简单说:
vData对应微信返回的待解密数据(密文) ,JSON中的ciphertext;
vKey对应微信你在微信中设置的APIv3密钥;
vIV对应微信返回的加密使用的随机串 ,JSON中的nonce;
vAD对应微信返回的附加数据 ,JSON中的associated_data;
在腾讯的文档中,没有明确的告诉你加密不使用填充模式,仅仅是在JAVA的演示代码中有这样一句:Cipher.getInstance(“AES/GCM/NoPadding”);,其中NoPadding就是不使用填充模式。
还有一点没有说明的是,JSON中返回的密文,不只是密文,而是密文加上TAG数据。
重要说明
- 1.以上代码可用,需要按需调整。
- 2.代码引用会有部分代码不能执行,引用不是引用 IdSSLOpenSSL,IdSSLOpenSSLHeaders, 可能是我的版本问题需要引用OpenSSL.Api_11.pas编译目录下需要libcrypto-1_1.dll
- 以上所需文件下载地址
转自:https://blog.csdn.net/tanqth/article/details/120043252
参考:http://bbs.2ccc.com/topic.asp?topicid=614470