Ssl – How to make Apache trust a client certificate using an unknown CA, without validating the CA

apache-2.4certificatecertificate-authoritysslssl-certificate

I am in the following situation. I need to set up a 2-way integration with an external system. The admin of the external system required me to send two CSRs, one to be used to generate a client certificate, the other to generate a server certificate.
They sent me the corresponding certificates. I set up the channel me->they with success (i.e.: I can invoke their service supplying my client certificate), but I can't set up the inverse channel correctly (i.e.: I can't make my Apache accept their client certificate without complaining).

Together with my server certificate (let's call it my-server.pem) they also sent me their own client certificate (let's call it their-client.pem). This certificate (their-client.pem) is emitted by a "self-signed" CA, that is a CA that is not among those well-known CAs already available in my Linux system. I don't have this certificate and I was not yet able to get it from the external system admins (they are reluctant… let's put aside any comment on this please… >-|)

This is how I set up my VirtualHost in Apache:

SSLEngine on
SSLCertificateFile /path/to/my-server.pem
SSLCertificateKeyFile /path/to/my-server-secret-key.key
SSLVerifyClient require
SSLCACertificateFile /path/to/their-client.pem
SSLVerifyDepth 0

Since I don't have the CA certificate and since it's perfectly fine for me to say "just trust that client certificate, nobody else!", I put the client certificate itself as the SSLCACertificateFile, as suggested in the answer to:
https://security.stackexchange.com/questions/36069/use-client-certificate-using-self-signed-ca-while-using-web-certificate-of-a-pub

However, this doesn't seem to work. The error they see on their side is:

javax.net.ssl.SSLException: Received fatal alert: unknown_ca

After enabling the SSL log and setting it to debug, what I see on my side is:

[ssl:debug] [pid 3396] ssl_engine_kernel.c(1381): [client <their-ip>:41474] AH02275: Certificate Verification, depth 1, CRL checking mode: none [subject: CN=Test CA,OU=Foo,O=Bar,C=it / issuer: CN=Test CA,OU=Foo,O=Bar,C=it / serial: 1BFE / notbefore: Dec  6 15:22:45 2010 GMT / notafter: Dec  6 15:21:52 2020 GMT]
[ssl:info] [pid 3396] [client <their-IP>:41474] AH02276: Certificate Verification: Error (19): self signed certificate in certificate chain [subject: CN=Test CA,OU=Foo,O=Bar,C=it / issuer: CN=Test CA,OU=Foo,O=Bar,C=it / serial: 1BFE / notbefore: Dec  6 15:22:45 2010 GMT / notafter: Dec  6 15:21:52 2020 GMT]
[ssl:info] [pid 3396] [client <their-IP>:41474] AH02008: SSL library error 1 in handshake (server my-server.com:443)
[ssl:info] [pid 3396] SSL Library Error: error:140890B2:SSL routines:SSL3_GET_CLIENT_CERTIFICATE:no certificate returned
[ssl:info] [pid 3396] [client <their-IP>:41474] AH01998: Connection closed to child 54 with abortive shutdown (server my-server.com:443)

In other words, it's still trying to validate the dummy CA of the client certificate. I also tried to change SSLVerifyDepth to 1, with no luck (same error).
If I disable the client certificate request (by changing the SSLVerifyClient value), the invocation goes fine, but I don't think it's the correct way to go.

A very similar question is:
How can I make apache request a client SSL certificate without needing to verify it against a known CA?

However I'm not sure I understand the accepted solution. First of all, the client certificate I must validate is not self-signed (it's issued by an unknown CA).

Secondly, from what I understand from Apache/mod_ssl documentation, SSLVerifyCLient optional_no_ca actually disables strong client certificate authentication, because it makes it optional.

Third, the possibility to create a fake certificate with the same DN of the missing root CA certificate sounds like a workaround for forcing the client to send its client certificate, but in my case I don't think my problem is the client not sending me the certificate, but rather the Apache inability to fully validate it correctly.

Any suggestion on this topic would be very helpful.

Best Answer

Your error message clearly shows the cause right here: "depth 1". You've set SSLVerifyDepth 0 which per manual means that:

self-signed client certificates are accepted only

To check if client cert is as expected, and without the chain validation, try something like this:

SSLVerify none
SSLRequire (   %{SSL_CLIENT_S_DN_O} eq "Snake Oil, Ltd."  and %{REMOTE_ADDR} =~ m/^192\.76\.162\.[0-9]+$/  )