Suppose I have a site that I want users to be able to log into with client certificates. As I understand it, the client is presenting the site with the public half of a keypair and proving that they have the corresponding private half. Is that correct? If so, then isn't checking that a client is presenting a known (previously-authorized) public key sufficient to know that it's a known user, without the certificate having been signed by a trusted CA?
Ssl – Do I really need client certificates to be signed by a trusted CA
certificatesslssl-certificate
Related Solutions
As you've found, you can disable the certificate verification at the SSL/TLS handshake level within Apache Httpd by using SSLVerifyCLient optional_no_ca
.
The second problem you're going to face with what you're trying to do is to get the client to send the certificate. Since your certificate are not intended to be within a PKI, they could be self-signed and have various issuers.
When requesting a client-certificate, the server sends a CertificateRequest
TLS message to the client during the handhsake. This message contains the certificate_authorities
list:
A list of the distinguished names of acceptable certificate authorities. These distinguished names may specify a desired distinguished name for a root CA or for a subordinate CA; thus, this message can be used to describe both known roots and a desired authorization space. If the certificate_authorities list is empty then the client MAY send any certificate of the appropriate ClientCertificateType, unless there is some external arrangement to the contrary.
Browsers use this to choose which client certificate to send (if any).
(Note that the part about the empty list is only in the specification from TLS 1.1 onwards. SSL 3.0 and TLS 1.0 are silent on this, and in practice, it will also work.)
You get two options for this.
If the client certificates you expect are going to be self-signed, they will all have different issuers. Because you won't know what to expect, the server will need to send an empty list. To do this, use the
SSLCADNRequestFile
directive and point it to a file that contains just an empty line (if I remember well, it doesn't work with a completely empty file).The second (less clean) option. Is to agree on an Issuer DN common to all the client certificates you expect, whether or not they have indeed been issued by that CA certificate (or whether or not that CA even exists). By doing so, you'd be breaking the PKI model considerably (more).
If you agree on an Issuer DN like
CN=Dummy CA
(for example). Anyone can build a self-signed certificate usingCN=Dummy CA
as Subject DN (and Issuer DN), possibly with different keys. Although theSSLCADNRequestFile
directive expects to be configured with certificates to build the list, these are not used to verify the client-certificate at all, it's just a complicated (but natural in the context of the other directives) way of configuring thecertificate_authorities
list. If you, as a service, puts a self-signed cert with these names inSSLCADNRequestFile
, this will make theCertificateRequest
TLS message useCN=Dummy CA
in thecertificate_authorities
list (these are just names, not certs at this stage). The client will then be able to pick up its own certificate with Issuer DNCN=Dummy CA
, whether or not its signature could be verified by that certificate (same keys) or not, since no signature verification is involved in these steps anyway.
This being said, remember that with SSLVerifyCLient optional_no_ca
, no real certificate verification is made (I suppose you could check the SSL_CLIENT_VERIFY
variable if your manual verification is just a fallback solution to a PKI you have configured anyway).
All you'll know at that stage is that the client has the private key for the public key certificate it has presented (guaranteed by the TLS CertificateVerify
message): you will need to perform some form of verification if you want there to be authentication of some sort. (You can't trust any of the content of the certificate, that is any of the binding between its public key and the names/attributes it contains.)
This won't work well for files, but you can do this for an application (e.g. PHP/CGI/... even Java if you pass the certificate to the proxied Java server). One basic way would be to have a pre-known list of public keys, or you could look at the ideas in FOAF+SSL/WebID.
The signature of the root CA certificates do not matter at all, since there is no need to verify them. They are all self-signed.
If you trust a root CA certificate, there’s no need to verify its signature. If you don’t trust it, its signature is worthless for you.
Edit: there are some very relevant comments below. I don’t feel comfortable copying or rephrasing them and taking credit for them instead of their authors. But I welcome people to add explanations to this answer.
Best Answer
When you use client-certificate authentication, towards the end of the handshake, the client sends a
Certificate Verify
TLS message where it signs with its private key the concatenation of all the TLS messages that have been exchanged between the client and the server: something commonly known by both.This is independent of whether the client-certificate is trusted or not. The server still has verify the signature against the public key presented in the client certificate. If it failed, the handshake would fail.
At the end of the handshake, whether or not the server trusts what the certificate asserts, that is, the binding between the public key, the identifier and various other attributes, it will know at least that the client has the private key for the public key in this certificate (the rest may or may not be true).
If you have a pre-defined list of know public keys (akin to public keys you would set up for an SSH connection, for example), you can perform authentication this way. What you miss out on is the PKI: the whole infrastructure to help you manage the keys and who they belong to. Since most configuration settings are intended for use within a PKI, this may also need more work (including additional programming perhaps).
All the other properties of the TLS connection are intact: the encryption is still guaranteed in the same way as it would be within the context of a PKI. I'm not sure what @WesleyDavid is talking about in his answer on this subject. Anyway, it's about client certificates, so encryption between the client and the server would take place anyway, whether or not a client-certificate is presented (provided a cipher suite with non-null encryption is used, of course).