PKI: Difference between revisions

From Smithnet Wiki
Jump to navigation Jump to search
m (1 revision imported)
 
(10 intermediate revisions by the same user not shown)
Line 119: Line 119:


Connect to a server, grab certificate and check against CA bundle:
Connect to a server, grab certificate and check against CA bundle:
  openssl s_client -connect 192.168.0.1:443 -CAfile ca.pem
  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).
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).


Line 130: Line 130:
* secp521r1 : NIST/SECG curve over a 521 bit prime field (highest security, lower 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)
* 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 ==
== OpenSSL CA Setup ==
Line 340: Line 343:
List entries in JVM keystore (CA certificates):
List entries in JVM keystore (CA certificates):
  $JAVA_HOME/jdk/bin/keytool -list -storepass changeit -keystore $JAVA_HOME/jdk/jre/lib/security/cacerts
  $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 [http://www.sslshopper.com/article-most-common-java-keytool-keystore-commands.html here] for other examples.
See [http://www.sslshopper.com/article-most-common-java-keytool-keystore-commands.html here] for other examples.
Line 369: Line 379:


TBC
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 [https://eff-certbot.readthedocs.io/en/latest/using.html 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

Latest revision as of 15:10, 12 September 2024

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