5.11 THE CHAINS WE FORGED IN LIFE
EXERCISE 5.11: THE CHAINS WE FORGED IN LIFE
Modify the identity validation programs to support a chain of trust. First, create some self signed certificates for the EA goverment (at least two as described previously). The existing issuer script can already do this. Just make the issuer private key for the self-signed certificate to be the organization’s own private key. Thus, the organization is signing its own cert, and the private key used to sign the certificate matches the public key in the certificate.
Next, create certificates for intermediate CAs such as “Department of Education”, “Department of Defense”, “Espionage Agency” and so forth. These certificates should be signed by the self signed certificates in the previous step.
Finally, sign certificates for Alice, Bob, and Charlie by the espionage CA. Perhaps create some certificates for employees of the defense department and the education department. These certificates should be signed by the appropriate intermediate CA.
Now, modify the verification program to take a chain of certificates instead of just a single certificate. Get rid of the command-line parameter for the issuer’s public key and instead hard-code which of the root certificate filenames are trusted. To specify the chain of certificates, have the program take the claimed identity as the first input (as it already does) and then an arbitrary number of certificates to follow. Each certificate’s issuer field should indicate the next certificate in the chain. For example, to validate Charlie, there may be three certificates:
charlie.cert,espionage.cert,covert_root.cert. The issuer ofcharlie.certshould have the same subject name asespionage.certand so forth. The verify program should only accept an identity if the last certificate in the chain is already trusted.
Creating two self-signed certificates
Let’s call the keys:
root1_private.key,root1_public.keyroot2_private.key,root2_public.key

Let’s call the certificates:
- root1.cert
- root2.cert

The contents of the bash script above is given below:
python3 fake_certs_issuer.py root1_private.key fake_cert_authority1 root1_public.key root1.cert
python3 fake_certs_issuer.py root2_private.key fake_cert_authority1 root2_public.key root2.certThe usage of fake_certs_issuer.py script is:
Usage: python3 fake_certs_issuer.py <issuer-private-key-file> <certificate-subject> <subject's-public-key-file> <certificate-output-file>
Creating certs for the departments
Department of Education

The contents of the above script is:
# this script is used to create the certificates
# for the DOE - Department of Education
# generate a pair of keys...
python3 generate_rsa_key_pairs.py doe_private.key doe_public.key
# sign doe's public key using one of the root certs.
python3 fake_certs_issuer.py root2_private.key doe doe_public.key doe.certDepartment of Defense

Espionage Agency

Creating certs for the employees of the departments

source code of the above script:
# Create certs for employees of EA
# generate a pair of keys...
python3 generate_rsa_key_pairs.py alice_private.key alice_public.key
python3 generate_rsa_key_pairs.py bob_private.key bob_public.key
python3 generate_rsa_key_pairs.py charlie_private.key charlie_public.key
# sign the public keys of the employees using the private key of ea.
python3 fake_certs_issuer.py ea_private.key alice alice_public.key alice.cert
python3 fake_certs_issuer.py ea_private.key bob bob_public.key bob.cert
python3 fake_certs_issuer.py ea_private.key charlie charlie_public.key charlie.certCharlie verifying his identity
I made a lot of changes to the script fake_certs_verify_identity.py and fixed bugs in fake_certs_issuer.py. But end result is as follows:

