FreePascal调用LibCrypto-3.dll执行x509_sign时遇OPENSSL_Applink错误
FreePascal调用OpenSSL 3创建自签名证书时X509_sign失败,报错OPENSSL_Uplink no OPENSSL_Applink
我用FreePascal编写代码,通过OpenSSL的LibCrypto-3.dll创建自签名证书,但调用X509_sign函数时失败。LibCrypto返回的错误信息如下:
OPENSSL_Uplink(721B6200,08): no OPENSSL_Applink
代码实现
program createselfsignedcert; {$MODE OBJFPC} {$LONGSTRINGS ON} uses SysUtils, Windows; const OPENSSL_LIB = 'libcrypto-3.dll'; // OPENSSL_LIB = 'libeay32.dll'; MBSTRING_FLAG = $1000; MBSTRING_ASC = MBSTRING_FLAG or 1; MBSTRING_BMP = MBSTRING_FLAG or 2; MBSTRING_UNIV = MBSTRING_FLAG or 3; MBSTRING_UTF8 = MBSTRING_FLAG or 4; EVP_PKEY_RSA = 6; // RSA key type type TCallback=procedure(str: PChar; len: size_t; u: Pointer); //cdecl; PX509=Pointer; PEVP_PKEY=Pointer; PEVP_MD=Pointer; PASN1_TIME=Pointer; PBIO=PAnsiChar; ASN1_STRING = packed record length: Integer; _type: Integer; data: PChar; flags: LongWord; end; PASN1_UTCTIME=^ASN1_UTCTIME; ASN1_UTCTIME=ASN1_STRING; ASN1_INTEGER=ASN1_STRING; PASN1_INTEGER=^ASN1_STRING; // Create Objects function X509_new: PX509; cdecl; external OPENSSL_LIB name 'X509_new'; function X509_NAME_new: Pointer; cdecl; external OPENSSL_LIB name 'X509_NAME_new'; function EVP_PKEY_new: PEVP_PKEY; cdecl; external OPENSSL_LIB name 'EVP_PKEY_new'; function RSA_generate_key(bits: Integer; e: Integer; callback: Pointer; arg: Pointer): Pointer; cdecl; external OPENSSL_LIB name 'RSA_generate_key'; function ASN1_UTCTIME_new: PASN1_TIME; cdecl; external OPENSSL_LIB name 'ASN1_UTCTIME_new'; function BN_new: Pointer; cdecl; external OPENSSL_LIB name 'BN_new'; function BIO_new_file(filename: PChar; mode: PChar): Pointer; cdecl; external OPENSSL_LIB name 'BIO_new_file'; // Free Objects procedure X509_free(x: PX509); cdecl; external OPENSSL_LIB name 'X509_free'; procedure X509_NAME_free(n: Pointer); cdecl; external OPENSSL_LIB name 'X509_NAME_free'; procedure EVP_PKEY_free(pkey: PEVP_PKEY); cdecl; external OPENSSL_LIB name 'EVP_PKEY_free'; procedure RSA_free(r: Pointer); cdecl; external OPENSSL_LIB name 'RSA_free'; procedure ASN1_UTCTIME_free(a: PASN1_TIME); cdecl; external OPENSSL_LIB name 'ASN1_UTCTIME_free'; procedure BN_free(a: Pointer); cdecl; external OPENSSL_LIB name 'BN_free'; function BIO_free(a: pointer): integer; cdecl; external OPENSSL_LIB name 'BIO_free'; // Handle Objects function X509_set_version(x: PX509; version: Integer): Integer; cdecl; external OPENSSL_LIB name 'X509_set_version'; function X509_get_serialNumber(x: PX509): Integer; cdecl; external OPENSSL_LIB name 'X509_get_serialNumber'; procedure X509_set_serialNumber(x: PX509; serial: PASN1_INTEGER); cdecl; external OPENSSL_LIB name 'X509_set_serialNumber'; procedure X509_gmtime_adj(s: PASN1_TIME; adj: Integer); cdecl; external OPENSSL_LIB name 'X509_gmtime_adj'; function X509_get0_notBefore(x: PX509): PASN1_TIME; cdecl; external OPENSSL_LIB name 'X509_get0_notBefore'; function X509_get0_notAfter(x: PX509): PASN1_TIME; cdecl; external OPENSSL_LIB name 'X509_get0_notAfter'; function X509_NAME_add_entry_by_txt(name: Pointer; field: PAnsiChar; _type: Integer; bytes: PAnsiChar; len: Integer; location, _default: Integer): integer; cdecl; external OPENSSL_LIB name 'X509_NAME_add_entry_by_txt'; function X509_set_issuer_name(x: PX509; name: Pointer): Integer; cdecl; external OPENSSL_LIB name 'X509_set_issuer_name'; function X509_set_subject_name(x: PX509; name: Pointer): Integer; cdecl; external OPENSSL_LIB name 'X509_set_subject_name'; function X509_set_pubkey(x: PX509; pkey: PEVP_PKEY): integer; cdecl; external OPENSSL_LIB name 'X509_set_pubkey'; function X509_sign(x: PX509; pkey: PEVP_PKEY; md: PEVP_MD): Integer; cdecl; external OPENSSL_LIB name 'X509_sign'; function EVP_PKEY_set1_RSA(key: pEVP_PKEY; rsa: pointer): integer; cdecl; external OPENSSL_LIB name 'EVP_PKEY_set1_RSA'; function EVP_PKEY_assign(pkey: PEVP_PKEY; typ: integer; key: pointer): integer; cdecl; external OPENSSL_LIB name 'EVP_PKEY_assign'; function PEM_write_bio_X509(bp: pBIO; x: pX509): integer; cdecl; external OPENSSL_LIB name 'PEM_write_bio_X509'; procedure PEM_write_X509(filename: pchar; x: PX509); cdecl; external OPENSSL_LIB name 'PEM_write_X509'; function PEM_write_bio_PrivateKey(bp: pointer; pkey: PEVP_PKEY; enc: pointer; kstr: pointer; klen: integer; pem_password_cb: pointer; u: pointer): integer; cdecl; external OPENSSL_LIB name 'PEM_write_bio_PrivateKey'; function PEM_write_PrivateKey(filename: pchar; pkey: PEVP_PKEY; enc: pointer; kstr: pointer; klen: integer; pem_password_cb: pointer; u: pointer): integer; cdecl; external OPENSSL_LIB name 'PEM_write_PrivateKey'; function ASN1_INTEGER_new: PASN1_INTEGER; cdecl; external OPENSSL_LIB name 'ASN1_INTEGER_new'; function ASN1_INTEGER_set_int64(a: PASN1_INTEGER; r: Int64): Integer; cdecl; external OPENSSL_LIB name 'ASN1_INTEGER_set_int64'; procedure ERR_print_errors_fp(filename: pchar); cdecl; external OPENSSL_LIB name 'ERR_print_errors_fp'; procedure ERR_print_errors_cb(callback: TCallBack; u: Pointer); cdecl; external OPENSSL_LIB name 'ERR_print_errors_cb'; procedure ERR_print_errors(bp: PBIO); cdecl; external OPENSSL_LIB name 'ERR_print_errors'; function ERR_get_error: Cardinal; cdecl; external OPENSSL_LIB name 'ERR_get_error'; // Message Digest function EVP_sha256: PEVP_MD; cdecl; external OPENSSL_LIB name 'EVP_sha256'; function EVP_sha1: PEVP_MD; cdecl; external OPENSSL_LIB name 'EVP_sha1'; function EVP_md5: PEVP_MD; cdecl; external OPENSSL_LIB name 'EVP_md5'; procedure HaltOnBadObj(Obj: pointer; Mess: string); begin if not Assigned(Obj) then begin writeln(Mess); halt(1); end; end; procedure MyErrorCallback(str: PChar; len: size_t; u: Pointer); //cdecl; begin WriteLn('OpenSSL Error: ', StrPas(str)); end; procedure ShowFuncStat(RetVal: integer; Mess: string; ShowErr: boolean=False); var err: Cardinal; begin if RetVal=1 then writeln(Mess+' ok.') else begin writeln('Error:- '+Mess+'.'); if ShowErr then ERR_print_errors_fp(@stderr); end; end; procedure GenerateRandomSerialNumber(var SerialNumber: PASN1_INTEGER); var RandomValue: Int64; i: Integer; begin // We generate a 64-bit random number RandomValue:=0; for i:=1 to 8 do RandomValue:=(RandomValue shl 8) or Random(256); // We make sure the number is positive RandomValue:=RandomValue and $7FFFFFFFFFFFFFFF; // We set the serial number ShowFuncStat(ASN1_INTEGER_set_int64(SerialNumber, RandomValue), 'Setting serial number'); end; procedure CreateCertificate; var x509: PX509; pkey: PEVP_PKEY; SerialNumber: PASN1_INTEGER; name: Pointer; time: PASN1_TIME; rsa: Pointer; bio: Pointer; begin // Create a new X509 structure x509:=X509_new; HaltOnBadObj(x509, 'Error creating X509 structure'); // Set the version of the certificate (X509v3) ShowFuncStat(X509_set_version(x509, 2), 'Setting X509 version'); // Generate key pair pkey:=EVP_PKEY_new; HaltOnBadObj(pkey, 'Error creating private key'); rsa:=RSA_generate_key(2048, $10001, nil, nil); HaltOnBadObj(rsa, 'Error generating RSA key'); // Assign the generated key to the pkey structure ShowFuncStat(EVP_PKEY_set1_RSA(pkey, rsa), 'Assigning RSA to pkey'); bio:=BIO_new_file('privatea_key.pem', 'w'); HaltOnBadObj(bio, 'Error creating BIO for file'); ShowFuncStat(PEM_write_bio_PrivateKey(bio, pkey, nil, nil, 0, nil, nil), 'Writing private key to file'); // ShowFuncStat(PEM_write_PrivateKey(@stderr, pkey, nil, nil, 0, nil, nil), 'Writing private key to file'); // Set the private key for the certificate ShowFuncStat(X509_set_pubkey(x509, pkey), 'Setting public key'); // Set the serial number for x509 SerialNumber:=ASN1_INTEGER_new; HaltOnBadObj(SerialNumber, 'Error creating Serial Number'); GenerateRandomSerialNumber(SerialNumber); X509_set_serialNumber(x509, SerialNumber); // Set validity period (e.g., one year from now) time:=ASN1_UTCTIME_new; // HaltOnBadObj(time, 'Error creating time structure'); X509_gmtime_adj(time, 60*60*24*365); // One year from now // X509_gmtime_adj(X509_get0_notBefore(x509), 0); // X509_gmtime_adj(X509_get0_notAfter(x509), 60*60*24*365); // Set the issuer and subject names (you can customize these) name:=X509_NAME_new; HaltOnBadObj(name, 'Error creating name structure'); ShowFuncStat(X509_NAME_add_entry_by_txt(name, 'C', MBSTRING_ASC, PAnsiChar('US'), -1, -1, 0), 'Set Country'); ShowFuncStat(X509_NAME_add_entry_by_txt(name, 'ST', MBSTRING_ASC, PAnsiChar('California'), -1, -1, 0), 'Set State'); ShowFuncStat(X509_NAME_add_entry_by_txt(name, 'L', MBSTRING_ASC, PAnsiChar('San Francisco'), -1, -1, 0), 'Set Location'); ShowFuncStat(X509_NAME_add_entry_by_txt(name, 'O', MBSTRING_ASC, PAnsiChar('My Organization'), -1, -1, 0), 'Set Organization'); ShowFuncStat(X509_NAME_add_entry_by_txt(name, 'OU', MBSTRING_ASC, PAnsiChar('My Organizational Unit'), -1, -1, 0), 'Set Department'); ShowFuncStat(X509_NAME_add_entry_by_txt(name, 'CN', MBSTRING_ASC, PAnsiChar('localhost'), -1, -1, 0), 'Set Common Name'); // Set the issuer and subject names in the certificate ShowFuncStat(X509_set_issuer_name(x509, name), 'Setting issuer name'); ShowFuncStat(X509_set_subject_name(x509, name), 'Setting subject name'); // Sign the certificate with the private key //////////////////////// This fails ShowFuncStat(X509_sign(x509, pkey, EVP_sha256()), 'Signing Certificate', true); //////////////////////// // Save the certificate to a file (e.g., cert.pem) bio:=BIO_new_file('cert.pem', 'w'); ShowFuncStat(PEM_write_bio_X509(bio, x509), 'Writing Certificate'); // Free everything X509_free(x509); EVP_PKEY_free(pkey); ASN1_UTCTIME_free(time); X509_NAME_free(name); BN_free(rsa); BIO_free(bio); end; begin try CreateCertificate; Writeln('Self-signed certificate created successfully.'); except on E: Exception do Writeln(E.ClassName, ': ', E.Message); end; end.
运行输出
Setting X509 version ok. Assigning RSA to pkey ok. Writing private key to file ok. Setting public key ok. Setting serial number ok. Set Country ok. Set State ok. Set Location ok. Set Organization ok. Set Department ok. Set Common Name ok. Setting issuer name ok. Setting subject name ok. Error:- Signing Certificate. OPENSSL_Uplink(721B6200,08): no OPENSSL_Applink
解决方法
这个错误是OpenSSL 3在Windows平台下的典型问题,根源是FreePascal的标准IO/内存管理机制和OpenSSL库的C实现不兼容,必须通过OPENSSL_Applink机制做桥接才能正常交互。
修复步骤:
- 从OpenSSL源码包中找到
applink.c文件(通常位于ms目录下) - 使用C编译器(如MinGW gcc)编译成obj文件:
gcc -c applink.c -o applink.obj - 在你的FreePascal代码开头添加链接指令,将生成的obj文件引入:
{$MODE OBJFPC} {$LONGSTRINGS ON} {$L applink.obj} // 添加这一行
完成后重新编译运行,X509_sign函数就能正常执行,证书签名流程可以顺利完成。
内容的提问来源于stack exchange,提问作者Trax Xart
相关产品推荐
相关产品推荐

