调用Indy中X509_new函数时触发访问违例问题求助
Delphi生成自签名X509证书时X509_new触发访问违例问题
问题详情
使用Delphi编写代码生成自签名X509证书,执行Cert := X509_new;语句时触发访问违例错误:
Access violation at address ...
完整代码
procedure TForm2.Label1DblClick(Sender: TObject); procedure GenerateSelfSignedCertificate(const AFileName: string); procedure GenerateRandomSerialNumber(SerialNumber: PASN1_INTEGER); var RandomValue: Int64; I: Integer; begin // 生成64位随机数 RandomValue := 0; for I := 1 to 8 do begin RandomValue := (RandomValue shl 8) or Random(256); end; // 确保数字为正数 RandomValue := RandomValue and $7FFFFFFFFFFFFFFF; // 设置序列号 if ASN1_INTEGER_set(SerialNumber, RandomValue) = 0 then raise Exception.Create('Error setting serial number'); end; function DateToASN1_TIME(const ADate: TDateTime): PASN1_TIME; var AStruct: TSystemTime; ASN1_Time: PASN1_TIME; begin DateTimeToSystemTime(ADate, AStruct); ASN1_Time := ASN1_TIME_set(nil, SystemTimeToDateTime(AStruct)); Result := ASN1_Time; end; var Cert: PX509; PrivateKey: PEVP_PKEY; SerialNumber: PASN1_INTEGER; Name: PX509_NAME; Bio: PBIO; NotBefore, NotAfter: TDateTime; begin // 初始化OpenSSL IdSSLOpenSSL.LoadOpenSSLLibrary; // 创建新证书 Cert := X509_new; // 错误触发点:Access Vioaltion at address 00000... try // 创建RSA私钥 PrivateKey := EVP_PKEY_new; try if EVP_PKEY_assign(PrivateKey, EVP_PKEY_RSA, PByte(RSA_generate_key(2048, RSA_F4, nil, nil))) = 0 then raise Exception.Create('Error generating private key'); // 设置证书版本(X509 v3) X509_set_version(Cert, 2); // 生成随机序列号 SerialNumber := ASN1_INTEGER_new; if SerialNumber = nil then raise Exception.Create('Error creating ASN1_INTEGER'); try GenerateRandomSerialNumber(SerialNumber); if X509_set_serialNumber(Cert, SerialNumber) = 0 then raise Exception.Create ('Error setting serial number for certificate'); finally ASN1_INTEGER_free(SerialNumber); end; // 设置证书有效期 NotBefore := Now - 1; // 从昨天开始生效 NotAfter := NotBefore + 365; // 有效期1年 X509_set_notBefore(Cert, DateToASN1_TIME(NotBefore)); X509_set_notAfter(Cert, DateToASN1_TIME(NotAfter)); // 设置公钥 X509_set_pubkey(Cert, PrivateKey); // 设置颁发者和主体名称(使用localhost) Name := X509_get_subject_name(Cert); X509_NAME_add_entry_by_txt(Name, 'CN', MBSTRING_ASC, PAnsiChar('localhost'), -1, -1, 0); X509_set_issuer_name(Cert, Name); // 签名证书 if X509_sign(Cert, PrivateKey, EVP_sha256) = 0 then raise Exception.Create('Error signing the certificate'); // 将证书和私钥保存到PEM文件 Bio := BIO_new_file(PAnsiChar(AnsiString(AFileName)), 'w+'); try PEM_write_bio_X509(Bio, Cert); PEM_write_bio_PrivateKey(Bio, PrivateKey, nil, nil, 0, nil, nil); finally BIO_free(Bio); end; finally EVP_PKEY_free(PrivateKey); end; finally X509_free(Cert); end; end; begin GenerateSelfSignedCertificate('.\CertificatTest.pem'); ShowMessage('Certificate successfully generated!'); end;
X509_new函数定义
该函数在Indy的IdSSLOpenSSLHeaders单元中定义为动态绑定函数:
{$EXTERNALSYM X509_new} X509_new : function: PX509 cdecl = nil;
问题原因及修复方案
核心原因
仅调用IdSSLOpenSSL.LoadOpenSSLLibrary仅完成了OpenSSL库文件的加载,但未完成动态函数指针的绑定,导致X509_new指向空地址,触发访问违例。
分步修复
补全OpenSSL初始化流程
在加载库文件后,必须调用LoadOpenSSLFunctions完成所有OpenSSL函数的绑定:// 初始化OpenSSL if not IdSSLOpenSSL.LoadOpenSSLLibrary then raise Exception.Create('无法加载OpenSSL库'); IdSSLOpenSSL.LoadOpenSSLFunctions;确保版本与位数兼容
- 确认OpenSSL库版本与Indy框架匹配(如Indy 10适配OpenSSL 1.1.x或3.x);
- 项目编译位数(32/64位)必须与OpenSSL库的位数完全一致。
优化代码兼容性
- 废弃函数替换:
RSA_generate_key在OpenSSL 1.1.0+已被废弃,改用RSA_generate_key_ex:var RSA: PRSA; Bignum: PBIGNUM; begin RSA := RSA_new; Bignum := BN_new; try BN_set_word(Bignum, RSA_F4); if not RSA_generate_key_ex(RSA, 2048, Bignum, nil) then raise Exception.Create('生成RSA密钥失败'); if EVP_PKEY_assign_RSA(PrivateKey, RSA) = 0 then begin RSA_free(RSA); raise Exception.Create('绑定RSA密钥到EVP_PKEY失败'); end; finally BN_free(Bignum); end; - 简化日期转换函数:
function DateToASN1_TIME(const ADate: TDateTime): PASN1_TIME; var UnixTime: time_t; begin UnixTime := DateTimeToUnix(ADate); Result := ASN1_TIME_set(nil, UnixTime); end;
- 废弃函数替换:
内容的提问来源于stack exchange,提问作者Sorin
相关产品推荐
相关产品推荐