Contents of the above shell script is:
python3 fake_certs_verify_identity.py charlie charlie.cert ea.cert root2.certContents of fake_certs_verify_identity.py
# Note that this script is being run by Alice,
# to verify the certificate of Charlie,
# that has been signed by HQ.
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import rsa, padding
import sys, json, os
from read_write_rsa_keys import *
ISSUER_NAME = "fake_cert_authority1"
SUBJECT_KEY = "subject"
ISSUER_KEY = "issuer"
PUBLICKEY_KEY = "public_key"
trusted_root_cert_fnames = [
'root1.cert',
'root2.cert',
]
class IssuerNotAsExpected(Exception):
def __init__(self, expected_issuer_name: str, actual_issuer_name: str):
self.expected_issuer_name = expected_issuer_name
self.actual_issuer_name = actual_issuer_name
def __str__(self):
return f"\nExpected: {self.expected_issuer_name}.\nBut got: {self.actual_issuer_name}"
class RootCertificateNotTrusted(Exception):
pass
def read_public_key_from_certificate(cert_fname: str):
if not os.path.exists(cert_fname):
raise FileNotFoundError()
with open(cert_fname, 'rb') as f:
data = f.read()
raw_cert_bytes, signature = data[:-256], data[-256:]
cert_data = json.loads(raw_cert_bytes.decode('utf-8'))
public_key = serialization.load_pem_public_key(
data=cert_data[PUBLICKEY_KEY].encode(),
backend=default_backend(),
)
return (public_key, cert_data[SUBJECT_KEY])
def validate_certificate_chain(cert_files: list[str]):
if cert_files[-1] not in trusted_root_cert_fnames:
raise RootCertificateNotTrusted()
for i in range(len(cert_files) - 1):
# validate the i th certificate using the (i + 1)th certificate
print(f"[+] Validating {cert_files[i]} using {cert_files[i+1]}...")
with open(cert_files[i], 'rb') as f:
certificate_bytes = f.read()
issuer_public_key, expected_issuer = read_public_key_from_certificate(cert_files[i+1])
raw_cert_bytes, signature = certificate_bytes[:-256], certificate_bytes[-256:]
issuer_public_key.verify(
signature,
raw_cert_bytes,
padding.PSS(
mgf=padding.MGF1(hashes.SHA256()),
salt_length=padding.PSS.MAX_LENGTH
),
hashes.SHA256(),)
current_cert = json.loads(raw_cert_bytes.decode('utf-8'))
if current_cert[ISSUER_KEY] != expected_issuer:
raise IssuerNotAsExpected(
actual_issuer_name=current_cert[ISSUER_KEY],
expected_issuer_name=expected_issuer,
)
with open(cert_files[0], 'rb') as f:
data = f.read()
raw_cert_bytes = data[:-256]
cert_data: dict[str, str | bytes] = json.loads(raw_cert_bytes.decode('utf-8'))
cert_data[PUBLICKEY_KEY] = cert_data[PUBLICKEY_KEY].encode('utf-8')
return cert_data
def verify_identity(identity: str, certificate_data: dict[str, str | bytes], challenge, response):
'''
* identity - the claimed identity. example: "Charlie"
* certificate_data - data of the certificate.
'''
if certificate_data[SUBJECT_KEY] != identity:
raise Exception("Claimed identity does not match")
certificate_public_key: rsa.RSAPublicKey = serialization.load_pem_public_key(
data=certificate_data[PUBLICKEY_KEY],
backend=default_backend(),
)
certificate_public_key.verify(
signature=response,
data=challenge,
padding=padding.PSS(
mgf=padding.MGF1(hashes.SHA256()),
salt_length=padding.PSS.MAX_LENGTH
),
algorithm=hashes.SHA256(),
)
if __name__ == "__main__":
if len(sys.argv) < 3:
print(f"Usage: python3 {sys.argv[0]} <claimed-identity> [cert-file...]")
exit(1)
claimed_identity = sys.argv[1]
cert_files = sys.argv[2:]
cert_data = validate_certificate_chain(
cert_files=cert_files,
)
print("Certificate has a valid signature from {}".format(ISSUER_NAME))
challenge_file = input("Enter a name for a challenge file: ")
print(f"Generating challenge to file {challenge_file}")
challenge_bytes = os.urandom(32)
with open(challenge_file, 'wb+') as f:
f.write(challenge_bytes)
response_file = input("Enter a name of the response file: ")
with open(response_file, 'rb') as f:
response_bytes = f.read()
verify_identity(
identity=claimed_identity,
certificate_data=cert_data,
challenge=challenge_bytes,
response=response_bytes,
)
print(f"[+] Identity validated. You really are {claimed_identity}.")