OpenSSL demystified
Perhaps I am all too stupid, but it took me several years(!) to understand how to do a following quite simple thing. Yes, that's true. Well, I did not dig deeply in OpenSSL over those years, nor put I high effort in it, but I steadily wondered over those years how to do this what you find on this pages:
Create and sign Keys with OpenSSL
Here are the scripts you can use to do following:
- Create self signed keys.
- Create signed keys based on other keys.
Yes, this all really is very simple, indeed, if you first found out, how to use the OpenSSL commands. However I did not find any trace or help for this, so I found out the hard way, by trial and error, over the last couple of years (not kidding).
Create a self signed key
#!/bin/bash
set -e
if [ -z "$1" ]
then
echo "Usage: `basename "$0"` filename [bits]"
echo "Create a self signed certificate. CN is set to filename."
exit 1
fi
if [ -f "$1.key" -o -f "$1.pem" -o -f "$1.pub" ]
then
echo "already exists: $1"
exit 1
fi
openssl genrsa -out "$1.key" "${2:-1024}"
openssl req -subj "/CN=$1" -new -key "$1.key" |
openssl x509 -req -days 9999 -signkey "$1.key" -out "$1.pub"
cat "$1.pub" "$1.key" > "$1.pem"
echo "private: key $1.key certificate $1.pem"
echo "public: key $1.pub ($1.pub also is the certificate)"
Example
If you stored this script as .self in some directory, then you can do:
./.self root 4096
This creates the files "root.pem" (private) and "root.pub" (public) and uses 4096 bits for the key. There also is "root.key", which only is the RSA key.
Notes:
Create a signed key
#!/bin/bash
set -e
if [ -z "$1" ]
then
echo "Usage: `basename "$0"` key filename [bits]"
echo "Create a signed certificate in filename. CN is set to filename."
exit 1
fi
if [ -f "$2.key" -o -f "$2.crt" -o -f "$2.pem" -o -f "$2.pub" ]
then
echo "already exists: $2"
exit 1
fi
ca="${1%.pem}"
ca="${ca%.}"
if [ ! -f "$ca.pem" -o ! -f "$ca.pub" ]
then
echo "missing $ca.pem"
exit 1
fi
openssl genrsa -out "$2.key" "${3:-1024}"
openssl req -subj "/CN=$2" -new -key "$2.key" |
openssl x509 -req -days 9999 -CA "$ca.pem" -CAserial "$ca.srl" -CAcreateserial -out "$2.crt"
if [ -n "$2.crt" ]
then
cat "$2.crt" "$2.key" "$ca.pub" > "$2.pem"
cat "$2.crt" "$ca.pub" > "$2.pub"
fi
echo "private: key $2.key certificate $2.pem"
echo "public: key $2.pub certificate $2.crt"
Example
If you stored this script as .sign in some directory, then you can do:
./.sign root key1
This creates the files "key1.pem" (private) and "key1.pub" (public) based on the previously created self signed key. There also is "key1.key", which only is the RSA key, and "key1.crt", which is the public certificate with all needed intermediate certificates for the trust chain.
Notes:
- To sign other keys the trust chain must be intact.
- All "*.pem" files created by the scripts on this page have an intact trust chain.
- You can sign keys using any other keys. So you can sign a key using key1, too!
- I don't know how to sign a key by more than one key. (Is this possible with x509?)
- No complicated things like revocations etc.
- To create a certificate for www.domain.tld, just name the file www.domain.tld!
List the contents
#!/bin/bash
in="${1%.pem}"
in="${in%.}"
openssl x509 -noout -text -in "$in.pem"
openssl verify -CAfile "$in.pem" "$in.pem"
Example
If you stored this script as .list in some directory, then you can do:
./.list key1
This shows the contents of the keys. Please notify the CN subject which contains the file name. The "issuer" is the key used to sign these here.
Create a mini CA for some simple authentication purpose
Think about following:
- You have a laptop with CygWin installed, which provides OpenSSL.
- You have a USB stick on which the CA directory resides.
Then you have a simple CA. Here are the steps to create a service which uses private key files to authenticate service and clients to this CA.
Step 1: Create the CAroot
./.self root1 4096
Step 2: Create the key for the organization
./.sign root1 master1 2048
You can hand out master1.pem to others, as it contains everything suitable for another not self-signed root.
But note that this is really a poor thing, as you then know the key, as you created it. However there are a lot of such type services on the Internet (they create the key and issue the certificate). While in Internet you will not have any trust in those services (and therefor such certificates are just unusable), on an organizational unit you ususally do not need a higher trust level.
Step 3: Create the service certificate
./.sign master1 service1
Step 4: Create the server's certificate
./.sign service1 server1
Use server1.pem for the server to authenticate the clients! If the service has a domain, call the certificate after the domain (like
www.intranet.example.com).
If the server shall only authenticate clients which are signed by service1, then use server1.crt instead and use service1.crt as the CA's key.
Step 5: Create a local service client
./.sign service1 client1
Hand out the client1.pem to the client to authenticate to all certificates of service1. As the service1 key was used to sign the client, the client is able to authenticate to the service1 only. (Do not mix this with server1, as you can have more than one server!)
Please note that the client is able to authenticate the service, too, as the client has the trust chain. If it only shall be able to authenticate to service1 (do not mix that with server1), then you can do:
cat client1.crt client1.key service1.crt > client1.service1.pem
This way the client is not able to accidentally authenticate certificates from another branch of the PKI.
Step 6: Create a global service client
./.sign master1 global1
This creates a client certificate which can be used at all servers, which are able to authenticate the master1 branch. For this server1 must use the .pem format, else it is lacking the key to verify global1.
Missing things
- Restrict usage: OpenSSL can restrict usage of a certificate, such that you cannot use the certificate for signing of other certificates anymore. This probably is a good thing for server1, client1 and global1, as not having this can be considered a security flaw.
- Revoke a certificate: Currently there is no such thing as revocation. If you want to revoke, you must create a new signing key, in this example at master1 level. This needs re-issuing of all keys below, which can be considered inpractical. Also unsolved: How to distribute revocation information easily? (wget -mirror shall be enough.)
- Request handling, such that the signer does not need to know the key.
Also missing
PKI implementation for dummies. Note that I already have a project for this:
www.caroot.org
That is:
- Software to install for People who do not want to know what PKI is. Best: Not needed to install, as it runs directly from a web page (Java, Flash/Shockwave, Python, Perl based).
- Create certificates for signing keys, clients, services, eMail.
- Create signing requests.
- Process signing requests.
So still a lot to do.
Usage example: socat tunnel using certificates
See also:
permalink.de/tino/socat
./.self example1 4096
./.sign example1 socat-server
./.sign example1 socat-client
Copy socat-server.pem to the server and socat-client.pem to the client, keep the rest secret.
On the server run:
socat -d -d openssl-listen:9999,reuseaddr,fork,cert=socat-server.pem,cafile=socat-server.pem,verify=1,cipher=HIGH:3DES:MD5 TCP4:127.0.0.1:9998
This assumes that some (nonpublic!) service is listening on 127.0.0.1:9998 on the server.
On the client run:
socat -d -d tcp-listen:9998,reuseaddr,bind=127.0.0.1,fork openssl:server.domain.tld:9999,cert=socat-client.pem,cafile=socat-client.pem,verify=1
where server.domain.tld is the name (or IP) of the server which provides the (nonpublic) service.
Then you have an encrypted tunnel from Port 127.0.0.1:9998 on the client to Port 127.0.0.1:9998 on the server, therefor you can access this port on the client as if you were on the server (provided the client is able to connect to the server on the transfer port 9999 in the example) and you do not worry about eavesdroppers.
Also note that this example does not rely on IPs, so server IP and client IP can change. Authentication is based soley on the PKI.
-Tino, 2007-05-15, Update 2008-11-06: set -e added
Usage example: Self signed SSL certificate
(This is untested)
./.self myroot 4096
./.sign myroot `hostname`
Note: Replace `hostname` with the FQDN of your host if you want the certificate to cover your FQND
- You will then find a .crt file which is named after your hostname.
- You can give the certificate to your web server.
Troubleshooting
OpenSSL needs a config file
You know you have this error if you see output like
Unable to load config info from /opt/openssl/openssl.cnf
About
- OpenSSL needs a config file to learn what to request on the "req" command
- Usually (as some ordinary user) you cannot the config at the default location
- Instead give OpenSSL the path to a config file (option -config)
- This config file must be populated with everything OpenSSL expects
First: Create file openssl.cnf
The file openssl.cnf (in the same directory where you execute the scripts) must look like:
[req]
default_bits=1024
distinguished_name=req_distinguished_name
[req_distinguished_name]
commonName="CN"
Change the scripts as follows
./.self
#!/bin/bash
set -e
if [ -z "$1" ]
then
echo "Usage: `basename "$0"` filename [bits]"
echo "Create a self signed certificate. CN is set to filename."
exit 1
fi
if [ -f "$1.key" -o -f "$1.pem" -o -f "$1.pub" ]
then
echo "already exists: $1"
exit 1
fi
openssl genrsa -out "$1.key" "${2:-1024}"
openssl req -config openssl.cnf -subj "/CN=$1" -new -key "$1.key" |
openssl x509 -req -days 9999 -signkey "$1.key" -out "$1.pub"
cat "$1.pub" "$1.key" > "$1.pem"
echo "private: key $1.key certificate $1.pem"
echo "public: key $1.pub ($1.pub also is the certificate)"
./.sign
#!/bin/bash
set -e
if [ -z "$1" ]
then
echo "Usage: `basename "$0"` key filename [bits]"
echo "Create a signed certificate in filename. CN is set to filename."
exit 1
fi
if [ -f "$2.key" -o -f "$2.crt" -o -f "$2.pem" -o -f "$2.pub" ]
then
echo "already exists: $2"
exit 1
fi
ca="${1%.pem}"
ca="${ca%.}"
if [ ! -f "$ca.pem" -o ! -f "$ca.pub" ]
then
echo "missing $ca.pem"
exit 1
fi
openssl genrsa -out "$2.key" "${3:-1024}"
openssl req -config openssl.cnf -subj "/CN=$2" -new -key "$2.key" |
openssl x509 -req -days 9999 -CA "$ca.pem" -CAserial "$ca.srl" -CAcreateserial -out "$2.crt"
if [ -n "$2.crt" ]
then
cat "$2.crt" "$2.key" "$ca.pub" > "$2.pem"
cat "$2.crt" "$ca.pub" > "$2.pub"
fi
echo "private: key $2.key certificate $2.pem"
echo "public: key $2.pub certificate $2.crt"
-Tino, 2008-11-06