I'm trying to do a POST
request in Delphi, using the Indy TIdHTTP
component. The target host requires a certificate and key files. Testing the requests with Postman or Insomnia, it works fine, returns 200, all right. But, in Delphi with the procedures I will show, it returns an error:
Project ... raised exception class EIdOSSLLoadingCertError with message 'Could not load certificate. error:0906D06C:PEM routines:PEM_read_bio:no start line'.
I load the certificates from a constant, and they are in the correct format:
-----BEGIN PRIVATE KEY-----
...
-----END PRIVATE KEY-----
-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----
The functions in question are these, the error occurs when trying to run the IdHTTP.Post()
:
function TAPI.LoadCertificate : TIdSSLIOHandlerSocketOpenSSL;
var
fileCrt, fileKey : TStringList;
var_path_system: string;
begin
if assigned(SSLIOHandler) then SSLIOHandler:= nil;
SSLIOHandler := TIdSSLIOHandlerSocketOpenSSL.Create(nil);
try
fileCrt:= TStringList.Create;
fileKey:= TStringList.Create;
fileCrt.Add(const_cert);
fileKey.Add(const_private_key);
var_path_system:= ExtractFilePath(Application.ExeName);
fileCrt.SaveToFile(var_path_system+'Certificate.crt', Tencoding.UTF8);
fileKey.SaveToFile(var_path_system+'PrivateKeyFile.key', Tencoding.UTF8);
SSLIOHandler.Port:= 443;
SSLIOHandler.DefaultPort:= 443;
SSLIOHandler.SSLOptions.CertFile:= var_path_system+'Certificate.crt';
SSLIOHandler.SSLOptions.KeyFile:= var_path_system+'PrivateKeyFile.key';
SSLIOHandler.SSLOptions.Method:= sslvTLSv1_2;
SSLIOHandler.SSLOptions.SSLVersions:= [sslvTLSv1_2];
SSLIOHandler.SSLOptions.Mode:= sslmClient;
SSLIOHandler.SSLOptions.VerifyMode:= [] ;
SSLIOHandler.SSLOptions.VerifyDepth:= 0;
SSLIOHandler.Host := 'place.holder.com';
finally
fileCrt.Free;
fileKey.Free;
end;
result:= SSLIOHandler;
end;
function TAPI.GetToken: TStringBuilder;
function RemoveSurroundingQuotes(const S: string): string; begin
Result := S;
if (Length(S) >= 2) and (S[1] = '"') and (S[Length(S)] = '"') then
Result := Trim(Copy(S, 2, Length(S) - 2));
end;
var
IdHTTP: TIdHTTP;
SSL: TIdSSLIOHandlerSocketOpenSSL;
Params: TStringList;
JsonStreamReturn: TStringStream;
var_Result: TJSONObject;
StringBuilder: TStringBuilder;
begin
IdHTTP := TIdHTTP.Create(nil);
try
StringBuilder := TStringBuilder.Create;
SSL := TIdSSLIOHandlerSocketOpenSSL.Create(IdHTTP);
IdHTTP.IOHandler := SSL;
SSL.SSLOptions.Method := sslvTLSv1_2;
JsonStreamReturn := TStringStream.Create('', TEncoding.UTF8);
try
Params := TStringList.Create;
try
Params.Add('client_id='+ FAAuthorization);
Params.Add('client_secret=' + FAAuthorizationSecret);
Params.Add('grant_type=client_credentials');
IdHTTP.Request.ContentType := 'application/x-www-form-urlencoded';
IdHTTP.IOHandler := LoadCertificate;
try
IdHTTP.Post(URLBasic, Params, JsonStreamReturn);
except
on E: EIdHTTPProtocolException do
LogHttpClientError(E, IdHTTP.ResponseCode, false);
on E: Exception do
raise;
// LogHttpClientError();
end;
[... rest of the code]
I can't find much information on this specific case. But I know the certificates are valid and with the correct syntax.
I've tried Google'ing it, but found no answer or clarification. I even tried a long talk with ChatGPT, but I can't find more information about this error. The certificates are correct, they load in an archive just fine, and the SSLIOHandler
loads the certificate without error.
The certificate was generated and signed by the host, it's still valid. I've also tried using PFX but it returns a [...] ASN1_CHECK_TLEN:wrong tag
error. I also tried different SSL options to load the certificate, to no avail. I tested the same again with Postman, and it works just fine with the same host, port, IDs, etc.
The error means that the SSLIOHandler
could not load the file specified in its CertFile
property. The no start line
error is a good indication that there is a problem with the formatting of the certificate file.
One thing I notice is you are saving the certificate files using TEncoding.UTF8
, which defines a BOM. By default, TStringList.SaveTo...()
will write out that BOM. But OpenSSL may have trouble processing a UTF-8 encoded certificate file that has a BOM. So, try disabling the TStringList.WriteBOM
option when calling TStringList.SaveToFile()
, eg:
fileCrt.WriteBOM := False;
fileKey.WriteBOM := False;
Alternatively, you can use TEncoding.ASCII
instead, which does not define a BOM. PEM-encoded certificate files do not use non-ASCII characters, so TEncoding.ASCII
will produce the same textual content as TEncoding.UTF8
, but without a BOM.