Self-signed certificates are useful for testing as well as some limited real-world use cases. The instructions below cover commands necessary to create simple self-signed certificates using either keytool or openssl. keytool, being included with Java JDK installations, is more likely to be available on Windows if OpenJDK or Oracle JDK is installed, however Linux/Unix/OSX users will also typically have the more robust openssl program readily available to them.
Whenever cryptography is involved, you can expect to a deluge of terminology about complex concepts. Fully fleshing out these concepts is beyond the scope of this piece, but it would be beneficial to roughly summarize some of the terms as they apply to the operations used further down:
- Keystore or Truststore – Container file designed to hold cryptographic materials such as keys, certificates, etc.
- PFX – Effectively an alternate name for PKCS12 files. PFX is usually found within Microsoft contexts as PFX was initially a Microsoft format before it eventually became re-defined by RFC 7292 as PKCS12.
- Key Pair – A public key and its corresponding private key together are commonly referred to as a “key pair”.
- Private Key – The secret key of a key pair that should be availble only to the owner or owner programs/processes
- Public Key – The public key of a key pair that is made available to one or more external parties to encrypt data that can (well…should) only be decrypted by someone with access to the corresponding secret key.
- Certificate – A file format (typically X.509) that includes a key (commonly a public key) along with other relevant information such as expiration, issuer, etc.
A Quick Note On Approach
Several best practices are intentionally not followed in the commands below in order to emphasize the overall mechanics. Things like the keystore password being ‘password’, private key password not being set, and passwords included on the command line should not be followed outside an educational exercise context.
As mentioned above, keytool is provided with Java JDK installations. It is capable of generating the necessary self-signed certificate materials however is otherwise fairly limited in its capabilities. For example as of the time of this writing there is no way to export a private key into its own dedicated file using keytool, only to another keystore container file. Another item to note is that prior to Java 9 the default keystore container format for keytool was JKS, which is a Java-specific format which limits its use by other software and tools. The examples below instruct keytool to use the more widely supported PKCS12 container format instead.
Keytool primarily deals with keystores, so the approach followed below is to simultaneously generate a new keypair and store it in a new keystore, then afterwards export the public certificate to its own file.
Create PKCS12 keystore container
keytool \ -genkeypair \ -ext san=dns:localhost \ -storetype PKCS12 \ -keystore server.p12 \ -storepass password \ -alias myalias \ -keyalg RSA \ -keysize 4096 \ -dname "C=,S=,L=,O=,OU=,CN=localhost"
Export the certificate
keytool \ -exportcert \ -rfc \ -keystore server.p12 \ -storepass password \ -alias myalias \ -file server.public.crt
(Optional) Examine the keystore
keytool \ -list \ -v \ -storetype PKCS12 \ -storepass password \ -keystore server.p12
OpenSSL approaches the creation and storage of cryptographic materials in a different sequence than keytool. In this case we will create the individual components (the private key and public cert) then package them up after into a PKCS12 file.
If you create a certificate the way you see on most posts, you may get a warning from some clients similar to the following from python3:
SubjectAltNameWarning: Certificate for localhost has no `subjectAltName`, falling back to check for a `commonName` for now. This feature is being removed by major browsers and deprecated by RFC 2818. (See https://github.com/shazow/urllib3/issues/497 for details.)
As the warning indicates there is no subjectAltName field in the certificate. To do so requires either modifying the system openssl.cnf (or a copy of it) for OpenSSL pre-1.1.1 or using additional command line options if using OpenSSL 1.1.1 and onwards. OpenSSL 1.1.1 was just released Sept 11 2018 and hasn’t yet made its way into most distributions. The commands below will show the pre-1.1.1 method using a copy of openssl.cnf which should also continue to work when 1.1.1 becomes more widely available.
Note: Your system’s openssl.cnf can be in several different places, common places to look:
- /System/Library/OpenSSL/openssl.cnf (OSX)
- /etc/pki/tls/openssl.cnf (RHEL)
Prepare local openssl.cnf
- Make a local copy of the system openssl.cnf:
cp /path/to/openssl.cnf openss.cnf.tmp
- Add subjectAltName entry in copy:
echo -e "[SAN]\nsubjectAltName=DNS:localhost\n" >> openssl.cnf.tmp
Create certificate and key
openssl req \ -newkey rsa:4096 \ -sha256 \ -x509 \ -days 3650 \ -nodes \ -subj "/C=C8/ST=ST/L=L/O=O/OU=OU/CN=localhost/emailAddressfirstname.lastname@example.org" \ -keyout server.private.key \ -config openssl.cnf.tmp \ -extensions SAN \ -out server.public.crt
If you get the following error:
problems making Certificate Request 139634576708864:error:0D07A098:asn1 encoding routines:ASN1_mbstring_ncopy:string too short:../crypto/asn1/a_mbstr.c:102:minsize=2
This indicates that one of the fields in the -subj text is either empty or too short
Assemble the PKCS12 container
openssl pkcs12 \ -export \ -inkey server.private.key \ -in server.public.crt \ -passout pass:password \ -out server.p12
(Optional) Examine the keystore
openssl pkcs12 \ -nokeys \ -info \ -in server.p12 \ -passin pass:password
Testing with OpenSSL
Whether you created your certificate and private key with keytool or openssl, you can use openssl to quickly test it.
Starting the test server
The following commands will start the TLS/SSL server bound to TCP port 4430 and instruct it to respond to a simple HTTP GET requests.
openssl s_server \ -key server.private.key \ -cert server.public.crt \ -accept 4430 \ -no_dhe \ -www
Testing client-side with web browser
Browse to https://localhost:4430 in a browser (you may have to click past warnings). You should see info text provided by test server (ciphers supported, ciphers in common with client, etc)
Testing client-side on the command line
- In a separate teminal window or tab, run:
openssl s_client -connect localhost:4430
- Followed by:
GET / HTTP 1.0
- Then the Enter key
You should see the same HTML used to generate the page for the browser test.