EldoS | Feel safer!

Software components for data protection, secure storage and transfer

How can I extract the certificate with public key from a pfx file?

Also by EldoS: Solid File System
A virtual file system that offers a feature-rich storage for application documents and data with built-in compression and encryption.
#26991
Posted: 10/24/2013 10:06:37
by Jacob jvandiermen (Standard support level)
Joined: 08/09/2013
Posts: 55

Hello Eldos,

I want to extract the certificate with public key from a pfx file as string value. The value will be included in the <ds:X509Certificate> element of the signature document.
like so:
<ds:X509Certificate>
MAUwBKACAhHOPriqWm39UTAGKkj3......pjuXhkSVRNh9aihGHLmo
</ds:X509Certificate>

The value is then used by the receiving party to very the signature document.
This is done by taking the value of the <ds:X509Certificate> element and use this to load the certificate


I tried to extract the certificate with public key from the pfx file with this function:

Code

function TXMLSignature.getCertificateAsString(fileNameCertificate,password: string): AnsiString;
var
  elX509Certificate: TElX509Certificate;
begin
  elX509Certificate:= TElX509Certificate.Create(nil);
  elX509Certificate:= getX509Certificate(fileNameCertificate,password);
  if assigned(elX509Certificate) then
    begin
      result:= SBEncoding.Base64EncodeString(StrPas(PChar(elX509Certificate.CertificateBinary)),False);
    end
  else
    result:= strEmptyString;

  FreeAndNil(elX509Certificate);
end;


With the function getX509Certificate we can load the certificate in an TElX509Certificate.
The string value that I get now isn't valid for loading the certificate.
The receiving party gets a ror with XML Marshalling cannot create X509Certificate
This function works. I used it already for signing!
How can I retrieve the certificate with public key from the pfx file.
Further more how can I then load the certificate from the <ds:X509Certificate> element value

Best Regards,

Jacob
#26992
Posted: 10/24/2013 10:30:12
by Eugene Mayevski (EldoS Corp.)

You are converting CertificateBinary (which is a binary value) to string. This is not correct. Right way is to call SBEncoding.Base64Encode() method which accepts a buffer.

Call something like Base64Encode(elx509Certificate.CertificateBinary, elx509Certificate.CertificateSize, ... )


Sincerely yours
Eugene Mayevski
#27000
Posted: 10/25/2013 02:51:41
by Jacob jvandiermen (Standard support level)
Joined: 08/09/2013
Posts: 55

Hello Eugene,

Thank you for your info.
I tried the following:
Code
function getCertificateAsString(fileNameCertificate,password: string): AnsiString;
var
  elX509Certificate: TElX509Certificate;
  certificatePByteArray: PByteArray;
  size: Integer;
begin
  elX509Certificate:= TElX509Certificate.Create(nil);
  elX509Certificate:= getX509Certificate(fileNameCertificate,password);
  if assigned(elX509Certificate) then
    begin
      size:= elX509Certificate.CertificateSize;
      SBEncoding.Base64Encode(elX509Certificate.CertificateBinary,size,certificatePByteArray,size,False);
      result:= SBEncoding.Base64EncodeString(StrPas(PChar(certificatePByteArray)));
    end
  else
    result:= strEmptyString;

  FreeAndNil(elX509Certificate);
end;


But this didn't work. Now the result contains only 4 characters!
What can/must I do now.

Regards,

Jacob
#27001
Posted: 10/25/2013 02:56:19
by Eugene Mayevski (EldoS Corp.)

The output buffer is too small in a call to Base64Encode. Encoding increases buffer size by about 1/3.

First you need to call the method with zero size. The method will calculate the space needed. Next you call the method with properly allocated buffer


Sincerely yours
Eugene Mayevski
#27002
Posted: 10/25/2013 02:58:48
by Vsevolod Ievgiienko (EldoS Corp.)

The correct code should look this way:

Code
var
  Buf: ByteArray;
  Size, OutSize: integer;
begin
  ...
Size:= elX509Certificate.CertificateSize;
OutSize := 0;
SBEncoding.Base64Encode(elX509Certificate.CertificateBinary,Size,nil,OutSize,False);
SetLength(Buf, OutSize);
SBEncoding.Base64Encode(elX509Certificate.CertificateBinary,Size,@Buf[0],OutSize,False);
...
#27005
Posted: 10/25/2013 05:31:12
by Jacob jvandiermen (Standard support level)
Joined: 08/09/2013
Posts: 55

Hello Eugene & Vsevolod,

I managed to extract the certificate as string value.
Also I managed to instantiate a TElX509Certificate object by using the string.

Code

function getCertificateAsString(fileNameCertificate,password: string): AnsiString;
var
  elX509Certificate: TElX509Certificate;
  certificateByteArray: ByteArray;
  size,outputSize:  Integer;

  elInverseX509Certificate: TElX509Certificate;
  certificateInverseByteArray: ByteArray;
  strValueOfRetrievedCertificate: AnsiString;
