PKI

From Smithnet
Jump to navigation Jump to search

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)

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