docker-compose upThis will bring up 4 running containers:
root-caintermediate-caserver, used for testing HTTPS serverclient, used for testing HTTPS client
The local tmp folder is shared between running containers, and it's mounted as /tmp in-container folder.
Store only public files (non-sensitive) files in /tmp folder.
Configuration file is located in /usr/local/openssl/opnssl-rca.cnf in root-ca container.
Open root-ca container CLI and run following to generate RootCA key and certificate
RCA_DIR="/usr/local/openssl"
RCA_KEY="${RCA_DIR}/private/rca.key"
RCA_CRT="${RCA_DIR}/certs/rca.crt"
export SUBJECT="/C=XY/ST=XY/L=XY/O=Acme LTD/OU=Acme LTD/CN=DemoRootCA"
openssl req -nodes -x509 -newkey rsa:4096 -keyout "${RCA_KEY}" -out "${RCA_CRT}" -subj "${SUBJECT}" > /dev/null 2>&1The RootCA's certificate will be located in /usr/local/openssl/certs/rca.crt.
The RootCA's private key will be located in /usr/local/openssl/private/rca.key.
In production, you should set secure permissions on private key
chmod 600 "${RCA_KEY}"ls -l /usr/local/openssl/certs/rca.crt /usr/local/openssl/private/rca.keyShow Root CA certificate info:
openssl x509 -text -noout -in /usr/local/openssl/certs/rca.crtNote, that subject and issuer are the same, e.g. C=XY, ST=XY, L=XY, O=Acme LTD, OU=Acme LTD, CN=DemoRootCA:
- Country is
XY - State is
XY - Location is
XY - Organisation (AKA
O) isAcme LTD - Organisation Unit (AKA
OU) isAcme LTD - Common name (AKA
CN- end entity name) isDemoRootCA
Look at Issuer and Subject.
Also, look at X509v3 Subject Key Identifier and X509v3 Authority Key Identifier.
The Subject means "current certificate" and Authority/Issuer is who issued/signed the certificate.
The difference between Subject and Subject Key Identifier is:
Subjectis (generally speaking) just human-readable string identification of certificate- While
Subject Key Identifieris derived from the public key (generally speaking is a hash of public key)
Note, that Subject Key Identifier and Authority Key Identifier have same values.
This means that this is so-called "self-signed" certificate: certificate that was signed by its own private key.
Self-signed certificate also called RootCA certificate.
Setting up Intermediate CA includes following steps:
- Generating private key for Intermediate CA (done in ICA side)
- Create certificate sign request (CSR) signed with ICA private key (done in ICA side)
- Copying ICA's CSR to RCA host
- Signing ICA's CSR with RCA private key and producing certificate for ICA (done on RCA side)
- Copying produced ICA certificate to ICA host
Configuration file is located in
/usr/local/openssl/opnssl-rca.cnfinroot-cacontainer.
Open intermediate-ca container CLI and run following to generate ICA private key
export ICA_DIR="/usr/local/openssl"
ICA_KEY="${ICA_DIR}/private/ica.key"
openssl genrsa -out "${ICA_KEY}"the resulted ICA private ky will be located in /usr/local/openssl/private/ica.key.
In production, you should set secure permissions on private key
chmod 600 "${ICA_KEY}"Continue using intermediate-ca container CLI and run following to generate ICA certificate sign request:
export SUBJECT="/C=XY/ST=XY/L=XY/O=Acme LTD/OU=Acme LTD/CN=DemoICA"
openssl req -new -key "${ICA_KEY}" -out /tmp/ica.csr -subj "${SUBJECT}"This will create ICA CSR in /tmp/ica.csr file.
The CSR form will contain following info:
- Country is
XY - State is
XY - Location is
XY - Organisation (AKA
O) isAcme LTD - Organisation Unit (AKA
OU) isAcme LTD - Common name (AKA
CN- end entity name) isDemoICA
Investigate resulted ICA CSR by running:
openssl req -text -noout -in /tmp/ica.csrIn production, you should copy created ICA CSR file (/tmp/ica.csr) to root-ca container.
Since we're running demo in containers with shared /tmp, no need to do so.
On root-ca container run following:
openssl ca -extensions v3_intermediate_ca -out /tmp/ica.crt -infiles /tmp/ica.csrThis will sign ICA CSR and create certificate for ICA. The resulted certificate will be saved twice :
/usr/local/openssl/certs/00.pemfile (for further tracking by RCA). 00 is a local certificate serial number. See/usr/local/openssl/serialfile.ica.crtfile in current directory ofroot-cacontainer.
They are the same:
# md5sum /usr/local/openssl/certs/00.pem /tmp/ica.crt
<some-hash-sum> /usr/local/openssl/certs/00.pem
<some-hash-sum> ica.crtDump ICA certificate info:
openssl x509 -text -noout -in /tmp/ica.crtLook at Issuer and Subject.
Also, look at X509v3 Subject Key Identifier and X509v3 Authority Key Identifier.
The Subject means "current certificate" and Authority/Issuer is who issued/signed the certificate.
The difference between Subject and Subject Key Identifier is:
Subjectis (generally speaking) just human-readable string identification of certificate- While
Subject Key Identifieris derived from the public key (generally speaking is a hash of public key)
Same for Issuer and X509v3 Authority Key Identifier.
Copy produced ICA certificate ica.crt from root-ca container to intermediate-ca and put the certificate
in /usr/local/openssl/certs/ica.crt on intermediate-ca container. (No need to do so in demo since we're running with shared /tmp)
On intermediate-ca CLI, run:
cp /tmp/ica.crt /usr/local/openssl/certs/ica.crtOpen server container CLI and run following to generate server private key
SRV_KEY="/root/server.key"
openssl genrsa -out "${SRV_KEY}"This will generate server private key and store it in /root/server.key.
In production, you should set secure permissions on private key
chmod 600 "${SRV_KEY}"Open server container CLI and run following to generate server CSR
SUBJECT="/C=XY/ST=XY/L=XY/O=Acme LTD/OU=Acme LTD/CN=server.local"
openssl req -new -key "${SRV_KEY}" -out /tmp/server.csr -subj "${SUBJECT}"Copy server CSR file (/tmp/server.csr) from server container to intermediate-ca container.
Sign it with ICA by running following on intermediate-ca CLI:
openssl ca -policy policy_strict -extensions server_cert -out /tmp/server.crt -infiles server.csrAs in case of issuing/signing ICA certificate above, the server certificate will be written twice in ICAL
Validate they are the same by running following in intermediate-ca:
root@intermediate-ca:~# ls -l /tmp/server.crt /usr/local/openssl/certs/00.pem
-rw-r--r-- 1 root root 5003 Sep 10 15:28 /usr/local/openssl/certs/00.pem
-rw-r--r-- 1 root root 5003 Sep 10 15:28 /tmp/server.crt
root@intermediate-ca:~# md5sum /tmp/server.crt /usr/local/openssl/certs/00.pem
<hash> /tmp/server.crt
<hash> /usr/local/openssl/certs/00.pemCopy server certificate to server container to be stored in /tmp/server.crt.
Now SSL/TLS server is ready to be run.
On server container run simple HTTPS server
openssl s_server -accept 443 -key "$SRV_KEY" -cert /tmp/server.crt -wwwOn root-ca container, copy RCA certificate to /tmp directory.
cp /usr/local/openssl/certs/rca.crt /tmpSince /tmp is shared between containers, switch to client container CLI and concatenate two CA certificates (rca.crt and ica.crt) and store them on client in /tmp/ca.crt file.
cat /tmp/rca.crt /tmp/ica.crt > /tmp/ca-cundle.crtOn client container CLI, run following:
openssl s_client -connect server.local:443 -CAfile /tmp/ca-cundle.crtThe result should be as following:
CONNECTED(00000003)
depth=2 C = XY, ST = XY, L = XY, O = Acme LTD, OU = Acme LTD, CN = DemoRootCA
verify return:1
depth=1 C = XY, ST = XY, O = Acme LTD, OU = Acme LTD, CN = DemoICA
verify return:1
depth=0 C = XY, ST = XY, O = Acme LTD, OU = Acme LTD, CN = server.local
verify return:1
---
Certificate chain
0 s:/C=XY/ST=XY/O=Acme LTD/OU=Acme LTD/CN=server.local
i:/C=XY/ST=XY/O=Acme LTD/OU=Acme LTD/CN=DemoICA
---
Server certificate
-----BEGIN CERTIFICATE-----
<... snip ...>
-----END CERTIFICATE-----
subject=/C=XY/ST=XY/O=Acme LTD/OU=Acme LTD/CN=server.local
issuer=/C=XY/ST=XY/O=Acme LTD/OU=Acme LTD/CN=DemoICA
---
No client certificate CA names sent
Peer signing digest: SHA512
Server Temp Key: ECDH, P-256, 256 bits
---
SSL handshake has read 1776 bytes and written 527 bytes
---
New, TLSv1/SSLv3, Cipher is ECDHE-RSA-AES256-GCM-SHA384
Server public key is 2048 bit
Secure Renegotiation IS supported
Compression: zlib compression
Expansion: zlib compression
No ALPN negotiated
SSL-Session:
Protocol : TLSv1.2
Cipher : ECDHE-RSA-AES256-GCM-SHA384
Session-ID: 84DE10A39A22CA4AB277EC4E4043EBEC07D3924B5DB1FBD7BBC1C48EC53D826A
Session-ID-ctx:
Master-Key: 5AA79E2052D57FB5AA32B5B85703BBB1CD67341D1EA8C42980175DAF52FEAEA70EF7DD02AD0D7BAA7D17E7719972BFFF
<.... snip ....>
Compression: 1 (zlib compression)
Start Time: 1631346230
Timeout : 300 (sec)
Verify return code: 0 (ok)
---The Verify return code: 0 (ok) indicates that certificate is valid in terms of:
- Issuers chain (ICA and RCA) is known (trusted)
- Certificate is not expired
CN(a.k.a. common-name) orSANs (Subject Alternative Name) matches server hostname
Copy server certificate from server to client and extract server public key from server certificate by running run following on client container:
# extract public key from certificate
openssl x509 -pubkey -noout -in server.crt > /tmp/server.pubCreate "top secret" data to be encrypted on client and store it in /tmp/secret-message:
echo "My TOP secret message" > /root/secret-messageEncrypt "top secret" data on client:
openssl rsautl -in /root/secret-message -out /tmp/secret-message.enc -pubin -inkey /tmp/server.pub -encryptThe encrypted result will be stored in /tmp/secret-message.enc file (it's a binary blob).
Transfer encrypted result (/tmp/secret-message.enc) from client container to server container.
Note: since encrypted data is binary blob, use base64 tool for ease of transferring between containers by copy-pasting the content.
Decrypt encrypted data on server container by running following:
openssl rsautl -in /tmp/secret-message.enc -out /root/secret-message.dec -inkey /root/server.key -decryptThe decrypted result will be stored in /root/secret-message.dec file.
Compare content of /root/secret-message.dec on server container with original "top-secret" message on client container (e.g. /root/secret-message).
In this test we will test singing and verify signature over previously encrypted message /tmp/secret-message.enc, though we may use any other test data.
To sign file, on server container run following:
openssl dgst -sha256 -sign /root/server.key -out /tmp/secret-message.sign /tmp/secret-message.encThe signature of signed data will be stored in /tmp/secret-message.sign.
To verify the signature with public key, run following on client container CLI:
openssl dgst -sha256 -verify /tmp/server.pub -signature /tmp/secret-message.sign /tmp/secret-message.encThe output will be
Verified OK- CRLs
- delta CRLs
- OCSP
- Extended key usage
- User certificates