Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Mini tutorial for configuring client-side SSL certificates.

Client-side SSL

For excessively paranoid client authentication.


Updated Apr 5 2019:

because this is a gist from 2011 that people stumble into and maybe you should AES instead of 3DES in the year of our lord 2019.

some other notes:

I've noticed that across platforms, some browsers/devices like like PFX bundles, others like PEMs, some things will import ECC certs just fine but fail to list them in the "select certificate" menu when the server wants it. Server-side stuff seems good, with most things supporting ECC, but clients are a crapshoot. I'd say unless you've got some time to experiment, you may want to stick to RSA.

(In my own dev servers i just ended up configuring both an RSA CA and an ECC CA and using them both on the server, and provisioning one of each type for each client and trying them both. if, like nginx, your server only lets you use one CA cert root, you can concatenate multiple CA PEMs together and then use that combined file.)


Using self-signed certificate.

Create a Certificate Authority root

This'll represent you / your org / your server -- basically the thing that vouches for the validity of a key.

###### PICK ONE OF THE TWO FOLLOWING ######

# OPTION ONE: RSA key. these are very well-supported around the internet.
# you can swap out 4096 for whatever RSA key size you want. this'll generate a key
# with password "xxxx" and then turn around and re-export it without a password,
# because genrsa doesn't work without a password of at least 4 characters.
#
# some appliance hardware only works w/2048 so if you're doing IOT keep that in
# mind as you generate CA and client keys. i've found that frirefox & chrome will
# happily work with stuff in the bigger 8192 ranges, but doing that vs sticking with
# 4096 doesn't buy you that much extra practical security anyway.

openssl genrsa -aes256 -passout pass:xxxx -out ca.pass.key 4096
openssl rsa -passin pass:xxxx -in ca.pass.key -out ca.key
rm ca.pass.key

# OPTION TWO: make an elliptic curve-based key.
# support for ECC varies widely, and support for the predefined curves also varies.
# it's "secp256r1" in this case, which is as well-supported as it gets but if you want to
# avoid NIST-provided things, or if you want to go with bigger/newer keys, you can
# swap that out:
#
# * check your openssl supported curves: `openssl ecparam -list_curves`
# * check client support for whatever browser/language/system/device you want to use:
#      https://en.wikipedia.org/wiki/Comparison_of_TLS_implementations#Supported_elliptic_curves

openssl ecparam -genkey -name secp256r1 | openssl ec -out ca.key

###### END  "PICK ONE" SECTION ######

# whichever you picked, you should now have a `ca.key` file.

# now generate the CA root cert
# when prompted, use whatever you'd like, but i'd recommend some human-readable Organization
# and Common Name.
openssl req -new -x509 -days 3650 -key ca.key -out ca.pem

Create the Client Key and CSR

# client_id is *only* for the output filenames
# incrementing the serial number is important
CLIENT_ID="01-alice"
CLIENT_SERIAL=01

###### PICK ONE OF THE TWO FOLLOWING ######
###### (instrux in the CA section above) ######
# rsa
openssl genrsa -aes256 -passout pass:xxxx -out ${CLIENT_ID}.pass.key 4096
openssl rsa -passin pass:xxxx -in ${CLIENT_ID}.pass.key -out ${CLIENT_ID}.key
rm ${CLIENT_ID}.pass.key
# ec
openssl ecparam -genkey -name secp256r1 | openssl ec -out ${CLIENT_ID}.key
###### END  "PICK ONE" SECTION ######

# whichever you picked, you should now have a `client.key` file.

# generate the CSR
# i think the Common Name is the only important thing here. think of it like
# a display name or login.
openssl req -new -key ${CLIENT_ID}.key -out ${CLIENT_ID}.csr

# issue this certificate, signed by the CA root we made in the previous section
openssl x509 -req -days 3650 -in ${CLIENT_ID}.csr -CA ca.pem -CAkey ca.key -set_serial ${CLIENT_SERIAL} -out ${CLIENT_ID}.pem

Bundle the private key & cert for end-user client use

basically https://www.digicert.com/ssl-support/pem-ssl-creation.htm , with the entire trust chain

cat ${CLIENT_ID}.key ${CLIENT_ID}.pem ca.pem > ${CLIENT_ID}.full.pem

Bundle client key into a PFX file

