2

I want to make my Apache web server accept SSL connections ONLY IF the client presents itself with a specific SSL client certificate. In other words, only ONE client is allowed and it MUST use a specific client certificate (that I also have).

This client certificate (let's call it "MyClientCertificate") is a normal PEM certificate emitted by a CA (let's call it "MyCA") for which I also have in turn its certificate.

This is how I configured my Apache virtual host for this purpose (I'm reporting only the options related to the SSL client verification):

SSLVerifyClient require
SSLCACertificatePath /etc/ssl/certs
SSLCACertificateFile /etc/ssl/client/MyClientCertificate.pem
SSLCADNRequestFile /etc/ssl/client/MyClientCertificate.pem
SSLVerifyDepth 1

Explanations of the options (or of my intentions at least...):

  • with "SSLVerifyClient require" I enforce Apache to always perform client verification and reject the connection if it fails
  • /etc/ssl/certs contains the PEM certificates of MyCA (properly installed in the system with the required hashing+symlinking process), as long as those of all the other well-known CAs Apache may recognize
  • with SSLCACertificateFile I'm adding the MyClientCertificate certificate to the list of CA certificates in /etc/ssl/certs, as a trusted certificate by itself, and with SSLCADNRequestFile I'm saying that my server should request just that certificate (and only that) to the client, not the whole list of CA certificates in SSLCACertificatePath+SSLCACertificateFile
  • with "SSLVerifyDepth 1" I'm saying that the allowed client certificate is signed by a CA which is among those recognized by the server (in SSLCACertificatePath)

This seemed to work: Apache requires the client certificate, refuses the connection if the specified client certificate is not provided and accept it if it is. However, recently the client (which is a supplier for me) has decided to change its client certificate, with a new one (let's call it MyNewClientCertificate.pem) with 2048 bit encryption instead of the 1024 bit depth of the old one. The problem is this:

  • the supplier says it has changed its client certificate and that it is now using the new certificate to authenticate
  • the supplier says for sure it's presenting itself with JUST the new certificate and not with both the new and the old one
  • I have not yet changed my Apache configuration (hence, my Apache is still configured to request the old certificate)
  • BUT my Apache is accepting the supplier client connections without any problem!! I would have expected it to start to fail until I change its configuration to replace MyClientCertificate.pem references with MyNewClientCertificate.pem

The new certificate is emitted by the same CA of the old one. The supplier suggests that my Apache configuration is probably accepting all connections from clients that present a certificate emitted by that CA, instead of performing a match on the client certificate itself.

If this is the case, what am I doing wrong in my configuration? Should I lower the SSLVerifyDepth to 0? (but isn't this saying that the certificate must be self-signed?) Or do I have to change something in the SSLCACertificateFile/SSLCADNRequestFile directives?

Since this system is in production, I don't have the ability to perform all the tests I can think of, but I should try to make focused changes to get to the correct result as quickly as possible. This is why I would appreciate any help on this topic.

Mauro Molinari
  • 192
  • 2
  • 2
  • 10

1 Answers1

3

You can match a specific client certificate by using the SSLRequire directive to match against the complete Subject DN or just the CN part from the client's certificate:

SSLRequire %{SSL_CLIENT_S_DN} eq "C=AU, ST=Some-State, L=Springfield, O=ServerFault.com, OU=Moderators, CN=HBruijn/emailAddress=hbruijn@serverfault.com"   

SSLRequire %{SSL_CLIENT_S_DN_CN} eq "HBruijn/emailAddress=hbruijn@serverfault.com"

Use openssl x509 -in client.crt -text to display the Subject string.

A more complete config would be:

SSLVerifyClient      none
SSLCACertificateFile conf/ssl.crt/ca.crt
SSLCACertificatePath conf/ssl.crt

<Directory /usr/local/apache2/htdocs/secure/area>
  SSLVerifyClient      require
  SSLVerifyDepth       5
  SSLOptions           +FakeBasicAuth
  SSLRequireSSL
  SSLRequire           %{SSL_CLIENT_S_DN_CN} eq "HBruijn/emailAddress=hbruijn@serverfault.com"
</Directory>
HBruijn
  • 72,524
  • 21
  • 127
  • 192
  • Hi, thanks for your answer. Let me see if I understand it correctly. Since both the old and new certificates share the same DN/CN fields, in this way I can't enforce the use of the new certificate over the old one. On the other hand, I see that with SSLRequire I can even enforce a check on the whole certificate with SSL_CLIENT_CERT, although I read in Google some users that had problems with that. Anyway, does this mean that SSLCADNRequestFile and SSLCACertificateFile actually apply on the CA of the supplied certificate, if it is not a root certificate itself? This is a bit confusing for me. – Mauro Molinari Jan 07 '16 at 10:09
  • 2
    You could use the serial number (using `SSL_CLIENT_M_SERIAL`) instead of the DN or CN, if you want to use just one single certificate. See http://httpd.apache.org/docs/2.4/mod/mod_ssl.html for all the things you can match against. (And yes, any option that starts with `SSLCA` generally refers to a CA certificate, not a client certificate.) – Jenny D Dec 07 '17 at 12:44
  • for reference (2018 10 09): the SSLRequire is deprecated in apache 2.4 as I just noticed here: https://httpd.apache.org/docs/current/mod/mod_ssl.html#sslrequire and it refers to using the "'require expr" instead: https://httpd.apache.org/docs/current/mod/mod_authz_core.html#reqexpr and details abt apache expressions: https://httpd.apache.org/docs/current/expr.html – lievendp Oct 09 '18 at 10:32