PKI
Background
- Public Key Encryption: Public/Private key pair are generated. Information encrypted with public key can only be decrypted with the private key. SSL uses this to allow a client to encrypt a symmetric key for the subsequent exchange of subsequent data.
- Public Key Certificate ("Digital Certificate"): data is encrypted with private key, can be decrypted by anyone with the public key. If identify information is used, this is bound to the public key - so a public key can be verified that it belongs to an individual/organisation and cannot be forged (as only the person with access to the private key could have generated it).
- Certificate: Public Key and ID bound by a CA signature.
- CA Certificate: Used by a CA to sign certificates and CRLs.
- Root Certificate: Self-signed CA Certificate. This is the PKI trust anchor.
- Cross Certificate: CA certificate issued by an CA external to the primary PKI hierarchy. Used to connect two PKIs, so normally are in pairs.
- User Certificate: Used by end users to purposes such as securing email, server authorisation, client authorisation, code signing, etc. Cannot sign other certificates.
- Certificate Authority (CA): The organisation that issues certificates and CRLs.
- Root CA: The root of a PKI hierarchy, and has a self-signed certificate. Only issues other CA certificates.
- Intermediate CA: A level down from the Root CA but above a signing CA. Only issues other CA certificates.
- Signing CA: At the bottom of the PKI hierarchy, issues only user certificates.
- Certificate Signing Request (CSR): A request for certification that is sent to a CA. Contains a public key and ID to be certified.
- Certificate Revocation List (CRL): A list of revoked certificates that were issued by a CA, but were later revoked and considered invalid.
- Online Certificate Status Protocol (OCSP): A protocol allowing a client to check with the CA that a certificate presented by a server is/is not revoked by the signing CA.
See also:
Constraints
Certificates can limit their capabilities with x509 constraints. An end-identify CA would have this, to show it can't act as a CA:
X509v3 extensions: X509v3 Basic Constraints: CA:FALSE
A CA that can sign certificates but not create sub-CAs would have:
X509v3 extensions: X509v3 Basic Constraints: critical CA:TRUE, pathlen:0
A pathLenConstraint of zero indicates that no non-self-issued intermediate CA certificates may follow in a valid certification path. Where it appears, the pathLenConstraint field MUST be greater than or equal to zero. Where pathLenConstraint does not appear, no limit is imposed.
and also:
X509v3 extensions: X509v3 Key Usage: critical Digital Signature, Certificate Sign, CRL Sign
File Formats
- Privacy Enhanced Mail (PEM): ASCII based format (preferred by OpenSSL, Apache, etc) that can include multiple certificates and keys. Base-64 encoded.
Typically .pem extension.
- Distingushed Encoding Rules (DER): Binary format preferred by by Windows, typically .der, .cer or .crt extension.
- PKCS7: Format for storing multiple certificates, no password protection.
- PKCS8: Format for storing private key information. Typically .key extension.
-----BEGIN PRIVATE KEY-----
or
-----BEGIN ENCRYPTED PRIVATE KEY-----
- PKCS10: Format for a CSR. Typically .csr extension. Header:
-----BEGIN CERTIFICATE REQUEST-----
- PKCS12: Common format for storing multiple private keys and public certificates in a single file, protected by password-based encryption. Typically .pfx or .p12 extension.
OpenSSL Useful Commands
Show readable contents of key, CSR and certificates:
openssl rsa -in ca.key -text -noout openssl req -in newreq.csr -text -noout openssl x509 -in cert.crt -text -noout openssl x509 -in cert.der -inform DER -text -noout
Some data contents may not be shown, eg:
... X509v3 Subject Alternative Name: othername:<unsupported>, othername:<unsupported> ...
Parse this like:
openssl asn1parse -in cert.pem ... 700:d=4 hl=2 l= 109 cons: SEQUENCE 702:d=5 hl=2 l= 3 prim: OBJECT :X509v3 Subject Alternative Name 707:d=5 hl=2 l= 100 prim: OCTET STRING [HEX DUMP]:0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF01234567 907:d=1 hl=2 l= 13 cons: SEQUENCE ...
and then expand the hex dump of interest:
# openssl asn1parse -in certificate.pem -strparse 707 0:d=0 hl=2 l= 100 cons: SEQUENCE 2:d=1 hl=2 l= 47 prim: cont [ 2 ] 51:d=1 hl=2 l= 4 prim: cont [ 7 ] 57:d=1 hl=2 l= 29 cons: cont [ 0 ] 59:d=2 hl=2 l= 3 prim: OBJECT :1.2.3.4 64:d=2 hl=2 l= 22 cons: cont [ 0 ] 66:d=3 hl=2 l= 20 prim: UTF8STRING :some-testing-value-1 88:d=1 hl=2 l= 12 cons: cont [ 0 ] 90:d=2 hl=2 l= 3 prim: OBJECT :1.2.3.5 95:d=2 hl=2 l= 5 cons: cont [ 0 ] 97:d=3 hl=2 l= 13 prim: UTF8STRING :other-value-2
Convert a PEM-format certificate into a DER-format certificate:
openssl x509 -inform PEM -outform DER -in yourcert.pem -out yourcert.der
Convert a DER-format certificate into a PEM-format certificate:
openssl x509 -in yourcert.der -out yourcert.pem -inform DER -outform PEM
Extract keys and/or certificates from PKCS12 file (add -nocerts or -nokeys as required):
openssl pkcs12 -in certsandkeys.p12 -out certs.pem -nodes -nokeys
Extract modulus from private key, and generate its hash:
openssl rsa -in yourkey.pem -noout -modulus | openssl md5
This modulus should be the same as that in the cert received back from the CA:
openssl x509 -in yourcert.pem -noout -modulus | openssl md5
Take an unencrypted key, and add a password-encryption:
openssl pkey -des3 -in unencrypted.pem -out encrypted.key
Verify a cert against a CA:
openssl verify -verbose -CAfile ca.pem yourcert.pem
Connect to a server, grab certificate and check against CA bundle:
openssl s_client -showcerts -connect 192.168.0.1:443 -CAfile ca.pem
which will show the certificate chain, SSL handshake, negotiated protocol version and cypher. If all goes well, a 0 return code will be given. Code 19 means a self-signed cert has been reached, which it doesn't trust (typically the CA cert is not in the CA bundle specified).
Show ECC curves that OpenSSL supports:
openssl ecparam -list_curves
- secp224r1 : NIST/SECG curve over a 224 bit prime field
- secp256k1 : SECG curve over a 256 bit prime field
- secp384r1 : NIST/SECG curve over a 384 bit prime field (higher CPU resources, fairly high compatibility)
- secp521r1 : NIST/SECG curve over a 521 bit prime field (highest security, lower compatibility)
- prime256v1 : X9.62/SECG curve over a 256 bit prime field (good compatibility)
Nmap can show available ciphers:
nmap --script ssl-enum-ciphers -p 443 localhost
OpenSSL CA Setup
Root CA
Configuration based on openssl.conf See here for a walkthough.
Create a base directory, eg /root/CA/root
# mkdir root # cd root # mkdir certs crl newcerts private # chmod 700 private # touch index.txt # echo 1000 > serial
Generate root key. Either RSA:
# openssl genrsa -aes256 -out private/ca.key.pem 4096
or ECC:
# openssl ecparam -genkey -out private/ca.key.pem -name secp384r1
set correct permissions:
# chmod 400 private/ca.key.pem
Use the root key (ca.key.pem) to create a root cert (ca.cert.pem), lasting for many years:
# openssl req -config openssl.conf -key private/ca.key.pem -new -x509 -days 7300 -sha256 -extensions v3_ca -out certs/ca.cert.pem
(Enter the root CA key password if RSA was chosen. Choose a CN such as "MyOrg Root CA")
# chmod 444 certs/ca.cert.pem
Observe root cert:
# openssl x509 -noout -text -in certs/ca.cert.pem
Intermediate (Signing) CA
# mkdir int1 # cd int1 # mkdir certs crl csr newcerts private # chmod 700 private # touch index.txt # echo 1000 > serial
Add crlnumber to keep track of CRLs
# echo 1000 > crl/number
Copy openssl.conf from root, and change (copy_extensions is used to copy SANs from a CSR to a certificate):
dir = /root/CA/int1 policy = policy_loose copy_extensions = copy
Create intermediate key:
# openssl genrsa -aes256 -out private/ca.key.pem 4096
(Enter intermediate CA password)
OR:
# openssl ecparam -genkey -out private/ca.key.pem -name secp384r1
# chmod 400 private/ca.key.pem
Create intermediate certificate (matching details apart from CN: "MyOrg Intermediate CA"), lasting 10 years:
# openssl req -config openssl.conf -new -sha256 -key private/ca.key.pem -out csr/ca.csr.pem # cd ../root # openssl ca -config openssl.conf -extensions v3_intermediate_ca -days 3650 -notext -md sha256 -in ../int1/csr/ca.csr.pem -out ../int1/certs/ca.cert.pem # chmod 444 ../int1/certs/ca.cert.pem
The index.txt file should contain an entry of the intermediate cert.
Verify intermediate cert:
# cd ../int1 # openssl x509 -noout -text -in certs/ca.cert.pem # openssl verify -CAfile ../root/certs/ca.cert.pem certs/ca.cert.pem
Create a certificate chain file (or if the Root CA cert is to be distributed to clients, only the intermediate cert):
# cat certs/ca.cert.pem ../root/certs/ca.cert.pem > certs/ca-chain.cert.pem # chmod 444 certs/ca-chain.cert.pem
NOTE: normally duplicate certificate entries are not allowed - "ERROR:There is already a certificate for /C=UK/ST=England/L=London/O...". To changes this behavour, make sure index.txt.attr contains:
unique_subject = no
Sign Server and Client Certs
Create a key with:
# cd ../int1 # openssl genrsa -aes256 -out private/myserver.myorg.com.key.pem 2048 # chmod 400 private/myserver.myorg.com.key.pem
or:
# cd ../int1 # openssl ecparam -genkey -out private/myserver.myorg.com.key.pem -name secp384r1 # chmod 400 private/myserver.myorg.com.key.pem
Create a certificate signing request: (for server certs, CN is a FQDN, for client certs it is anything unique, like email). At least one SAN should be added for Web certificates:
# openssl req -config openssl.conf -addext "subjectAltName = DNS:www.example.com, DNS:example.com" -key private/myserver.myorg.com.key.pem -new -sha256 -out csr/myserver.myorg.com.csr.pem
Check CSR:
# openssl req -in csr/myserver.myorg.com.csr.pem -text -noout
Sign request as CA (For server use server_cert extension, for client, use usr_cert extension):
# openssl ca -config openssl.conf -extensions server_cert -days 365 -notext -md sha256 -in csr/myserver.myorg.com.csr.pem -out newcerts/myserver.myorg.com.cert.pem # chmod 444 newcerts/myserver.myorg.com.cert.pem
The index.txt should now have an entry for the newly signed cert.
Verify:
# openssl x509 -noout -text -in newcerts/myserver.myorg.com.cert.pem # openssl verify -CAfile certs/ca-chain.cert.pem newcerts/myserver.myorg.com.cert.pem
Deploy Certificates
If the CSR was from a 3rd party, just send them the certs/myserver.myorg.com.cert.pem certificate and certs/ca
Eg in Apache (ssl.conf):
SSLCertificateFile /etc/pki/tls/certs/myserver.myorg.com.cert.pem SSLCertificateKeyFile /etc/pki/tls/private/myserver.myorg.com.key.pem
If using client authentication, additionally:
SSLCACertificateFile /etc/pki/tls/certs/SmithnetCA-chain.cert.pem
(This directive sets the all-in-one file where you can assemble the Certificates of Certification Authorities whose clients you deal with. Such a file is simply the concatenation of the various PEM-encoded Certificate files, in order of preference. This can be used alternatively and/or additionally to SSLCACertificatePath.)
Subject Alternative Name
SANs can be defined in the openssl.conf file:
req_extensions = v3_req
[ v3_req ] ... subjectAltName = @alt_names
[alt_names] DNS.1 = server.example.com DNS.2 = www.example.com
- ALL* names should be listed: if SAN is present, CN is ignored. CSR should then have the SANs present.
However a subsequent CA signing also has to have them defined (not just in the CSR):
[ server cert ] ... subjectAltName = @alt_names
PKCS#8
Encode key file with password:
openssl pkcs8 -in server-open.key -out server-protected.key
PKCS#12
If required, the CA public certificate can be packaged as a PFX file, with additional "Friendly Name" and optional export password:
openssl pkcs12 -export -in cacert.pem -inkey cakey.pem -out cacert.pfx -name "Smithnet CA"
User Certificates
Generate key:
# openssl ecparam -genkey -name secp384r1 -out private/joebloggs.key.pem
Generate CSR:
# openssl req -config openssl.conf -new -key private/joebloggs.key.pem -out csr/joebloggs.csr.pem
Sign with CA:
# openssl ca -config openssl.conf -extensions usr_cert -days 720 -notext -md sha256 -in csr/joebloggs.csr.pem -out newcerts/joebloggs.cert.pem
Package key and cert with password, to be imported into browser:
# openssl pkcs12 -export -clcerts -in newcerts/joebloggs.cert.pem -inkey private/joebloggs.key.pem -out joebloggs.p12
Configure Apache to check client certificate:
SSLCACertificateFile /etc/pki/tls/certs/ca-chain.cert.pem <Location /private/> SSLVerifyClient require SSLVerifyDepth 2 SSLRequireSSL SSLRequire %{SSL_CIPHER} !~ m/^(EXP|NULL)/ \ and %{SSL_CLIENT_S_DN_O} eq "MyOrg" and %{SSL_CLIENT_S_DN_OU} in {"MyOrg IT", "MyOrg Sales"} </Location>
See here for a full list of certificate parameters that can be interrogated. Note that there are problems with TLS 1.3 and HTTP/2 (see here) so need to ensure TLS 1.2 is used:
SSLProtocol -all +TLSv1.2
Certificate Revocation Lists
See: here
Tomcat
Java Keystore
Java keytool supports keystore formats:
- JKS
- PKCS7 - package multiple certs in a single package, no password
- PKCS10 - CSR
- PKCS11
- PKCS12 - package multiple certs in a single package, with a password, used my OpenSSL and Microsoft Key-Manager
See also full list here
Entry types:
- PrivateKeyEntry - Private key with key chain
- secretKeyEntry
- trustedCertEntry - Public key of another entity that we trust (eg root CA)
List entries in truststore:
keytool -list -v -keystore cacerts.p12 -storepass changeit -storetype PKCS12 -providername JsafeJCE
List entries in keystore:
keytool -list -v -keystore keystore.p12 -storepass internal -storetype PKCS12 -providername JsafeJCE
List entries in JVM keystore (CA certificates):
$JAVA_HOME/jdk/bin/keytool -list -storepass changeit -keystore $JAVA_HOME/jdk/jre/lib/security/cacerts
Convert JKS to PKCS12:
keytool -importkeystore -srckeystore certificate.jks -srcstorepass password -srckeypass password -destkeystore certificate.p12
-deststoretype PKCS12 -srcalias hostname -deststorepass password -destkeypass password
Convert PKCS12 to PEM:
openssl pkcs12 -in cert.p12 -passin pass:password -nokeys -out certificate.pem
See here for other examples.
HTTPS connector
- Configured in server.xml
- If keyAlias on the connector is not specified, the first certificate in the keystore will be used.
JSSE Mode
cd /etc/tomcat keytool -genkey -alias tomcat -keyalg RSA -keystore keystore.p12 -keysize 2048 -storetype pkcs12 -storepass password keytool -certreq -alias tomcat -keyalg RSA -keystore keystore.p12 -storetype pkcs12 -storepass password -file certreq.csr
Send CSR to CA and obtain signed cert newcert.pem and CA root cert RootCA.pem
keytool -importcert -trustcacerts -alias rootca -keystore /usr/lib/jvm/java-1.7.0-openjdk-1.7.0.60-2.4.2.4.fc19.x86_64/jre/lib/security/cacerts -storepass changeit -file RootCA.pem
keytool -importcert -trustcacerts -alias tomcat -keyalg RSA -keystore keystore.p12 -storetype pkcs12 -storepass password -file newcert.pem
Configuration in server.xml:
<Connector port="8443" protocol="HTTP/1.1" SSLEnabled="true" keystorefile="/etc/tomcat/keystore.p12" keystorePass="password" keystoreType="PKCS12" maxThreads="150" scheme="https" secure="true" clientAuth="false" sslProtocol="TLS" />
APR Mode (using OpenSSL)
TBC
Let's Encrypt
Install snapd:
dnf install snapd ln -s /var/lib/snapd/snap /snap systemctl enable snapd systemctl start snapd
Install certbot:
snap install --classic certbot ln -s /snap/bin/certbot /usr/local/bin/certbot
Check installation:
root@svr1:/# snap list Name Version Rev Tracking Publisher Notes certbot 2.10.0 3700 latest/stable certbot-eff✓ classic core20 20240227 2264 latest/stable canonical✓ base snapd 2.62 21465 latest/stable canonical✓ snapd
Certbot will authenticate (to prove you own the domain) and optionally install the certicate. See Certbot Docs for more information.
Get certificate:
certbot certonly -d www.example.com -d example.com
... 3: Saves the necessary validation files to a .well-known/acme-challenge/ directory within the nominated webroot path. A seperate HTTP server must be running and serving files from the webroot path. HTTP challenge only (wildcards not supported). (webroot) ... Select webroot: /var/www/html
Show certificates:
certbot certificates
This will show details including the installed paths, eg:
- Expiry Date (validity is 89 days)
- Certificate Path: /etc/letsencrypt/live/www.example.com/fullchain.pem
- Private Key Path: /etc/letsencrypt/live/www.example.com/privkey.pem
A systemd timer is installed for auto-renewal:
- /etc/systemd/system/snap.certbot.renew.timer