Most browsers will happily use this if they don't like the raw ascii PEM file. You'll possibly need to set a password here, which you'll need on the browser/client end when you import the key+cert PFX bundle.

openssl pkcs12 -export -out ${CLIENT_ID}.full.pfx -inkey ${CLIENT_ID}.key -in ${CLIENT_ID}.pem -certfile ca.pem

Install Client Key on client device (OS or browser)

Use client.full.pfx (most commonly accepted in GUI apps) and/or client.full.pem. Actual instructions vary.

Install CA cert on nginx

So that the Web server knows to ask for (and validate) a user's Client Key against the internal CA certificate.

ssl_client_certificate /path/to/ca.pem;
ssl_verify_client optional; # or `on` if you require client key

Configure nginx to pass the authentication data to the backend application:

See also:

Using CACert Keys

(removed)

@arr2036

This comment has been minimized.

Copy link

arr2036 commented Nov 4, 2012

Top tip for those signing client certs with an intermediary CA, you must cat rootca.pem >> intermediaryca.pem else nginx will return a very unhelpful 400 error.

@jacobbrunson

This comment has been minimized.

Copy link

jacobbrunson commented Aug 3, 2013

Thank you, this helped me after a lot of searching.

@lucdig

This comment has been minimized.

Copy link

lucdig commented Mar 26, 2015

Hello, I use Nginx as reverse http proxy to more than one web servers. I need to configure the client certificate-based authentication only for some locations, not all the locations configured in nginx.

How can I do?
Can I put the lines:

ssl_client_certificate /path/to/ca.crt;
ssl_verify_client optional;

inside the "location" definition?

Thnks a lot, regards
Luca

@justabaka

This comment has been minimized.

Copy link

justabaka commented Mar 31, 2015

@lucdig, those lines belong to the server block along wih another SSL directives: https://gist.github.com/konklone/6532544

@jhmartin

This comment has been minimized.

Copy link

jhmartin commented May 11, 2015

@lucdig SSL handshaking occurs prior to the server knowing any details about the request, so it will be a global rather than specific to a particular location.

@developerworks

This comment has been minimized.

Copy link

developerworks commented Oct 8, 2015

Issues:

  1. One client certificate could be used for multiple users ? ( Distribute the client certificate to multiple user)
  2. If i want to let every user to use unique client certificate, I have to generate a client certificate for every user ?
@risacher

This comment has been minimized.

Copy link

risacher commented Nov 12, 2015

@lucdig @jhmartin RFC 5746 specifies extensions to TLS that allow secure renegotiation, so it's not quite correct to say that requiring client certs must happen at the global level. Apache, for example can require client certs on a per-location basis. That said, nginx does not, and will not for the foreseeable future. The feature has been previously requested and categorized as 'wontfix'; see https://trac.nginx.org/nginx/ticket/317 and https://trac.nginx.org/nginx/ticket/498

@jacobalberty

This comment has been minimized.

Copy link

jacobalberty commented Nov 16, 2015

@risacher @lucdig @jhmartin it is however pretty easy to simply set ssl verification to optional and then just error unauthenticated users under certain locations while allowing them anywhere else.

@ddacunha

This comment has been minimized.

Copy link

ddacunha commented Oct 14, 2016

Thanks for the instructions.
I had to use client.pem for it to work.

ssl_client_certificate /path/to/client.pem;
ssl_verify_client optional; # or `on` if you require client key
@fercho-ayala

This comment has been minimized.

Copy link

fercho-ayala commented Dec 2, 2016

It works as expected. I used client.pfx instead of client.p12. Also, I used this with a Dropwizard framework, I generated a .jks file from .pfx file using the next command:

keytool -importkeystore -srckeystore client.pfx \
        -srcstoretype PKCS12 \
        -destkeystore client.jks \
        -deststoretype JKS
@michelde

This comment has been minimized.

Copy link

michelde commented Jan 7, 2017

Thank you, this helped me after a lot of searching.

@RSchlenker

This comment has been minimized.

Copy link

RSchlenker commented Jan 16, 2017

Hey guys,
I would like to repeat the question asked by @developerworks
Is it possible to have different client certificates for one and the same server certificate? They could get for example a different CN to identify each user respectively.
Is a setup like this possible?
Kind regards,
RSchlenker

@jchevali

This comment has been minimized.

Copy link