begin
  elX509Certificate:= TElX509Certificate.Create(nil);
  elX509Certificate:= getX509Certificate(fileNameCertificate,password);
  if assigned(elX509Certificate) then
    begin
      size:= elX509Certificate.CertificateSize;
      outputSize:= 0;
      SBEncoding.Base64Encode(elX509Certificate.CertificateBinary,size,nil,outputSize,False);
      SetLength(certificateByteArray,outputSize);
      SBEncoding.Base64Encode(elX509Certificate.CertificateBinary,size,@certificateByteArray[0],outputSize,False);
      result:= SBEncoding.Base64EncodeArray(certificateByteArray);


      // now use the string value to retrieve the certificate in a
      // TElX509Certificate  object  
      elInverseX509Certificate:= TElX509Certificate.Create(nil);

      // note : use the outputSize to convert the  string value back to    
      // a ByteArray
      elInverseX509Certificate.LoadFromBuffer(@SBEncoding.Base64DecodeArray(result)[0],outputSize);

      // now check if the certificate string output is equal to the string    
      // output of the retrieved certificate
      size:= elInverseX509Certificate.CertificateSize;
      outputSize:= 0;
      SBEncoding.Base64Encode(elInverseX509Certificate.CertificateBinary,size,nil,outputSize,False);
      SetLength(certificateInverseByteArray,outputSize);
      SBEncoding.Base64Encode(elInverseX509Certificate.CertificateBinary,size,@certificateInverseByteArray[0],outputSize,False);
      strValueOfRetrievedCertificate:= SBEncoding.Base64EncodeArray(certificateInverseByteArray);


      // If the two values are equal than the two certificate object
      // hold the same certificate!  
      if AnsiCompareStr(strValueOfRetrievedCertificate,result) = 0 then
        begin
          FreeAndNil(elInverseX509Certificate);
          SetLength(certificateInverseByteArray,0);
        end;
    end
  else
    result:= strEmptyString;

  SetLength(certificateByteArray,0);
  FreeAndNil(elX509Certificate);
end;


I now refactored the source code for loading the certificate from a string

Code

function loadCertificateFromString(certificateAsAnsiString: AnsiString): TElX509Certificate;
var
  elX509Certificate: TElX509Certificate;
  size:  Integer;
begin
  try
    result:= TElX509Certificate.Create(nil);
    // What is the value of the size ?
    size:= ??;
    result.LoadFromBuffer(@SBEncoding.Base64DecodeArray(certificateAsAnsiString)[0],size);
  except on E: Exception do
    result:= nil;
  end;
end;


The only thing now is how can i calculate the value of the size in order to load the certificate back into a TElX509Certificate object.

Regards,

Jacob
#27006
Posted: 10/25/2013 05:32:58
by Eugene Mayevski (EldoS Corp.)

If you don't put everything to one line and instead use intermediate variables, you'll know how to measure size.


Sincerely yours
Eugene Mayevski
#27007
Posted: 10/25/2013 05:53:56
by Jacob jvandiermen (Standard support level)
Joined: 08/09/2013
Posts: 55

Hallo Eugene,

Got it!!! Thanks for the hint!

Code
function loadCertificateFromString(certificateAsAnsiString: AnsiString): TElX509Certificate;
var
  elX509Certificate: TElX509Certificate;
  certificateByteArray: ByteArray;
  size:  Integer;
begin
  try
    result:= TElX509Certificate.Create(nil);
    certificateByteArray:= SBEncoding.Base64DecodeArray(certificateAsAnsiString);
    size:= Length(certificateByteArray);
    result.LoadFromBuffer(@SBEncoding.Base64DecodeArray(certificateAsAnsiString)[0],size);
  except on E: Exception do
    result:= nil;
  end;
end;


Regards,

Jacob
#27014
Posted: 10/28/2013 06:10:11
by Jacob jvandiermen (Standard support level)
Joined: 08/09/2013
Posts: 55

Hallo Eugene,

Let us just refer to the pfx certificate that I converted to a AnsiString as the original certificate
When I use the function loadCertificateFromString to load the certificate back to a TElX509Certificate object and then save this certificate with procedure TElX509Certificate.SaveToFile back to a pfx file I noticed that the size of the saved pfx Certificate isn't the same size as the original pfx certificate.
The saved pfx Certificate is much smaller.
Why is this the case? What is the best way to extract the certificate and the public key from from the pfx file?

Regards,

Jacob
#27015
Posted: 10/28/2013 06:19:17
by Vsevolod Ievgiienko (EldoS Corp.)

You loadCertificateFromString function doesn't load PFX certificates. It works with Base64 encoded DER format. If you load a certificate stored as DER and save it as PFX, obviously it has different size.
Also by EldoS: CallbackFilter
A component to monitor and control disk activity, track file and directory operations (create, read, write, rename etc.), alter file data, encrypt files, create virtual files.

Reply

Statistics

Topic viewed 2995 times

Number of guests: 1, registered members: 0, in total hidden: 0




|

Back to top

As of July 15, 2016 EldoS Corporation will operate as a division of /n software inc. For more information, please read the announcement.

Got it!