Certificates#
A digital certificate is an electronic document that verifies the identity of an individual, organization, or device. It is a crucial component of secure online communication, ensuring that data exchanged between parties is encrypted and protected from unauthorized access.
The Certificate
class is a crucial representation in the SPSDK (Secure Provisioning SDK), serving as a representation of X.509 certificates. It encapsulates functionalities related to generating, saving, loading, and manipulating certificates.
1. Prerequisites#
SPSDK is needed with examples extension.
pip install spsdk[examples]
(Please refer to the installation documentation.)Generate 4 key pairs using nxpcrypto application. To generate these keys, use the nxpcrypto application (refer to keys generation). In this example we will use pre-generated keys
Let’s prepare also workspace and variables.
# Initialization cell
import os
# This env variable sets colored logger output to STDOUT
%env JUPYTER_SPSDK=1
# Set a magic for command execution and echo
%alias execute echo %l && %l
%alias_magic ! execute
env: JUPYTER_SPSDK=1
Created `%!` as an alias for `%execute`.
WORKSPACE = "workspace/" # change this to path to your workspace
KEYS_DIR = "_data/keys/"
VERBOSITY = (
"-v" # verbosity of commands, might be -v or -vv for debug or blank for no additional info
)
2. Chain of Trust#
A chain certificate is a collection of certificates that form a hierarchical structure, with each certificate signing the next one in the chain. The chain starts with a root certificate, which is a self-signed certificate that serves as the trust anchor. Each subsequent certificate in the chain is signed by the previous one, forming a chain of trust.
The SPSDK uses a specific structure for chain certificates, which consists of the following components:
Root Certificate
The root certificate is a self-signed certificate that serves as the trust anchor. It is the top-most certificate in the chain and is used to sign the next certificate in the chain.
Intermediate Certificate
The intermediate certificate is signed by the root certificate and is used to sign the next certificate in the chain.
Device Certificate
The device certificate is signed by the intermediate certificate and is used to authenticate the device.
2.1 Chain of Trust generation#
In following example we will generate following certificates chain.
CA Certificate / \ / \ crt1 intermediate_crt \ \ crt2
from os import path
from spsdk.crypto.certificate import Certificate, generate_extensions, generate_name
from spsdk.crypto.crypto_types import SPSDKEncoding
from spsdk.crypto.keys import PrivateKeyRsa, PublicKeyRsa
# Generate Root CA Certificate (self-signed certificate)
private_key_2048_ca = PrivateKeyRsa.load(path.join(KEYS_DIR, "ca_privatekey_rsa2048.pem"))
public_key_2048_ca = PublicKeyRsa.load(path.join(KEYS_DIR, "ca_publickey_rsa2048.pem"))
subject = issuer = generate_name([{"COMMON_NAME": "first"}, {"COUNTRY_NAME": "CZ"}])
ca_cert = Certificate.generate_certificate(
subject=subject,
issuer=issuer,
subject_public_key=public_key_2048_ca,
issuer_private_key=private_key_2048_ca,
serial_number=0x1,
duration=20 * 365,
extensions=generate_extensions(
{"BASIC_CONSTRAINTS": {"ca": True, "path_length": 5}},
),
)
# Save certificates in two formats (pem and der)
ca_cert.save(path.join(WORKSPACE, "ca_cert_pem.crt"))
ca_cert.save(path.join(WORKSPACE, "ca_cert_der.crt"), encoding_type=SPSDKEncoding.DER)
print(f"The CA Certificate was created in der and pem format in {WORKSPACE}")
The CA Certificate was created in der and pem format in workspace/
# Create leaf certificate signed by private key of the CA certificate
# Note that the CA flag is set to False
subject_crt1 = generate_name([{"COMMON_NAME": "second"}, {"COUNTRY_NAME": "CZ"}])
public_key_2048_subject = PublicKeyRsa.load(path.join(KEYS_DIR, "crt1_publickey_rsa2048.pem"))
crt1 = Certificate.generate_certificate(
subject=subject_crt1,
issuer=issuer,
subject_public_key=public_key_2048_subject,
issuer_private_key=private_key_2048_ca,
serial_number=0x3CC30000BABADEDA,
duration=20 * 365,
extensions=generate_extensions(
{"BASIC_CONSTRAINTS": {"ca": False, "path_length": None}},
),
)
# Save certificates in two formats (pem and der)
crt1.save(path.join(WORKSPACE, "crt1_pem.crt"))
crt1.save(path.join(WORKSPACE, "crt1_der.crt"), encoding_type=SPSDKEncoding.DER)
print(
f"The first chain certificate (signed by CA certificate) was created in der and pem format in {WORKSPACE}."
)
The first chain certificate (signed by CA certificate) was created in der and pem format in workspace/.
# Create intermediate certificate signed by private key of the CA certificate
# Note that the CA flag is set to True
subject_crt2 = generate_name([{"COMMON_NAME": "third"}, {"COUNTRY_NAME": "CZ"}])
private_key_2048_subject_1 = PrivateKeyRsa.load(
path.join(KEYS_DIR, "intermediate_privatekey_rsa2048.pem")
)
public_key_2048_subject_1 = PublicKeyRsa.load(
path.join(KEYS_DIR, "intermediate_publickey_rsa2048.pem")
)
crt2 = Certificate.generate_certificate(
subject=subject_crt2,
issuer=issuer,
subject_public_key=public_key_2048_subject_1,
issuer_private_key=private_key_2048_ca,
serial_number=0x2,
duration=20 * 365,
extensions=generate_extensions(
{"BASIC_CONSTRAINTS": {"ca": True, "path_length": 3}},
),
)
# Save certificates in two formats (pem and der)
crt2.save(path.join(WORKSPACE, "intermediate_crt_pem.crt"))
crt2.save(path.join(WORKSPACE, "intermediate_crt_der.crt"), encoding_type=SPSDKEncoding.DER)
print(
f"The intermediate certificate (signed by CA certificate) was created in der and pem format in {WORKSPACE}."
)
The intermediate certificate (signed by CA certificate) was created in der and pem format in workspace/.
# Create leaf certificate signed by private key of the intermediate certificate
# Note that the CA flag is set to False
subject_crt3 = generate_name([{"COMMON_NAME": "fourth"}, {"COUNTRY_NAME": "CZ"}])
issuer_crt3 = subject_crt2
public_key_2048_subject_2 = PublicKeyRsa.load(path.join(KEYS_DIR, "crt2_publickey_rsa2048.pem"))
assert isinstance(public_key_2048_subject_2, PublicKeyRsa)
crt3 = Certificate.generate_certificate(
subject=subject_crt3,
issuer=issuer_crt3,
subject_public_key=public_key_2048_subject_2,
issuer_private_key=private_key_2048_subject_1,
serial_number=0x3CC30000BABADEDA,
duration=20 * 365,
extensions=generate_extensions(
{"BASIC_CONSTRAINTS": {"ca": False, "path_length": None}},
),
)
# Save certificates in two formats (pem and der)
crt3.save(path.join(WORKSPACE, "crt2_pem.crt"))
crt3.save(path.join(WORKSPACE, "crt2_der.crt"), encoding_type=SPSDKEncoding.DER)
print(f"The second certificate in a chain was created in der and pem format in {WORKSPACE}.")
The second certificate in a chain was created in der and pem format in workspace/.
2.2 Chain of Trust validation#
# Example provides the usage of certificates validation. It validates previously created chains.
from spsdk.crypto.certificate import Certificate, validate_certificate_chain
from spsdk.exceptions import SPSDKError
# Load public key of CA certificate
ca0_pubkey_rsa2048 = PublicKeyRsa.load(path.join(KEYS_DIR, "ca_publickey_rsa2048.pem"))
# Load CA certificate
ca_cert = Certificate.load(path.join(WORKSPACE, "ca_cert_pem.crt"))
# Compare CA's public key from file and the one from certificate
if ca0_pubkey_rsa2048 != ca_cert.get_public_key():
raise SPSDKError("Keys are not the same (the one from disc and the one from cert)")
# Load certificate, which is singed by CA
crt = Certificate.load(path.join(WORKSPACE, "crt1_pem.crt"))
if not ca_cert.validate_subject(crt):
raise SPSDKError("The certificate is not valid")
print("The certificate was signed by the CA.")
# # Load chain of certificate
chain = ["crt2_pem.crt", "intermediate_crt_pem.crt", "ca_cert_pem.crt"]
chain_certs = [Certificate.load(path.join(WORKSPACE, cert_name)) for cert_name in chain]
# Validate the chain (if corresponding items in chain are singed by one another)
if not validate_certificate_chain(chain_certs):
raise SPSDKError("The certificate chain is not valid")
print("The chain of certificates is valid.")
# Checks if CA flag is set correctly
if chain_certs[0].ca:
raise SPSDKError("CA flag is set")
if not chain_certs[1].ca:
raise SPSDKError("CA flag is not set")
if not chain_certs[2].ca:
raise SPSDKError("CA flag is not set")
The certificate was signed by the CA.
The chain of certificates is valid.