jchevali commented Mar 17, 2017

Thank you, this helped me after a lot of searching.

@GuidoThomassen

This comment has been minimized.

Copy link

GuidoThomassen commented May 19, 2017

Thanks, this was very useful! 👍

@prbreezy

This comment has been minimized.

Copy link

prbreezy commented Jun 14, 2017

Works like a charm!

@NeetishPathak

This comment has been minimized.

Copy link

NeetishPathak commented Jun 21, 2017

what's the openssl API to ask for client side certificates from the server application. As I understand, client side authentication will not take place until server explicitly asks for it using the CertificateRequest message

@josejaguirre

This comment has been minimized.

Copy link

josejaguirre commented Aug 23, 2017

You are awesome 👍

@storrgie

This comment has been minimized.

Copy link

storrgie commented Oct 29, 2017

@jacobalberty do you have an example of how to check verification in locations with nginx?

@jango

This comment has been minimized.

Copy link

jango commented Nov 5, 2017

Here is a helpful command to figure out if the client certificate will verify correctly: openssl verify -verbose -CAfile ca.crt client.crt

Also, if you get error 18 at 0 depth lookup: self signed certificate it's due to the fact that you use the same Organization Name for both your ca and your client certificate.

@mr-m0nst3r

This comment has been minimized.

Copy link

mr-m0nst3r commented Aug 21, 2018

Hi there, what if I want to use Burpsuite to proxy the app's traffic which is using two way of https auth?

I can get client.p12 and trust.bks from the apk files, but can't import the client.p12 into Burpsuite.

  • I got "unable to retrive certificate" error when adding Client SSL Certificate to burp's user option.
  • I got "java.util.NoSuchElementException" error when import CA certificate to burp's proxy listener
  • For the above actions, I'm just using the client.p12 exported from jeb 2.2.5

Any guidance?

@chobits

This comment has been minimized.

Copy link

chobits commented Mar 6, 2019

hi @storrgie

@jacobalberty do you have an example of how to check verification in locations with nginx?

See https://serverfault.com/a/639249/188143 and https://stackoverflow.com/a/36067338/1009249

@phi1ipp

This comment has been minimized.

Copy link

phi1ipp commented Apr 3, 2019

Client Side Certificate Auth in Nginx, section “Passing to PHP.”

This link gives 404

@gruhby

This comment has been minimized.

Copy link

gruhby commented Apr 16, 2019

Thank you for this tutorial. In Create a Certificate Authority root you write "because genrsa doesn't work without a password of at least 4 characters". That's not exactly true (although I thought this also for a while).

What needs the password is the -aes256 parameter, that leads to an encrypted key. If you leave this parameter out, you get an unencrypted key the same way you get after using the 2nd command (i.e. openssl rsa -passin pass:xxxx -in ca.pass.key -out ca.key).

@PaulAndersonMule

This comment has been minimized.

Copy link

PaulAndersonMule commented Apr 24, 2019

concise and helpful. Right to the point. Many thanks for this

@gadzhikuliev

This comment has been minimized.

Copy link

gadzhikuliev commented Apr 26, 2019

Thank you so much!

@CassianoSF

This comment has been minimized.

Copy link

CassianoSF commented May 24, 2019

I'am trying to connect to a MariaDB instance hosted on AWS RDS, my application requires ca.pem, client-cert.pem and client-key.pem, and Amazon only gives me a rds-combined-ca-bundle.pem

I don't know much about TLS. I need to generate those files?

When I follow the Create the Client Key and CSR it outputs:
CA certificate and CA private key do not match

${CLIENT_ID}.key is equivalent to client-key.pem?
${CLIENT_ID}.csr is equivalent to client-cert.pem?

Any one can give me a light?

@lksilesian

This comment has been minimized.

Copy link

lksilesian commented Aug 22, 2019

It needs a lot of time spent + expertise to be able to tackle something as cryptic (for me) as creating own chain of certificates and present it in such a plain way :) thanks for the hard work - worked like a charm for internal UCP

@brunogabuzomeu

This comment has been minimized.

Copy link

brunogabuzomeu commented Oct 1, 2019

Helpfull, thanks.

@Diaoul

This comment has been minimized.

Copy link

Diaoul commented Oct 11, 2019

I suggest using -CAserial file instead of -set_serial which will eventually get out of sync.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.