Delphi实现OpenSSL签名转Base64并跨应用公钥解密求助
解决Delphi Chilkat与OpenSSL签名结果不兼容的问题
咱们先梳理清楚你要模拟的OpenSSL命令的核心行为:
openssl dgst -sha1 -sign:读取输入文本的原始字节计算SHA-1哈希,然后用RSA私钥以PKCS#1 v1.5填充生成二进制签名(这是OpenSSL的默认填充规则)openssl enc -base64 -A:把二进制签名编码为Base64,并且强制输出为单行(无换行)
你的Chilkat代码有几个关键细节没和OpenSSL对齐,导致其他应用用公钥无法解密/验证,下面逐一修正:
1. 字节序设置错误
OpenSSL的RSA签名默认使用大端字节序,但你代码里设置了CkRsa_putLittleEndian(rsa,True);,这直接导致签名的字节顺序和OpenSSL完全相反,必须改成:
CkRsa_putLittleEndian(rsa, False);
2. 字符串编码不匹配
OpenSSL处理Imput.txt时是读取文件的原始字节(比如如果是UTF-8文本就用UTF-8字节,Windows系统默认编码就用对应字节),但你直接传递Addr(s[1])——Delphi默认的string是UTF-16编码的Unicode字符串,这样Chilkat处理的是UTF-16字节,和OpenSSL处理的原始文本字节完全不同,哈希结果自然不一致。
你需要把字符串转换成和输入文件一致的字节编码,比如用UTF-8的话:
var utf8Bytes: TBytes; begin // ... 其他代码 ... utf8Bytes := TEncoding.UTF8.GetBytes(s); b64Sig := CkRsa__signBytesENC(rsa, @utf8Bytes[0], Length(utf8Bytes), 'sha1'); // ... 其他代码 ...
如果你的Imput.txt是系统默认编码,就换成TEncoding.Default.GetBytes(s)。
3. Base64输出格式不对
OpenSSL的-A参数会生成无换行的单行Base64,但Chilkat默认会每76个字符换行,需要关闭这个设置:
CkRsa_putBd64LineLength(rsa, 0); // 0表示不添加换行符
4. 显式指定填充方式(避免默认值变更)
虽然Chilkat默认用的是PKCS#1 v1.5填充(和OpenSSL一致),但显式设置更稳妥,防止后续组件版本变更:
CkRsa_putPssPadding(rsa, False); // 关闭PSS填充,使用PKCS#1 v1.5 CkRsa_putSignatureHashAlgorithm(rsa, 'sha1');
修正后的完整代码
function Tf_invsign.EncSHAB64(s: string): String; var pkey: HCkPrivateKey; success: Boolean; rsa: HCkRsa; b64Sig: PWideChar; utf8Bytes: TBytes; begin addlog(s); pkey := CkPrivateKey_Create(); try success := CkPrivateKey_LoadEncryptedPemFile(pkey, 'private.PEM', ''); if not success then begin addlog(CkPrivateKey__lastErrorText(pkey)); Exit; end; rsa := CkRsa_Create(); try success := CkRsa_ImportPrivateKeyObj(rsa, pkey); if not success then begin addlog(CkRsa__lastErrorText(rsa)); Exit; end; // 对齐OpenSSL的RSA配置 CkRsa_putLittleEndian(rsa, False); // 大端字节序,匹配OpenSSL CkRsa_putEncodingMode(rsa, 'base64'); CkRsa_putBd64LineLength(rsa, 0); // 单行Base64输出 CkRsa_putPssPadding(rsa, False); // 使用PKCS#1 v1.5填充 CkRsa_putSignatureHashAlgorithm(rsa, 'sha1'); // 将字符串转换为UTF-8字节(和OpenSSL处理文本的方式一致) utf8Bytes := TEncoding.UTF8.GetBytes(s); b64Sig := CkRsa__signBytesENC(rsa, @utf8Bytes[0], Length(utf8Bytes), 'sha1'); if b64Sig = nil then begin addlog(CkRsa__lastErrorText(rsa)); Exit; end; Result := b64Sig; finally CkRsa_Dispose(rsa); end; finally CkPrivateKey_Dispose(pkey); end; end;
验证方法
- 对比你的代码生成的Base64字符串和OpenSSL命令生成的
OutB64.b64内容,应该完全一致 - 用OpenSSL验证你的签名:把代码生成的Base64解码成二进制文件,执行
openssl dgst -sha1 -verify PublicKey.pem -signature your_signature.bin input.txt,应该返回Verified OK - 也可以用Chilkat的
VerifyStringENC方法验证,确保公钥能正常验证签名
内容的提问来源于stack exchange,提问作者Yuri Adam
相关产品推荐
相关产品推荐

