Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Apache Won't Request My SSL Client Certificate

First off, please note that I am new to configuring SSL. In the past, I've always been fortunate to have an IT department to set that up for me ahead of time. So be prepared for the possibility that I might need to ask for clarification on some of your answers. =)

What I'm Trying to Do

I'm setting-up a company intranet website for employees. For example, there will be a browser start page that shows content customized for each employee. As such, I need to be able to identify said employee without requiring any username/password or other prompting (one-time setup is ok though; I just don't want them having to be prompted every time). Naturally, SSL would seem to be the best way to go about doing this.

I'll have a MySQL database setup to associate "user" accounts with SSL_CLIENT_M_SERIAL and SSL_CLIENT_I_DN, which I'm assuming will be unique for each client certificate(?). I got that idea from this article: http://cweiske.de/tagebuch/ssl-client-certificates.htm

The first time the user goes to the internal website, they won't have a certificate (I do NOT want to be generating them manually for clients!), in which case $_SERVER["SSL_CLIENT_VERIFY"] == "NONE". If that happens, it'll go to the user account setup page, which will include a step where PHP generates an SSL client certificate and sends it to the browser for the user to install. Nice and simple. The user then installs the cert, the association is made, and after restarting the browser (for good measure), the user goes back to the internal website.

At this point, Apache should request the client certificate, which the browser then sends. The PHP script then parses the necessary $_SERVER variables, compares against the MySQL database, and good times are had by all. Again, nice and simple.

What's Working So Far

I have the server-side certificates installed. And yes, they are self-signed (for obvious reasons). Apache has mod_ssl installed and all that seems to be working fine. I created a PHP script that just dumps the $_SERVER array, and all the SSL_SERVER_* key values match the certificate I created for it.

The Problem

I can't get the client certificates to work! In that same PHP script, no matter what I do, SSL_CLIENT_VERIFY == "NONE" and the other SSL_CLIENT_* keys are missing. This is what happens if I have SSLVerifyClient set to optional in ssl.conf. According to every tutorial I've read, they all say that the webserver should ask the browser for a client certificate. Thing is, I can't get it to do that! It just goes straight to the PHP script and assumes I have no client certificates at all. This happens in Firefox, Chrome, and IE.

So I tried setting SSLVerifyClient to required and restarted the webserver. With that option in place, I can't even establish an SSL connection. Firefox just says the connection has been reset (other browsers display their own versions of that error as well). What's weird is that the logs don't show ANY activity on these connection attempts! I.e. access_log, error_log, ssl_access_log, ssl_error_log, AND ssl_request_log all don't show ANYTHING; it's as if the attempt never even occurred. This is frustrating because it means I don't even have an error message to work from. Just a webserver passive-aggressively telling me to go to hell.

I tried generating/installing my own client certificate manually using PHP's OpenSSL extension. The certificate installed just fine, though I can't find any information on how to associate that certificate with the server one (assuming I even need to?). Also, it doesn't seem to matter anyway, because Apache still won't even request a client certificate if optional is set. And if require is set, it just blows up without explanation. I need it to be set to optional anyway for this schema to work.

The Environment

OS: CentOS 5.7 64-bit (VirtualBox)

Apache: 2.2.3

PHP: 5.3.10

I'm guessing you might need more info to help me, so please ask! I'll provide you with whatever you need.

To summarize, I need to know how to get Apache to request an SSL client certificate given the conditions outlined above. Also, if there's any special signing/etc that has to be done to make the client certificate "compatible" with the server certificate (again, WITHOUT doing so manually via shell for each client cert!), I'll need to know that as well.

I am 100% stuck on this as of now. Can't find anything even remotely helpful on Google. Any help you can provide on this would be TREMENDOUSLY appreciated!! Thanks! =)

like image 911
Kris Craig Avatar asked Apr 20 '12 19:04

Kris Craig


People also ask

How do I send a client certificate in HTTP request?

The client certificate is sent during the TLS handshake when establishing a connection and can't be sent via HTTP within that connection. The communication is layered like this: HTTP (application-layer protocol) within. TLS (presentation-layer protocol) within.

Why is my SSL certificate not working?

The most common cause of a "certificate not trusted" error is that the certificate installation was not properly completed on the server (or servers) hosting the site. Use our SSL Certificate tester to check for this issue. In the tester, an incomplete installation shows one certificate file and a broken red chain.

What module does Apache need to support SSL?

mod_ssl is an Apache module that adds “secure sockets layer” (ssl) and “transport layer security” (tls) between a web server and it's clients (web browsers).


1 Answers

First, you need to configure Apache Httpd to request a client certificate. For this, you need at least to use SSLVerifyClient optional on the location/directory you want to be authenticated with this method.

