program createselfsignedcert;
{$MODE OBJFPC}
{$LONGSTRINGS ON}
uses
SysUtils, Windows;
const
OPENSSL_LIB = 'libcrypto-3.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
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';
procedure X509_gmtime_adj(s: PASN1_TIME; adj: Integer); cdecl; external OPENSSL_LIB name 'X509_gmtime_adj';
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';
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 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';
// 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 ShowFuncStat(RetVal: integer; Mess: string);
begin
if RetVal=1 then
writeln(Mess+' ok.')
else
writeln('Error:- '+Mess+'.');
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');
// 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
// 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'), Length('US'), -1, -1), 'Set Country');
ShowFuncStat(X509_NAME_add_entry_by_txt(name, 'ST', MBSTRING_ASC, PAnsiChar('California'), Length('California'), -1, -1), 'Set State');
ShowFuncStat(X509_NAME_add_entry_by_txt(name, 'L', MBSTRING_ASC, PAnsiChar('San Francisco'), Length('San Francisco'), -1, -1), 'Set Location');
ShowFuncStat(X509_NAME_add_entry_by_txt(name, 'O', MBSTRING_ASC, PAnsiChar('My Organization'), Length('My Organization'), -1, -1), 'Set Organization');
ShowFuncStat(X509_NAME_add_entry_by_txt(name, 'OU', MBSTRING_ASC, PAnsiChar('My Organizational Unit'), Length('My Organizational Unit'), -1, -1), 'Set Department');
ShowFuncStat(X509_NAME_add_entry_by_txt(name, 'CN', MBSTRING_ASC, PAnsiChar('localhost'), Length('localhost'), -1, -1), '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
ShowFuncStat(X509_sign(x509, pkey, EVP_sha256()), 'Signing Certificate');
// 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.