I’m having trouble with the x509_sign
function in the following code.
The error I get from LibCrypto
via ERR_print_errors_fp
is:
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.
This this the output.
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
I’ve seen this same error in several other coding languages and seen no solutions for this error.