If I wanted to check the expiration date of a SSL certificate I can use IdHTTP
connected to a IdSSLIOHandlerSocketOpenSSL
IdHTTP1.IOHandler := IdSSLIOHandlerSocketOpenSSL1;
And then tapping into OnVerifyPeer
function TForm1.IdSSLIOHandlerSocketOpenSSL1VerifyPeer(Certificate: TIdX509;
AOk: Boolean; ADepth, AError: Integer): Boolean;
begin
Showmessage(Certificate.notAfter));
end;
And finally doing a IdHTTP.Get
to the server I want to retrieve the server certificate of, and this works.
BUT
I have a client certificate stored locally on my Android device that gets deployed with the App, how would I access the Certificate?
meaning how would I get the certificate loaded into a TidX509
Object?
the constructor for TidX509
requires a PX509
which is then assigned to a pointer of a record X509
so I am very lost in there
As Remy pointed out I just had to look how OpenSSL loads the Certificate from memory to get the certificate
but first the value that you get from reading the certificates notAfter
is UTC Time value in String so you will need something like this
function StringUTCtoDATETIME(UTCString : String) : TDateTime;
begin
if UTCString.Length = 13 then
begin
Delete(UTCString,UTCString.Length,1);
Delete(UTCString,0,2);
Insert(Copy(FormatDateTime('yyyy',Now),0,2),UTCString,0);
end
else
begin
Delete(UTCString,UTCString.Length,1);
end;
Result := EncodeDateTime(StrToInt(Copy(UTCString,1,4)),StrToInt(Copy(UTCString,5,2)),StrToInt(Copy(UTCString,7,2)),
StrToInt(Copy(UTCString,9,2)),StrToInt(Copy(UTCString,11,2)),StrToInt(Copy(UTCString,13,2)),000);
end;
The value conforms to the ISO8601 standard which can be YYMMDDHHNNSS
or YYYYMMDDHHNNSS
so I just made provision for that the value returned will always be YYYYMMDDHHNNSS
And to get the value we load it into memory and work with it as Indy does.
function ReturnCertificateExpiryDate(const AFileName: String): TDateTime;
var
LM : TMemoryStream;
LX: PX509;
LB: PBIO;
begin
LM := nil;
try
LM := TMemoryStream.Create;
LM.LoadFromFile(AFileName);
except
SSLerr(SSL_F_SSL_LOAD_CLIENT_CA_FILE, ERR_R_SYS_LIB);
LM.Free;
Exit;
end;
if LM = nil then
begin
Exit;
end;
try
LB := BIO_new_mem_buf(LM.Memory, LM.Size);
if Assigned(LB) then begin
LX := PEM_read_bio_X509(LB, nil, nil, nil);
if LX<> nil then
begin
RESULT := StringUTCtoDATETIME(String(LX.cert_info.validity.notAfter.data));
end;
end;
finally
FreeAndNil(LM);
end;
end;
And you also need to create a Context variable
var
Context : TIdSSLContext;
begin
Context := TIdSSLContext.Create;
ShowMessage(FormatDateTime('yyyy-mm-dd hh:nn:ss',(ReturnCertificateExpiryDate(TPath.Combine(TPath.GetDocumentsPath, 'client-cert.pem')))));
end;