delphiopensslindyx509certficiate2

Access Violation when call X509_new in Indy


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.


Solution

  • 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.