Secondly, the certificates sent by the client needs to be trusted by Apache Httpd too. (You could in principle use SSLVerifyClient optional_no_ca, let any client cert through at the Apache Httpd SSL/TLS stack, and only then verify the certificate within PHP, but that's quite a bit of work, for which you need be a bit more careful since that's not necessarily easy code; more importantly, this would be quite useless in this context, since you're in a scenario where you're in control of your CA.)

As far as I understand, SSL_CLIENT_VERIFY (a variable that I haven't used much myself) seems only really useful with the SSLVerifyClient optional_no_ca. It might work with SSLVerifyClient optional, but I doubt so. SSLVerifyClient require will reject connections using a client certificate that is not trusted (by one of the CAs in SSLCACertificateFile/SSLCACertificatePath), or if there is no certificate. As far as I know, SSLVerifyClient optional will let the client through without a certificate or with a trusted certificate, but will also reject the connection if the certificate is not trusted.

Here, by rejecting the connection, I mean closing the SSL/TLS connection abruptly with an alert. There is no chance to produce an HTTP(S) error page. All you'll get in the standard browser error, something along the lines of ssl_error_unknown_certificate_.... (You should consider this in terms of usability.)

From then onwards, what you need is to set up your own CA, possibly web-based with in-browser key-generation and within the same website. You wouldn't want SSLVerifyClient require for that, because you would need to let the users who don't have a certificate yet in (use optional instead). This being said, these directives need not apply to the entire host, but can be specific to certain locations/directories.

Integrating your own web-based CA (or more generally, creating your own CA) isn't necessarily easy if you're new to all this. Ready-made tools exist (e.g. OpenCA), or you can build your own using various bits of JavaScript/ActiveX, and you would need the server-side code to handle the SPKAC or PKCS#10 requests (and to issue the actual certificate). (For such a CA to be useful, you'd want the users who apply for a new certificate to provide some proof of ID at the time of application, perhaps a password.)

When this is set up, you should configure SSLCACertificateFile (or ...Path) to point to the CA certificate of your internal CA (whether it's a web-based CA or not, on the same site or not). (Of course, keep the private key of your CA private, perhaps configured within your CA web-based application, but Apache Httpd itself doesn't need to know about it.) Browsers will only suggest certificates issued by those CAs or intermediates (unless you've also configured SSLCADNRequestFile, which would be used to send the list of accepted CAs instead).

Note that these two steps (setting up your CA and setting up your website to use client-certificates) really are independent in fact. The fact that both can be part of the same site can be convenient, but isn't necessary. You could try out the Apache Httpd set up without deploying an entire CA on the site first (I'd recommend that, even if it's just to see what you're getting into). There are a number of tools to create your own little CA that are manageable with a handful of certificates: OpenSSL's CA.pl or TinyCA for example. You could also use these test certificates (localhost and testclient, testclient_r is revoked if you want to use the CRL, probably not necessary at first): all passwords are testtest.

As you've already anticipated (with your MySQL DB), you'll need to manage the certificates you issue and map them to users. SSL_CLIENT_M_SERIAL and SSL_CLIENT_I_DN are not the right variables to use, though. SSL_CLIENT_I_DN is the Issuer DN (i.e. the CA's Subject DN). What you'd be looking for is SSL_CLIENT_S_DN: the client cert Subject DN. SSL_CLIENT_M_SERIAL is the certificate serial number: don't use it, since it's unique per certificate: one user could have multiple certificates with the same Subject DN (e.g. if one expires or is revoked).


Despite all this, I'm not sure whether client-certificates are the best way to achieve your goal (letting the employees in your company log on without password).

  • Firstly, the user should protect their own certificates with a password anyway. What you're really after is some form of Single-Sign On (SSO), I guess.

  • Secondly, depending on the degree of computer-literacy of your users, certificates can actually be quite difficult to manage.

    The fact that the word "certificate", strictly speaking, doesn't include the private key at all, but sometimes implies usage of the private key can be confusing for some. On the one hand, you sometimes hear "Import your certificate into your browser" and "Use your certificate to log in"; on the other hand, you can also hear "send me your certificate". The former implies usage and availability of the private key ("certificate" might just mean .p12 in these expressions). The latter definitely shouldn't involve the private key.

    Browser user interfaces tend to be quite poor or confusing for managing the certificates or logging out. Again, if the certificate isn't recognised, the SSL/TLS connection will not be established, so the web server doesn't get a chance to display an HTML error page of any sort.

Perhaps you could also consider other forms of SSO (e.g. CAS, something SAML-based or Kerberos/SPNEGO.)

like image 58
Bruno Avatar answered Oct 05 '22 23:10

Bruno