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

FreePascal调用LibCrypto-3.dll执行x509_sign时遇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机制做桥接才能正常交互。

修复步骤:

  1. 从OpenSSL源码包中找到applink.c文件(通常位于ms目录下)
  2. 使用C编译器(如MinGW gcc)编译成obj文件:
    gcc -c applink.c -o applink.obj
    
  3. 在你的FreePascal代码开头添加链接指令,将生成的obj文件引入:
    {$MODE OBJFPC}
    {$LONGSTRINGS ON}
    {$L applink.obj} // 添加这一行
    

完成后重新编译运行,X509_sign函数就能正常执行,证书签名流程可以顺利完成。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.13 17:23:13