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

Delphi 10.3中OpenSSL RSA_public_encrypt加密触发访问冲突问题排查

问题:Delphi 10.3中通过X509证书公钥加密时触发访问冲突错误

问题背景

已在Delphi 10.3中通过BIO加载X509证书到内存,成功调用X509_get_pubkey()获取到有效的PEVP_PKEY指针,但直接访问EVP_PKEY内部的pkey.rsa字段获取RSA指针时,调用RSA_size()触发访问冲突,调试发现该RSA结构体的多个字段为nil。

当前错误代码:

function TSomeObject.Encrypt(const pFrom: PAnsiChar; pPadding: Integer): TBytes;
begin
  SetLength(Result, RSA_size(ptrEVP_PKEY^.pkey.rsa));   // 此处触发EAV
  RSA_public_encrypt(Length(pFrom), pFrom, @Result[0], ptrEVP_PKEY^.pkey.rsa, pPadding)
end;  

错误原因

  1. 直接操作内部结构体不安全:EVP_PKEY的pkey字段属于内部实现细节,OpenSSL不推荐直接访问,不同版本的OpenSSL可能修改结构体布局,加上{$ALIGN OFF}的编译设置,会导致内存对齐不匹配,读取到错误的内存地址。
  2. 未使用官方API提取RSA密钥:OpenSSL提供了专门的API来从EVP_PKEY中提取RSA对象,直接读取结构体字段会绕过引用计数和正确性校验,导致获取到无效的RSA指针。

解决方法

使用OpenSSL官方提供的EVP_PKEY_get1_RSA()函数提取RSA指针,该函数会正确处理内部引用计数和结构体兼容性,使用完成后通过RSA_free()释放资源。

步骤1:确保函数声明

如果你的libeay32.pas中未声明以下函数,需要添加:

function EVP_PKEY_get1_RSA(pkey: PEVP_PKEY): PRSA; cdecl; external 'libeay32.dll';
procedure RSA_free(rsa: PRSA); cdecl; external 'libeay32.dll';

步骤2:修正加密函数

function TSomeObject.Encrypt(const pFrom: PAnsiChar; pPadding: Integer): TBytes;
var
  ptrRSA: PRSA;
  encryptSize: Integer;
  resultCode: Integer;
begin
  Result := [];
  // 从EVP_PKEY中安全提取RSA公钥
  ptrRSA := EVP_PKEY_get1_RSA(ptrEVP_PKEY);
  if Assigned(ptrRSA) then
  try
    // 获取加密所需缓冲区大小
    encryptSize := RSA_size(ptrRSA);
    SetLength(Result, encryptSize);
    // 执行公钥加密,注意用StrLen获取有效字节长度(pFrom是AnsiChar指针)
    resultCode := RSA_public_encrypt(StrLen(pFrom), PByte(pFrom), @Result[0], ptrRSA, pPadding);
    if resultCode = -1 then
    begin
      // 加密失败,清空结果
      SetLength(Result, 0);
      // 可选:调用ERR_get_error()获取具体错误信息
    end
    else
    begin
      // 实际加密结果长度可能小于缓冲区大小,调整结果长度
      SetLength(Result, resultCode);
    end;
  finally
    // 必须释放RSA指针,避免内存泄漏
    RSA_free(ptrRSA);
  end;
end;

补充说明

  • EVP_PKEY_get1_RSA()会增加RSA对象的引用计数,因此必须调用RSA_free()释放,否则会造成内存泄漏。
  • 避免直接访问OpenSSL结构体的内部字段,所有操作尽量使用官方提供的API,确保跨版本兼容性。
  • RSA_public_encrypt()的返回值为实际加密后的字节长度,返回-1表示加密失败,可以通过ERR_get_error()、ERR_error_string()等函数获取详细错误信息。

内容的提问来源于stack exchange,提问作者alvaroc

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.12 07:43:20