Is it possible to set something up like SSH Keys for http traffic, so that a browser must have the matching key in order to access?

  • Obviously it would have to be HTTPS, not HTTP, since HTTP doesn't involve any cryptography. Sep 17, 2013 at 22:56

2 Answers 2


What you are looking for is called Mutual SSL Authentication.

In the case of a commonly deployed web server as Apache, and an also commonly deployed SSL/TLS implementation as OpenSSL, the steps would be as follows:

There is a handy script distributed alongside openssl, CA.sh to do most of this stuff. Its location is distribution specific. In Debian and derivatives you can locate it using:

# apt-file search CA.sh
openssl: /usr/lib/ssl/misc/CA.sh

And RedHat and derivatives the (approximate) equivalent is:

# yum provides */CA
1:openssl-1.0.1e-4.fc18.x86_64 : Utilities from the general purpose cryptography library with TLS implementation
Repo        : @updates
Matched from:
Filename    : /etc/pki/tls/misc/CA

This is a very simple bash script that eases the creation of the directory structure needed to manage a CA (this is described in the [ CA_default ] section of openssl.cnf). I recommend you to use it and look at the code to learn what it is actually doing.

# ./CA.sh -help
usage: ./CA.sh -newcert|-newreq|-newreq-nodes|-newca|-sign|-verify

This script will use the defaults provided in openssl.cnf, and/or you can provide one configuration file as an argument to the openssl $command using the -config switch if not using CA.sh. The location of the openssl.cnf file is also distribution specific, and you can use the same commands above to find it. The one you want is the one provided by the openssl package.

You probably want to modify the following sections:

[ CA_default ]
default_days    = 365                   # how long to certify for
default_crl_days= 30                    # how long before next CRL

[ req ]
default_bits            = 2048

[ req_distinguished_name ]
countryName                     = Country Name (2 letter code)
countryName_default             = AU
countryName_min                 = 2
countryName_max                 = 2
stateOrProvinceName             = State or Province Name (full name)
stateOrProvinceName_default     = Some-State
localityName                    = Locality Name (eg, city)
0.organizationName              = Organization Name (eg, company)
0.organizationName_default      = Internet Widgits Pty Ltd
#1.organizationName             = Second Organization Name (eg, company)
#1.organizationName_default     = World Wide Web Pty Ltd
organizationalUnitName          = Organizational Unit Name (eg, section)
#organizationalUnitName_default =
commonName                      = Common Name (e.g. server FQDN or YOUR name)
commonName_max                  = 64
emailAddress                    = Email Address
emailAddress_max                = 64
  • Generate the CA

Once you have edited openssl.cnf to match your needs, you can generate a CA certificate. Depending on whether you need this CA signed by an recognized third-party CA or not, you can generate a self-signed CA or a CSR to be submitted for signing.

./CA.sh -newca

You will be interactively prompted to ask some questions, the defaults will appear between square brackets. You will recognize the options you modified in openssl.cnf here.

  • Generate a certificate for the server

You can create a certificate request for the server using the same script:

./CA.sh -newreq

Again, you will be prompted to ask several questions, the most important is the Common Name of the certificate, which must match the DNS resolvable name for the IP of the server (or you can use other means, like /etc/hosts, not recommended, hard to maintain and scale)

What you will obtain is a Certificate Signing Request (CSR for short). This will be signed by the Certificate Authority (CA) you created before.

./CA.sh -sign

  • Generate certificates for the clients

Repeat the steps of creating a CSR and getting it signed by the CA. When doing so, pay close attention to how you fill in the Common Name, Organization and Organizational Unit fields, as those will be required afterwards in order to configure the server.

A neat way to distribute the client certificates alongside their respective private keys and the CA certificate is using p12 bundles:

openssl pkcs12 -export -in Certificates/client.pem -inkey client.key -certfile CA.pem -out clientcert.p12

  • Configure the server

Let's suppose the server you are referring to is an Apache web server. Once you have the server certificate, you configure the appropriate VHOST to serve whatever content is going to be protected by mutual SSL authentication. An example could be this phpmyadmin virtual hosts. This is working in an Apache 2.4 server, so please don't use it as-is and carefully review and test it to adapt it to your needs.

Listen                   443 https

  DocumentRoot           "/srv/www/html"
  ServerAdmin            [email protected]
  SSLCACertificateFile   /etc/pki/CA/cacert.pem
  SSLCertificateFile     /etc/pki/tls/private/company.com/newcert.pem
  SSLCertificateKeyFile  /etc/pki/tls/private/company.com/newkey.pem
  SSLCARevocationCheck   chain
  SSLCARevocationFile    /etc/pki/CA/crl/crl.pem
  SSLEngine              on
  SSLStrictSNIVHostCheck on
  SSLVerifyClient        require
  SSLVerifyDepth         5
  ServerName             phpmyadmin.company.com
  RewriteEngine          on
  RewriteCond            %{REMOTE_ADDR} !^127\.0\.0\.1$
  RewriteCond            %{HTTPS} !=on
  RewriteRule            . - [F]
  ErrorLog               "|/usr/sbin/rotatelogs -L /var/log/httpd/foo/error.log -f /var/log/httpd/foo/error.log.%Y%m%d 86400"
  CustomLog              "|/usr/sbin/rotatelogs -L /var/log/httpd/foo/access.log -f /var/log/httpd/foo/access.log.%Y%m%d 86400" logstash_json
  <Directory /usr/share/bar/>
    Require              ssl
    Require              ssl-verify-client
    SSLOptions           +FakeBasicAuth +StrictRequire
    SSLRequire           %{SSL_CIPHER_USEKEYSIZE} >= 256
    SSLRequire           %{SSL_CLIENT_S_DN_O} eq "Awesome Company" \
                     and %{SSL_CLIENT_S_DN_OU} eq "Development" \
                     and %{SSL_CLIENT_S_DN_CN} in {"John Doe", "Jane Doe"}
    SSLRenegBufferSize   131072

You can use as many per-directory access control as you need, the important part is that the client certificates shown must comply with the restrictions imposed by the SSLRequire directives, i.e., they must match the Organization, Organizational Unit and Common Name conditions (or other fields of the certificate as you see fit). These fields are taken from the client certificates.

  • Generate a Certificate Revocation List

In order to be able to revoke access to a client certificate, you need to generate a CRL. The command to do it (provided you in the top of the CA directory structure):

openssl ca -config /path/to/openssl.cnf -gencrl -out crl/crl.pem

Afterwards, you revoke client certs as needed using:

openssl ca -config /path/to/openssl.cnf -revoke clientcert.pem


OpenSSL online documentation and Apache online documentation.


Yes, it is called "Client Certificate Authentication" and is oft used with Smart Cards and the like.

It can be complicated to get running well, depending on what your desired product is.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .