HTTPS Using Server-Client Certificate Pair (1): Generate & Sign by OpenSSL
Multiple command lines in the process of generating certificates using openssl
can be quite confusing and easily mixed up over which-do-what. Most of them are repetitions of almost the same syntax (where the confusion comes).
Background:
I need to setup an HTTPS site with not just server certificate to secure it, but requiring also client side certificate. The site will only show the content to authorized users with the correct pair of server-client certificate. It will also expire after a certain date. The certificates are self-signed as they’re for closed environment usage.
This post covers two general processes: generating and signing.
How to generate SSL certificate using openssl
is a straightforward process of:
- generate its key
- create certificate request with that key
- generate certificate from request and key
Hence, in any type of the certificate I have a general <some-cert-key>.key
, <some-cert-request>.csr
, and <some-cert>.crt
. When I mean “type”, they are CA (Certificate Authority), one/more server certificate, and one/more client certificate.
In terms of signing the certificates, all of them are signed using the CA. Which files to be used in the server will become the subject of the next post.
openssl
will run interactively. To go through all the recurring questions using prepared default answers, we need to create a config file first. I created caconfig.cnf
(find it on the bottom of the post) and use -config caconfig.cnf
option in some commands.
First, prepare set of directories to clearly separate what we’re working on (keys, requests, and resulting certificates in different places for server and user):
mkdir certs mkdir private mkdir server mkdir server/certs mkdir server/creqs mkdir server/ckeys mkdir user mkdir user/certs mkdir user/creqs mkdir user/ckeys
Prepare “database” and index number to keep track of certificates issued:
echo 01 > serial touch index.txt
Generate the CA: (again) generate key, create request (the interactive part), and create certificate:
openssl genrsa -out private/myCA.key 2048 openssl req -new -key private/myCA.key -out certs/myCA.csr -config caconfig.cnf openssl req -x509 -days 365 -in certs/myCA.csr -out certs/myCA.crt -key private/myCA.key
We can always check:
openssl x509 -in certs/myCA.crt -text
Now, generating for the server, I use the name lakmus
as an example. First the key:
openssl genrsa -des3 -out server/ckeys/lakmus.key 2048
(Triple-DES cipher will ask for pass phrase of 4 characters minimum)
Enter pass phrase for server/ckeys/lakmus.key: Verifying - Enter pass phrase for server/ckeys/lakmus.key:
Then, the request (which will ask for the above key pass phrase):
openssl req -new -key server/ckeys/lakmus.key -out server/creqs/lakmus.csr -config caconfig.cnf
Note that organizationName
field needs to be the same with the CA certificate.
Then, signing it with the CA (and again check as text):
openssl ca -days 365 -in server/creqs/lakmus.csr -cert certs/myCA.crt -out server/certs/lakmus.crt -keyfile private/myCA.key -config caconfig.cnf openssl x509 -in server/certs/lakmus.crt -text
During signing we’ll see something like (expiry date and validity periode)
Certificate is to be certified until <some date> (365 days) Sign the certificate? [y/n]:
and index.txt
is updated:
V 140123034822Z 01 unknown /C=ID/ST=WEST JAVA/O=My Organization/CN=lakm.us V 140123041441Z 02 unknown /C=ID/ST=WEST JAVA/O=My Organization/CN=client
Finally, the same generate with own key and sign with CA except this time is for client:
openssl genrsa -des3 -out user/ckeys/client1.key 2048 openssl req -new -key user/ckeys/client1.key -out user/creqs/client1.csr -config caconfig.cnf openssl ca -in user/creqs/client1.csr -cert certs/myCA.crt -keyfile private/myCA.key -out user/certs/client1.crt -config caconfig.cnf openssl x509 -in user/certs/client1.crt -text
For the client certificate to be usable when importing to browser, convert it to PKCS 12
openssl pkcs12 -export -clcerts -in user/certs/client1.crt -inkey user/ckeys/client1.key -out user/certs/client1.p12
It will ask for pass phrase and export password (that will be prompted when importing to browser)
Enter pass phrase for user/ckeys/client1.key: Enter Export Password: Verifying - Enter Export Password:
If the CA generation already worked smoothly, it is better to remove the key, request, and certificate files of subsequent signing process before repeating them when any error is found. Otherwise it will finally show error such as:
failed to update database
TXT_DB error number 2
Or something like
No certificate matches private key
when exporting to PKCS 12.
We can check the index.txt
, index.old
, serial
, and serial.old
to figure our situation by evaluating indexes of signed certificate.
I used parts from Code Ghar post for my caconfig.cnf
:
[ ca ] default_ca = CA_default [ CA_default ] dir = /home/arif/ssl serial = $dir/serial database = $dir/index.txt new_certs_dir = $dir/certs certificate = $dir/certs/myCAcert.crt private_key = $dir/private/myCA.key default_days = 365 default_md = md5 preserve = no email_in_dn = no nameopt = default_ca certopt = default_ca policy = policy_match [ policy_match ] countryName = match stateOrProvinceName = match organizationName = match organizationalUnitName = optional commonName = supplied emailAddress = optional [ req ] default_bits = 2048 # Size of keys default_keyfile = key.pem # name of generated keys default_md = md5 # message digest algorithm string_mask = nombstr # permitted characters distinguished_name = req_distinguished_name [ req_distinguished_name ] # Variable name Prompt string #------------------------- ---------------------------------- 0.organizationName = Organization Name (company) organizationalUnitName = Organizational Unit Name (department, division) emailAddress = Email Address emailAddress_max = 40 localityName = Locality Name (city, district) stateOrProvinceName = State or Province Name (full name) countryName = Country Name (2 letter code) countryName_min = 2 countryName_max = 2 commonName = Common Name (hostname, IP, or your name) commonName_max = 64 # Default values for the above, for consistency and less typing. # Variable name Value #------------------------ ------------------------------ 0.organizationName_default = My Organization localityName_default = BOGOR stateOrProvinceName_default = WEST JAVA countryName_default = ID emailAddress_default = fake@lakm.us commonName_default = lakm.us
4 thoughts on “HTTPS Using Server-Client Certificate Pair (1): Generate & Sign by OpenSSL”