I use the following procedure:
procedure TForm2.Label1DblClick(Sender: TObject);
procedure GenerateSelfSignedCertificate(const AFileName: string);
procedure GenerateRandomSerialNumber(SerialNumber: PASN1_INTEGER);
var
RandomValue: Int64;
I: Integer;
begin
// We generate a 64-bit random number
RandomValue := 0;
for I := 1 to 8 do
begin
RandomValue := (RandomValue shl 8) or Random(256);
end;
// We make sure the number is positive
RandomValue := RandomValue and $7FFFFFFFFFFFFFFF;
// We set the serial number
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
// Inițializați OpenSSL
IdSSLOpenSSL.LoadOpenSSLLibrary;
// A new certificate is created
Cert := X509_new; // ERROR!!!! Access Vioaltion at address 00000...
try // A new RSA private key is created
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');
// Certificate version is set (X509 v3)
X509_set_version(Cert, 2);
// A random serial number is generated
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;
// The start and end date of the certificate is set
NotBefore := Now - 1;
// The certificate is valid from yesterday
NotAfter := NotBefore + 365;
// The certificate is valid for approximately one year
X509_set_notBefore(Cert, DateToASN1_TIME(NotBefore));
X509_set_notAfter(Cert, DateToASN1_TIME(NotAfter));
// The public key is set
X509_set_pubkey(Cert, PrivateKey);
// Issuer and subject name is set (localhost is used)
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);
// The certificate is signed
if X509_sign(Cert, PrivateKey, EVP_sha256) = 0 then
raise Exception.Create('Error signing the certificate');
// The certificate and private key are saved in a PEM file
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;
At the line Cert := X509_new;
I get an error message
Access violation at address ...
The X509_new function is defined in the IdSSLOpenSSLHeaders unit of the Indy framework that uses OpenSSL as follows:
{$EXTERNALSYM X509_new}
X509_new : function: PX509 cdecl = nil;
I am trying to generate a self-signed X509 certificate. Code is generated after many discussions with ChatGPT. However I can't get past this error.
Indy loads OpenSSL functions dynamically at runtime. You are not checking whether LoadOpenSSLLibrary()
is successful or not before trying to use any functions that it loads. Check the return value of LoadOpenSSLLibrary()
, eg:
if not IdSSLOpenSSL.LoadOpenSSLLibrary then
begin
// error handling...
end;
Also, check with IdSSLOpenSSLHeaders.WhichFailedToLoad()
after an OpenSSL load failure to see what actually failed to load. As you mentioned in a comment:
the OpenSSl library has version 3
Indy does not yet support OpenSSL 3.x (work in progress). The last version supported is 1.0.2u, which you can get from https://github.com/IndySockets/OpenSSL-Binaries. If you try to load a newer version, LoadOpenSSLLibrary()
will fail and WhichFailedToLoad()
will report Unsupported SSL Library version
.
That being said, X509_new
is not considered a critical function since Indy does not use it directly, so loading OpenSSL will not fail (if otherwise successful) if X509_new
is not exported from the libcrypto
library. The IdSSLOpenSSLHeaders.X509_new
pointer will simply be nil
, so you should check for that condition as well if you intend to use X509_new()
directly.