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;
错误原因
- 直接操作内部结构体不安全:
EVP_PKEY的pkey字段属于内部实现细节,OpenSSL不推荐直接访问,不同版本的OpenSSL可能修改结构体布局,加上{$ALIGN OFF}的编译设置,会导致内存对齐不匹配,读取到错误的内存地址。 - 未使用官方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
相关产品推荐
相关产品推荐

