Lab - X509 Certificates
Public Key certificates comply with the X.509v3 standard and are managed in the context of a PKI (Public Key Infrastructure), that define the policies under which the certificates may be used. Most of the standards that regulate the use of X.509 certificates in the Internet are produced by the IETF PKIX Working Group1.
In this guide you will focus in the certificate validation processes.
For creating Certification Authorities and certificates look the syntax
of openssl x509
or XCA
2. A certificate is considered valid for a
specific purpose if the following conditions are verified: (i) if
the certificate is used during its validity interval, (ii) if the
certificate was issued (signed) by an entity (CA) in which the user
trusts and (iii) if the policies in the certificate allow its use for
the intended specific purpose. Here we are going to handle the first two
conditions. For additional information on the subject of this guide, you
may consult the Python Cryptography X509 PKI Tutorial3.
To complete this guide you will need to obtain a X509 Public Key Certificate. They are present in the Portuguese Citizen Card.
In alternative you can also use software such as XCA to create a new Certification Authority and certificates, or simply obtain the certificate from any website using the command:
openssl s_client -connect host:port -showcerts
Certificate Validity Interval
X.509 public key certificates contain a validity interval, that defines the temporal date interval where the certificate may be used.
Task: Implement a small program that reads a certificate and a function that
verifies its vality by analysing the attributes not_valid_after
and
not_valid_before
.
The certificate loaded should be stored in a dictionary where the key is the certificate subject. This will speed up the implementation of the remaining guide.
HINT: Use the cryptography.x509
class4 to load the certificate
and then check its attributes.
Trust Anchor certificates
Trust anchor certificates are the user trustable certificates, that typically are root certificates (i.e., self-signed certificates). Trust anchor certificates are important to validate certification paths (certificate chains). Every valid certification path must terminate (have a root) in a trust anchor certificate.
Reading trust anchor certificates
Usually trust anchor certificates are provided by the operating system
or the program in use (e.g., Firefox, Chrome, etc.), but may also be
provided by the user. The anchor certificates in which we trust must be
protected in a keystore
, or a restricted location (/etc/ssl/certs
),
to prevent new additions our removals, intentionally or not
intentionally. Python uses the certificates present in the system, as
individual files containing certificates in the PEM format.
Task: Implement a small program that reads all system trusted certificates into a dictionary of certificates, with the subject as the key.
Do not load certificates with that have expired (use the previous function)
HINT: Use the os.scandir
object to scan for all certificates in
/etc/ssl/certs
.
Build a certification path
A certificate chain, or certification path, is the set of certificates that composes a chain of trust, from a trust anchor certificate till an end user certificate. Frequently, to validate an end-user certificate it is necessary to build the certification path from a set of several candidate intermediary certificates. Other times, the entity to be validated (e.g, web server) provides the certification path together with his certificate.
Task: For this exercise, take each user certificate (from CC, downloaded, etc...), and create list with the full chain. To create the chain, load the user certificate, a dictionary of user specified intermediate roots, and the roots.
Task: Then build a list starting on the user certificate and adding each issuer certificate.
You should stop when you get a root certificate (self signed).
HINT: Remember that the user and root certificates are loaded into a dictionary with the subject as key. Therefore it is simple to obtain the issuer of a certificate.
Validate a certification path
Given a certification path we can validate it, by validating each certificate, and the relations between certificates.
Task: As the first step, create a function that validates the revokation status of each certificate in the chain. The validation should be made using CRL, Delta-CRL or OCSP as available. Each certificate specifies the validation methods and endpoints to use. A certificate can support all, or only some methods.
HINT: A CRL is a file that can be downloaded and loaded using the
x509.load_pen_x509_crl
method. It is comprised by a list of Revoked
Certificates, and you can search it for the certificate under
validation.
Task: The next step will be to validate the signatures in the chain. Each
certificate should have a signature (x509.signature
field) created
with the private key of the issuer. Use the issuer x509.public_key
to
validate the signature. The signed data is present in the attribute
x509.tbs_certificate_bytes
.
Validating the certificates also includes checking its purpose, common name and validity. Build a function that checks these attributes, and then call the previous functions to further validate the certificate chain.
NOTE: You can use Wireshark to verify the messages exchanged with the OCSP servers to get the revocation status of certificates.
To explore
There are several other tasks you can do to explore how the X509 certificates are used in common systems. Some are:
-
Alter your program to only use the CRL mechanism for revocation check when validating the certification path.
-
Alter your program to use OCSP first and to use CRL if OCSP mechanism fails.
-
Add caching mechanisms that store OCSP and CRL responses while they are valid.
Acknowledgements
Authored by João Paulo Barraca, André Zúquete, and Hélder Gomes