You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Delphi 10.3中使用OpenSSL实现RSA-PSS签名失败排查

Delphi 10.3下OpenSSL RSA-PSS签名问题排查与修复

核心问题分析

你的代码存在5个关键错误,直接导致了全0签名和盐长度设置失败的问题:

  1. 签名缓冲区未初始化
    原代码中sig变量未预先分配内存,直接通过@sig[0]传入EVP_DigestSignFinal会访问无效内存,最终返回全0的签名结果。

  2. 盐长度函数误用
    EVP_PKEY_CTX_set_rsa_pss_keygen_saltlen是生成RSA密钥对时设置默认盐长度的函数,签名场景下必须使用EVP_PKEY_CTX_set_rsa_pss_saltlen,这是返回-2(操作不支持)的根本原因。

  3. 重复计算哈希
    Sign_RSAPSS中先调用CreateDigest计算SHA256摘要,又在InternalSign_RSAPSS里通过EVP_DigestSignInit指定SHA256算法,等于对原始数据做了两次哈希,违背了标准RSA-PSS签名流程(签名函数会自动处理哈希)。

  4. PEM密钥加载方式错误
    用TFile.ReadAllText读取密钥文件再转UTF8,会破坏PEM格式的二进制完整性(比如换行符、特殊字符的编码转换),必须直接读取原始字节。

  5. 上下文初始化冗余且错误
    手动创建的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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.07 19:55:54