Delphi 10.3中使用OpenSSL实现RSA-PSS签名失败排查
Delphi 10.3下OpenSSL RSA-PSS签名问题排查与修复
核心问题分析
你的代码存在5个关键错误,直接导致了全0签名和盐长度设置失败的问题:
签名缓冲区未初始化
原代码中sig变量未预先分配内存,直接通过@sig[0]传入EVP_DigestSignFinal会访问无效内存,最终返回全0的签名结果。盐长度函数误用
EVP_PKEY_CTX_set_rsa_pss_keygen_saltlen是生成RSA密钥对时设置默认盐长度的函数,签名场景下必须使用EVP_PKEY_CTX_set_rsa_pss_saltlen,这是返回-2(操作不支持)的根本原因。重复计算哈希
Sign_RSAPSS中先调用CreateDigest计算SHA256摘要,又在InternalSign_RSAPSS里通过EVP_DigestSignInit指定SHA256算法,等于对原始数据做了两次哈希,违背了标准RSA-PSS签名流程(签名函数会自动处理哈希)。PEM密钥加载方式错误
用TFile.ReadAllText读取密钥文件再转UTF8,会破坏PEM格式的二进制完整性(比如换行符、特殊字符的编码转换),必须直接读取原始字节。上下文初始化冗余且错误
手动创建的pkeyCtx会被EVP_DigestSignInit覆盖,该函数会根据传入的密钥重新生成上下文,导致后续的填充、盐长度设置全部失效。
修正后的关键代码
1. 修复签名核心逻辑
class function TAuthenticationHelper.InternalSign_RSAPSS(const AInput: TBytes; AKey: EVP_PKEY): TBytes; var mdCtx: EVP_MD_CTX; pkeyCtx: PEVP_PKEY_CTX; sig: TBytes; iSiglen: NativeUInt; begin mdCtx := EVP_MD_CTX_new(); if not Assigned(mdCtx) then raise Exception.Create('mdCtx not assigned'); try // 让EVP_DigestSignInit自动创建上下文,无需手动提前初始化 if EVP_DigestSignInit(mdCtx, @pkeyCtx, EVP_sha256(), nil, AKey) <= 0 then raise Exception.Create('DigestSignInit failed'); // 设置RSA-PSS填充模式 if EVP_PKEY_CTX_set_rsa_padding(pkeyCtx^, RSA_PKCS1_PSS_PADDING) <= 0 then raise Exception.Create('Set RSA PSS padding failed'); // 设置盐长度为32(与SHA256摘要长度一致,符合标准) if EVP_PKEY_CTX_set_rsa_pss_saltlen(pkeyCtx^, 32) <= 0 then raise Exception.Create('Set PSS salt length failed'); // 预先分配签名缓冲区 iSigLen := EVP_PKEY_size(AKey); if iSigLen <= 0 then raise Exception.Create('Invalid key size'); SetLength(sig, iSigLen); // 直接传入原始数据,函数自动处理SHA256哈希 if EVP_DigestSignUpdate(mdCtx, @AInput[0], Length(AInput)) <= 0 then raise Exception.Create('DigestSignUpdate failed'); // 完成签名,实际长度会写入iSigLen if EVP_DigestSignFinal(mdCtx, @sig[0], @iSigLen) <= 0 then raise Exception.Create('DigestSignFinal failed'); // 调整缓冲区到实际签名长度 SetLength(sig, iSigLen); Result := sig; finally EVP_MD_CTX_free(mdCtx); end; end;
2. 修复签名入口函数(去掉重复哈希)
class function TAuthenticationHelper.Sign_RSAPSS(const AInput, AKey: TBytes; aPwd: string): TBytes; var lPkey: PEVP_PKEY; begin lPkey := LoadPrivateKey(AKey, PAnsiChar(AnsiString(aPwd)), nil); try // 直接传入原始数据,无需提前计算摘要 Result := InternalSign_RSAPSS(AInput, lPkey^); finally EVP_PKEY_free(lPkey); end; end;
3. 修复密钥文件加载逻辑
class function TAuthenticationHelper.Sign_RSAPSS(const aKeyFile, aStringToSign, aPwd: string): string; var bTxt: TBytes; bData: TBytes; bSign: TBytes; begin Result := ''; // 直接读取密钥文件原始字节,避免编码转换破坏PEM格式 bTxt := TFile.ReadAllBytes(aKeyFile); bData := TEncoding.UTF8.GetBytes(aStringToSign); bSign := Sign_RSAPSS(bData, bTxt, aPwd); Result := TNetEncoding.Base64.EncodeBytesToString(bSign); Result := StringReplace(Result, sLineBreak, '', [rfReplaceAll]); end;
内容的提问来源于stack exchange,提问作者Liezzzje
相关产品推荐
相关产品推荐

