I have scoured the internet and haven't found a solution or method on how to verify the certificate when connecting over HTTPS using TIdHTTP.
I have hooked up a IdSSLIOHandlerSocketOpenSSL component as the IOHandler, set the SSLModes, etc. but when I browse to https://s3.amazonaws.com it cannot verify the certificate.
OpenSSL (Indy) gives
"Error connecting with SSL. SSL3_GET_SERVER_CERTIFICATE: Certificate verify failed"
The OpenSSL libraries have successfully loaded (checked with WhichFailedToLoad). The OnStatusInfo event writes the following:
SSL status: "before/connect initialization"
SSL status: "before/connect initialization"
SSL status: "SSLv2/v3 write client hello A"
SSL status: "SSLv3 read server hello A"
SSL status: "SSLv3 read server certificate B"
SSL status: "SSLv3 read server certificate B"
SSL status: "SSLv3 read server certificate B"
And OnVerifyPeer, AOk = False.
How can I get it to verify correctly. What's going on?
Thanks for reading,
Adrian
You have to implement a event handler for the OnVerifyPeer event of your TIdSSLIOHandlerSocketOpenSSL component.
From IdSSLOpenSSL.pas:
Note that you really should always
implement OnVerifyPeer, otherwise the certificate of the peer you are
connecting to is NOT checked to ensure it is valid.
If you just want to consider valid the same certificates the Library considers also valid, you just have to implement it this way:
function TForm1.IdSSLIOHandlerSocketOpenSSL1VerifyPeer(Certificate: TIdX509;
AOk: Boolean; ADepth, AError: Integer): Boolean;
begin
Result := AOk;
end;
As Indy first checks for the validity of the certificate and pass you if it is Ok or not in the AOk parameter. The last word is in your code, as you may want to let pass some kinds of minor validation errors, like being out of date, or even ask the user if the certificate is accepted or not in case of any error (minor or not).
To understand why it works this way, you may also want to read all the comments at the top of the IdSSLOpenSSL.pas file:
{
Important information concerning OnVerifyPeer:
Rev 1.39 of February 2005 deliberately broke the OnVerifyPeer interface,
which (obviously?) only affects programs that implemented that callback
as part of the SSL negotiation. Note that you really should always
implement OnVerifyPeer, otherwise the certificate of the peer you are
connecting to is NOT checked to ensure it is valid.
Prior to this, if the SSL library detected a problem with a certificate
or the Depth was insufficient (i.e. the "Ok" parameter in VerifyCallback
is 0 / FALSE), then irrespective of whether your OnVerifyPeer returned True
or False, the SSL connection would be deliberately failed.
This created a problem in that even if there was only a very minor
problem with one of the certificates in the chain (OnVerifyPeer is called
once for each certificate in the certificate chain), which the user may
have been happy to accept, the SSL negotiation would be failed. However,
changing the code to allow the SSL connection when a user returned True
for OnVerifyPeer would have meant that existing code which depended on
automatic rejection of invalid certificates would then be accepting
invalid certificates, which would have been an unacceptable security
change.
Consequently, OnVerifyPeer was changed to deliberately break existing code
by adding an AOk parameter. To preserve the previous functionality, your
OnVerifyPeer event should do "Result := AOk;". If you wish to consider
accepting certificates that the SSL library has considered invalid, then
in your OnVerifyPeer, make sure you satisfy yourself that the certificate
really is valid and then set Result to True. In reality, in addition to
checking AOk, you should always implement code that ensures you are only
accepting certificates which are valid (at least from your point of view).
Ciaran Costelloe, ccostelloe[_a_t_]flogas.ie
}
{
RLebeau 1/12/2011: Breaking OnVerifyPeer event again, this time to add an additional
AError parameter (patch courtesy of "jvlad", dmda@yandex.ru). This
helps user code distinquish between Self-signed and invalid
certificates.
}
I know this is an ancient post, but it is all I could find to solve the problem. So I would like to add to Marcus's answer for others with the same problem:
When OpenSSL cannot find the root certificate on the PC, AError will return #19 (X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN), and the AOK will be false for the Root certificate. When you manually load the root certificate from file, AOK should always return true (and you have achieved certificate pinning of some sort too):
FSSLIOHandlerSocketOpenSSL.SSLOptions.RootCertFile := 'MyRoot.cer';
If you get an error like SSL3_GET_SERVER_CERTIFICATE:certificate verify failed
, then keep reading:
It seems in Indy 10, that if you set a VerifyDepth
of 0
, the 0
actually means all
.