Skip to content

Instantly share code, notes, and snippets.

@bryan-hunt
Last active January 3, 2022 17:14
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save bryan-hunt/a6c27e99391213e462ac6fc58ed84929 to your computer and use it in GitHub Desktop.
Save bryan-hunt/a6c27e99391213e462ac6fc58ed84929 to your computer and use it in GitHub Desktop.
Example Certificate Provisioning

Microchip Secure Element Tools

These tools are used to set up an example chain of trust ecosystem for use with Microchip ATECC508A and ATECC608A parts. Included are utilities to create the ecosystem keys and certificates.

Dependencies

Python scripts will require python 3 to be installed. Once python is installed install the requirements (from the path of this file):

> pip install -r requirements.txt

The application will have to built by loading the microchip_security_tool.sln project file and building either the x86 or x64 version of the application

Set up a Certificate Ecosystem

The first step is to set up a certificate chain that mirrors how a secure iot ecosystem would be configured. For this we'll create a dummy root certificate authority (normally this would be handled by a third party, or an internal PKI system) and an intermediate certificate authority that mirrors the signing hardware security modules (HSM) that are used in the Microchip facility during manufacturing of security devices.

Create the Root CA

> ca_create_root.py 

Create the Signing CA

> ca_create_signer.py <code_from_aws>

code_from_aws is the registration code required to verify the private key used for the signing certificate. This is obtained where you upload the signer into the AWS IOT cacertificatehub.

  1. In the AWS console navigate to AWS IOT -> Secure -> CAs
  2. Click the "Register" button and then the "Register CA" button
  3. Copy the registration code in "Step 2"
  4. Run ca_create_signer.py with the registration code
  5. Select the CA certificate (default is signer-ca.crt)
  6. Select the Verification certificate (default is verificationCert.crt)
  7. Select Activate CA certificate & Enable auto-registration
  8. Click the "Register CA certificate" button
  9. Make note of the certificate ARN hash for later (the first few digits)

The intermediate signing certificate is now active and allows for automatic registration of new devices when they are created (requires lambda functions). At this time we are not going to configure the lambda functions for automatic registration so we'll upload the devices certificate manually.

Create the Device Certificate

  1. Run the ca_create_device script:
> ca_create_device.py

This step mirrors the production provisioning process where Microchip uses HSMs to sign each device produced and loads the certificate information into them. This performs the certificate creation and signing process but does not load the certificates into the device (provisioning).

If lambda functions have been set up for Just In Time Registration this following step may be skipped, otherwise we have to load the device certificate into the aws certificatehub manually.

  1. In the AWS console navigate to AWS IOT -> Secure -> Certificates
  2. Click the "Create" button
  3. Select "Use my certificate" path by clicking the "Get started" button
  4. Select the CA that was created earlier (see the ARN hash we noted)
  5. Click "Register Certificates"
  6. Select the device certificate (default device.crt)
  7. Click "Register certificates"
  8. Find the newly create "inactive" certificate and select it
  9. Attach Policies and Things to the new certificate
  10. Activate the certificate

Finally provision the device

ca_write_certs.py

import os
import base64
import argparse
import pytz
import binascii
import ctypes
import datetime
import cryptography
from cryptoauthlib import *
from cryptography import x509
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives import serialization
def pubnums_to_bytes(pub_nums):
try:
pubkey = pub_nums.x.to_bytes(32, byteorder='big', signed=False)
pubkey += pub_nums.y.to_bytes(32, byteorder='big', signed=False)
except AttributeError:
pubkey = bytes(bytearray.fromhex(hex(pub_nums.x)[2:-1] + hex(pub_nums.y)[2:-1]))
return pubkey
def device_cert_sn(size, builder):
"""Cert serial number is the SHA256(Subject public key + Encoded dates)"""
# Get the public key as X and Y integers concatenated
pubkey = pubnums_to_bytes(builder._public_key.public_numbers())
# Get the encoded dates
expire_years = 0
enc_dates = bytearray(b'\x00'*3)
enc_dates[0] = (enc_dates[0] & 0x07) | ((((builder._not_valid_before.year - 2000) & 0x1F) << 3) & 0xFF)
enc_dates[0] = (enc_dates[0] & 0xF8) | ((((builder._not_valid_before.month) & 0x0F) >> 1) & 0xFF)
enc_dates[1] = (enc_dates[1] & 0x7F) | ((((builder._not_valid_before.month) & 0x0F) << 7) & 0xFF)
enc_dates[1] = (enc_dates[1] & 0x83) | (((builder._not_valid_before.day & 0x1F) << 2) & 0xFF)
enc_dates[1] = (enc_dates[1] & 0xFC) | (((builder._not_valid_before.hour & 0x1F) >> 3) & 0xFF)
enc_dates[2] = (enc_dates[2] & 0x1F) | (((builder._not_valid_before.hour & 0x1F) << 5) & 0xFF)
enc_dates[2] = (enc_dates[2] & 0xE0) | ((expire_years & 0x1F) & 0xFF)
enc_dates = bytes(enc_dates)
# SAH256 hash of the public key and encoded dates
digest = hashes.Hash(hashes.SHA256(), backend=default_backend())
digest.update(pubkey)
digest.update(enc_dates)
raw_sn = bytearray(digest.finalize()[:size])
raw_sn[0] = raw_sn[0] & 0x7F # Force MSB bit to 0 to ensure positive integer
raw_sn[0] = raw_sn[0] | 0x40 # Force next bit to 1 to ensure the integer won't be trimmed in ASN.1 DER encoding
try:
return int.from_bytes(raw_sn, byteorder='big', signed=False)
except AttributeError:
return int(binascii.hexlify(raw_sn), 16)
def create_device_cert(signer_file, signer_key_file):
# Make sure files exist
if not (os.path.isfile(signer_file) and os.path.isfile(signer_key_file)):
raise FileNotFoundError('Failed to find {}, {}, or {}'.format(signer_file, signer_key_file))
assert atcab_init(cfg_ateccx08a_kithid_default()) == Status.ATCA_SUCCESS
# Load device public key
public_key = bytearray(64)
assert Status.ATCA_SUCCESS == atcab_get_pubkey(0, public_key)
# Convert to the key to PEM format
public_key_pem = bytearray.fromhex('3059301306072A8648CE3D020106082A8648CE3D03010703420004') + public_key
public_key_pem = '-----BEGIN PUBLIC KEY-----\n' + base64.b64encode(public_key_pem).decode('ascii') + '\n-----END PUBLIC KEY-----'
# Convert the key into the cryptography format
public_key = serialization.load_pem_public_key(public_key_pem.encode('ascii'), default_backend())
# Load the Signing key from the file
with open(signer_key_file, 'rb') as f:
signer_ca_priv_key = serialization.load_pem_private_key(data=f.read(), password=None, backend=default_backend())
signer_ca_pub_key = bytearray(signer_ca_priv_key.public_key().public_bytes(encoding=serialization.Encoding.X962,
format=serialization.PublicFormat.UncompressedPoint)[1:])
# Load the Signing Certificate from the file
with open(signer_file, 'rb') as f:
signer_ca_cert = x509.load_pem_x509_certificate(f.read(), default_backend())
# Build certificate
builder = x509.CertificateBuilder()
builder = builder.issuer_name(signer_ca_cert.subject)
# Device cert must have minutes and seconds set to 0
builder = builder.not_valid_before(datetime.datetime.now(tz=pytz.utc).replace(minute=0,second=0))
# Should be year 9999, but this doesn't work on windows
builder = builder.not_valid_after(datetime.datetime(3000, 12, 31, 23, 59, 59))
builder = builder.subject_name(x509.Name([
x509.NameAttribute(x509.oid.NameOID.ORGANIZATION_NAME, u'Example Inc'),
x509.NameAttribute(x509.oid.NameOID.COMMON_NAME, u'Example Device')]))
builder = builder.public_key(public_key)
# Device certificate is generated from certificate dates and public key
builder = builder.serial_number(device_cert_sn(16, builder))
# Subject Key ID is used as the thing name and MQTT client ID and is required for this demo
builder = builder.add_extension(
x509.SubjectKeyIdentifier.from_public_key(public_key),
critical=False)
issuer_ski = signer_ca_cert.extensions.get_extension_for_class(x509.SubjectKeyIdentifier)
builder = builder.add_extension(
x509.AuthorityKeyIdentifier.from_issuer_subject_key_identifier(issuer_ski),
critical=False)
# Sign certificate
device_cert = builder.sign(private_key=signer_ca_priv_key, algorithm=hashes.SHA256(), backend=default_backend())
with open('device.crt', 'wb') as f:
f.write(device_cert.public_bytes(encoding=serialization.Encoding.PEM))
print('Done');
if __name__ == '__main__':
# Create argument parser to document script use
parser = argparse.ArgumentParser(description='Provisions the kit by requesting a CSR and returning signed certificates.')
parser.add_argument('--cert', default='signer-ca.crt', help='Certificate file of the signer')
parser.add_argument('--key', default='signer-ca.key', help='Private Key file of the signer')
args = parser.parse_args()
assert atcab_init(cfg_ateccx08a_kithid_default()) == Status.ATCA_SUCCESS
create_device_cert(args.cert, args.key)
import os
import datetime
import pytz
import cryptography
import argparse
from cryptography import x509
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import ec
crypto_be = cryptography.hazmat.backends.default_backend()
def random_cert_sn(size):
"""Create a positive, non-trimmable serial number for X.509 certificates"""
raw_sn = bytearray(os.urandom(size))
raw_sn[0] = raw_sn[0] & 0x7F # Force MSB bit to 0 to ensure positive integer
raw_sn[0] = raw_sn[0] | 0x40 # Force next bit to 1 to ensure the integer won't be trimmed in ASN.1 DER encoding
return int.from_bytes(raw_sn, byteorder='big', signed=False)
def load_or_create_key(filename):
# Create or load a root CA key pair
priv_key = None
if os.path.isfile(filename):
# Load existing key
with open(filename, 'rb') as f:
priv_key = serialization.load_pem_private_key(
data=f.read(),
password=None,
backend=crypto_be)
if priv_key == None:
# No private key loaded, generate new one
priv_key = ec.generate_private_key(ec.SECP256R1(), crypto_be)
# Save private key to file
with open(filename, 'wb') as f:
pem_key = priv_key.private_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PrivateFormat.PKCS8,
encryption_algorithm=serialization.NoEncryption())
f.write(pem_key)
return priv_key
def create_root_cert_key(certfile, keyfile, pubfile):
# Create or load a root CA key pair
print('\nLoading root CA key')
root_ca_priv_key = load_or_create_key(keyfile)
# Create root CA certificate
print('\nGenerating self-signed root CA certificate')
builder = x509.CertificateBuilder()
builder = builder.serial_number(random_cert_sn(16))
# Please note that the name of the root CA is also part of the signer certificate and thus, it's
# part of certificate definition in the firmware (g_cert_elements_1_signer). If this name is
# changed, it will also need to be changed in the firmware.
builder = builder.issuer_name(x509.Name([
x509.NameAttribute(x509.oid.NameOID.ORGANIZATION_NAME, u'Example Inc'),
x509.NameAttribute(x509.oid.NameOID.COMMON_NAME, u'Example Root CA')]))
builder = builder.not_valid_before(datetime.datetime.now(tz=pytz.utc))
builder = builder.not_valid_after(builder._not_valid_before.replace(year=builder._not_valid_before.year + 25))
builder = builder.subject_name(builder._issuer_name)
builder = builder.public_key(root_ca_priv_key.public_key())
builder = builder.add_extension(
x509.SubjectKeyIdentifier.from_public_key(root_ca_priv_key.public_key()),
critical=False)
builder = builder.add_extension(
x509.BasicConstraints(ca=True, path_length=None),
critical=True)
# Self-sign certificate
root_ca_cert = builder.sign(
private_key=root_ca_priv_key,
algorithm=hashes.SHA256(),
backend=crypto_be)
# Write root CA certificate to file
with open(certfile, 'wb') as f:
print(' Saving to ' + f.name)
f.write(root_ca_cert.public_bytes(encoding=serialization.Encoding.PEM))
# Save root public key
with open(pubfile, 'wb') as f:
f.write(root_ca_cert.public_key().public_bytes(serialization.Encoding.PEM, serialization.PublicFormat.SubjectPublicKeyInfo))
print('\nDone')
if __name__ == '__main__':
# Create argument parser to document script use
parser = argparse.ArgumentParser(description='Create a root certificate and key')
parser.add_argument('--out', default='root-ca.crt', help='Device Certificate (PEM)')
parser.add_argument('--key', default='root-ca.key', help='Root public key (PEM)')
parser.add_argument('--pubkey', default='root-pub.pem', help='Root public key (PEM)')
args = parser.parse_args()
create_root_cert_key(args.out, args.key, args.pubkey)
import os
import pytz
import argparse
import datetime
import cryptography
from cryptography import x509
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import ec
crypto_be = cryptography.hazmat.backends.default_backend()
def random_cert_sn(size):
"""Create a positive, non-trimmable serial number for X.509 certificates"""
raw_sn = bytearray(os.urandom(size))
raw_sn[0] = raw_sn[0] & 0x7F # Force MSB bit to 0 to ensure positive integer
raw_sn[0] = raw_sn[0] | 0x40 # Force next bit to 1 to ensure the integer won't be trimmed in ASN.1 DER encoding
return int.from_bytes(raw_sn, byteorder='big', signed=False)
def load_or_create_key(filename):
# Create or load a root CA key pair
priv_key = None
if os.path.isfile(filename):
# Load existing key
with open(filename, 'rb') as f:
priv_key = serialization.load_pem_private_key(
data=f.read(),
password=None,
backend=crypto_be)
if priv_key == None:
# No private key loaded, generate new one
priv_key = ec.generate_private_key(ec.SECP256R1(), crypto_be)
# Save private key to file
with open(filename, 'wb') as f:
pem_key = priv_key.private_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PrivateFormat.PKCS8,
encryption_algorithm=serialization.NoEncryption())
f.write(pem_key)
return priv_key
def create_intermediate_cert(reg_code, verifyfile, certfile, keyfile, rootfile, rootkeyfile):
print('\nLoading signer CA key')
signer_ca_priv_key = load_or_create_key(keyfile)
print('\nLoading root CA key')
if not os.path.isfile(rootkeyfile):
raise Exception('Failed to find root CA key file, ' + rootkeyfile + '. Have you run ca_create_root first?')
with open(rootkeyfile, 'rb') as f:
print(' Loading from ' + f.name)
root_ca_priv_key = serialization.load_pem_private_key(
data=f.read(),
password=None,
backend=crypto_be)
print('\nLoading Root CA certificate')
if not os.path.isfile(rootfile):
raise Exception('Failed to find root CA certificate file, ' + rootfile + '. Have you run ca_create_root first?')
with open(rootfile, 'rb') as f:
print(' Loading from ' + f.name)
root_ca_cert = x509.load_pem_x509_certificate(f.read(), crypto_be)
# Create signer CA certificate
print('\nGenerating Signer/Intermediate Certificate')
# Please note that the structure of the signer certificate is part of certificate definition in the firmware
# If any part of it is changed, it will also need to be changed in the firmware.
builder = x509.CertificateBuilder()
builder = builder.serial_number(random_cert_sn(16))
builder = builder.subject_name(x509.Name([
x509.NameAttribute(x509.oid.NameOID.ORGANIZATION_NAME, u'Example Inc'),
x509.NameAttribute(x509.oid.NameOID.COMMON_NAME, u'Example Signer FFFF')]))
builder = builder.issuer_name(root_ca_cert.subject)
builder = builder.not_valid_before(datetime.datetime.now(tz=pytz.utc))
builder = builder.not_valid_after(builder._not_valid_before.replace(year=builder._not_valid_before.year + 10))
builder = builder.public_key(signer_ca_priv_key.public_key())
builder = builder.add_extension(
x509.BasicConstraints(ca=True, path_length=0),
critical=True)
builder = builder.add_extension(
x509.KeyUsage(
digital_signature=True,
content_commitment=False,
key_encipherment=False,
data_encipherment=False,
key_agreement=False,
key_cert_sign=True,
crl_sign=True,
encipher_only=False,
decipher_only=False),
critical=True)
builder = builder.add_extension(
x509.SubjectKeyIdentifier.from_public_key(signer_ca_priv_key.public_key()),
critical=False)
issuer_ski = root_ca_cert.extensions.get_extension_for_class(x509.SubjectKeyIdentifier)
builder = builder.add_extension(
x509.AuthorityKeyIdentifier.from_issuer_subject_key_identifier(issuer_ski),
critical=False)
# Sign signer certificate with root
signer_ca_cert = builder.sign(
private_key=root_ca_priv_key,
algorithm=hashes.SHA256(),
backend=crypto_be)
# Write signer CA certificate to file
with open(certfile, 'wb') as f:
print(' Saving to ' + f.name)
f.write(signer_ca_cert.public_bytes(encoding=serialization.Encoding.PEM))
# Generate a verification certificate around the registration code (subject common name)
print('\nGenerating signer CA AWS verification certificate')
builder = x509.CertificateBuilder()
builder = builder.serial_number(random_cert_sn(16))
builder = builder.issuer_name(signer_ca_cert.subject)
builder = builder.not_valid_before(datetime.datetime.now(tz=pytz.utc))
builder = builder.not_valid_after(builder._not_valid_before.replace(day=builder._not_valid_before.day + 1))
builder = builder.subject_name(x509.Name([x509.NameAttribute(x509.oid.NameOID.COMMON_NAME, reg_code)]))
builder = builder.public_key(signer_ca_cert.public_key())
signer_ca_ver_cert = builder.sign(
private_key=signer_ca_priv_key,
algorithm=hashes.SHA256(),
backend=crypto_be)
# Write signer CA certificate to file for reference
with open(verifyfile, 'wb') as f:
print(' Saved to ' + f.name)
f.write(signer_ca_ver_cert.public_bytes(encoding=serialization.Encoding.PEM))
print('\nDone')
if __name__ == '__main__':
# Create argument parser to document script use
parser = argparse.ArgumentParser(description='Create a Signer/Intermediate Certificate')
parser.add_argument('code', help='AWS Verification Code')
parser.add_argument('--vcert', default='verificationCert.crt', help='AWS Verification Certificate')
parser.add_argument('--cert', default='signer-ca.crt', help='Certificate file of the signer')
parser.add_argument('--key', default='signer-ca.key', help='Private Key file of the signer')
parser.add_argument('--root', default='root-ca.crt', help='Root Certificate')
parser.add_argument('--rootkey', default='root-ca.key', help='Root Key')
args = parser.parse_args()
create_intermediate_cert(args.code, args.vcert, args.cert, args.key, args.root, args.rootkey)
import os
import base64
import argparse
import pytz
import binascii
import ctypes
import datetime
import cryptography
from cryptoauthlib import *
from cryptography import x509
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import serialization
ROOT_PUBKEY_SLOT = 15
ATCACERT_DEF_SIGNER_CONFIG_ELEMENTS = (atcacert_cert_element_t*2)(
atcacert_cert_element_t(
# id='IssueDate',
device_loc = atcacert_device_loc_t(**{
'zone': atcacert_device_zone_t.DEVZONE_DATA,
'slot': 14,
'is_genkey': 0,
'offset': 35-13,
'count': 13}),
cert_loc= atcacert_cert_loc_t(offset=97, count=13)
),
atcacert_cert_element_t(
# id='ExpireDate',
device_loc = atcacert_device_loc_t(**{
'zone': atcacert_device_zone_t.DEVZONE_DATA,
'slot': 14,
'is_genkey': 0,
'offset': 50-13,
'count': 13}),
cert_loc = atcacert_cert_loc_t(offset=112, count=13)
)
)
ATCACERT_DEF_SIGNER_CONFIG = {
'type': atcacert_cert_type_t.CERTTYPE_X509,
'template_id': 1,
'chain_id': 0,
'private_key_slot': 0,
'sn_source': atcacert_cert_sn_src_t.SNSRC_STORED,
'cert_sn_dev_loc': {
'zone': atcacert_device_zone_t.DEVZONE_DATA,
'slot': 14,
'is_genkey': 0,
'offset': 20-16,
'count': 16
},
'issue_date_format': atcacert_date_format_t.DATEFMT_RFC5280_UTC,
'expire_date_format': atcacert_date_format_t.DATEFMT_RFC5280_GEN,
'tbs_cert_loc': {'offset': 4, 'count': 370},
'expire_years': 10,
'public_key_dev_loc': {
'zone': atcacert_device_zone_t.DEVZONE_DATA,
'slot': 11,
'is_genkey': 0,
'offset': 0,
'count': 72
},
'comp_cert_dev_loc': {
'zone': atcacert_device_zone_t.DEVZONE_DATA,
'slot': 12,
'is_genkey': 0,
'offset': 0,
'count': 72
},
'std_cert_elements' : [
{'offset': 206, 'count': 64},
{'offset': 386, 'count': 74},
{'offset': 97, 'count': 13},
{'offset': 112, 'count': 13},
{'offset': 175, 'count': 4},
{'offset': 15, 'count': 16},
{'offset': 354, 'count': 20},
{'offset': 321, 'count': 20},
],
'cert_elements': ctypes.cast(ATCACERT_DEF_SIGNER_CONFIG_ELEMENTS, ctypes.POINTER(atcacert_cert_element_t)),
'cert_elements_count': 2
}
ATCACERT_DEF_DEVICE_CONFIG = {
'type': atcacert_cert_type_t.CERTTYPE_X509,
'template_id': 2,
'chain_id': 0,
'private_key_slot': 0,
'sn_source': atcacert_cert_sn_src_t.SNSRC_PUB_KEY_HASH,
'cert_sn_dev_loc': {
'zone': atcacert_device_zone_t.DEVZONE_NONE,
'slot': 0,
'is_genkey': 0,
'offset': 0,
'count': 0
},
'issue_date_format': atcacert_date_format_t.DATEFMT_RFC5280_UTC,
'expire_date_format': atcacert_date_format_t.DATEFMT_RFC5280_GEN,
'tbs_cert_loc': {'offset': 4, 'count': 335},
'expire_years': 0,
'public_key_dev_loc': {
'zone': atcacert_device_zone_t.DEVZONE_DATA,
'slot': 0,
'is_genkey': 1,
'offset': 0,
'count': 64
},
'comp_cert_dev_loc': {
'zone': atcacert_device_zone_t.DEVZONE_DATA,
'slot': 10,
'is_genkey': 0,
'offset': 0,
'count': 72
},
'std_cert_elements' : [
{'offset': 207, 'count': 64},
{'offset': 351, 'count': 75},
{'offset': 101, 'count': 13},
{'offset': 0, 'count': 0},
{'offset': 93, 'count': 4},
{'offset': 15, 'count': 16},
{'offset': 319, 'count': 20},
{'offset': 286, 'count': 20},
]
}
def write_root(root_file):
# Load the Signing Certificate from the file
with open(root_file, 'rb') as f:
cert = x509.load_pem_x509_certificate(f.read(), default_backend())
root_pubkey = bytearray(cert.public_key().public_bytes(encoding=serialization.Encoding.X962,
format=serialization.PublicFormat.UncompressedPoint)[1:])
assert Status.ATCA_SUCCESS == atcab_write_pubkey(ROOT_PUBKEY_SLOT, root_pubkey)
def write_signer(signer_file):
# Load the Signing Certificate from the file
with open(signer_file, 'rb') as f:
cert = x509.load_pem_x509_certificate(f.read(), default_backend())
cert_def = atcacert_def_t(**ATCACERT_DEF_SIGNER_CONFIG)
cert = cert.public_bytes(encoding=serialization.Encoding.DER)
assert Status.ATCA_SUCCESS == atcacert_write_cert(cert_def, cert, len(cert))
def write_device(device_file):
# Load the Signing Certificate from the file
with open(device_file, 'rb') as f:
cert = x509.load_pem_x509_certificate(f.read(), default_backend())
cert_def = atcacert_def_t(**ATCACERT_DEF_DEVICE_CONFIG)
cert = cert.public_bytes(encoding=serialization.Encoding.DER)
# Write the device certificate
assert Status.ATCA_SUCCESS == atcacert_write_cert(cert_def, cert, len(cert))
if __name__ == '__main__':
# Create argument parser to document script use
parser = argparse.ArgumentParser(description='Programs a certificate chain into a device using the provided definitions')
parser.add_argument('--cert', default='device.crt', help='Certificate file of the device (PEM)')
parser.add_argument('--signer', default='signer-ca.crt', help='Certificate file of the signer (PEM)')
parser.add_argument('--root', default='root-ca.crt', help='Device Certificate (PEM)')
args = parser.parse_args()
assert atcab_init(cfg_ateccx08a_kithid_default()) == Status.ATCA_SUCCESS
write_root(args.root)
write_signer(args.signer)
write_device(args.cert)
#include "atcacert/atcacert_def.h"
#include "atca_cert_chain.h"
const atcacert_def_t g_cert_def_0_root = {
.type = CERTTYPE_X509,
.template_id = 0,
.public_key_dev_loc = {
.zone = DEVZONE_DATA,
.slot = 15,
.is_genkey = 0,
.offset = 0,
.count = 72
}
};
const atcacert_cert_element_t g_cert_elements_1_signer[] = {
{
.id = "IssueDate",
.device_loc = {
.zone = DEVZONE_DATA,
.slot = 14,
.is_genkey = 0,
.offset = 35-13,
.count = 13
},
.cert_loc = {
.offset = 97,
.count = 13
}
},
{
.id = "ExpireDate",
.device_loc = {
.zone = DEVZONE_DATA,
.slot = 14,
.is_genkey = 0,
.offset = 50-13,
.count = 13
},
.cert_loc = {
.offset = 112,
.count = 13
}
}
};
const uint8_t g_cert_template_1_signer[] = {
0x30, 0x82, 0x01, 0xc8, 0x30, 0x82, 0x01, 0x6e, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x10, 0x57,
0x06, 0x2e, 0xf0, 0x05, 0xea, 0x8a, 0x70, 0x44, 0xff, 0x1b, 0x90, 0x00, 0x21, 0x78, 0xd6, 0x30,
0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02, 0x30, 0x30, 0x31, 0x14, 0x30,
0x12, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x0b, 0x45, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x20,
0x49, 0x6e, 0x63, 0x31, 0x18, 0x30, 0x16, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x0f, 0x45, 0x78,
0x61, 0x6d, 0x70, 0x6c, 0x65, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20, 0x43, 0x41, 0x30, 0x1e, 0x17,
0x0d, 0x31, 0x37, 0x30, 0x36, 0x30, 0x37, 0x31, 0x37, 0x35, 0x36, 0x31, 0x32, 0x5a, 0x17, 0x0d,
0x32, 0x37, 0x30, 0x36, 0x30, 0x37, 0x31, 0x37, 0x35, 0x36, 0x31, 0x32, 0x5a, 0x30, 0x34, 0x31,
0x14, 0x30, 0x12, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x0b, 0x45, 0x78, 0x61, 0x6d, 0x70, 0x6c,
0x65, 0x20, 0x49, 0x6e, 0x63, 0x31, 0x1c, 0x30, 0x1a, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x13,
0x45, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x20, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x72, 0x20, 0x46,
0x46, 0x46, 0x46, 0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01,
0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0x03, 0x42, 0x00, 0x04, 0xb1, 0xf5,
0x9c, 0xbe, 0x22, 0x11, 0x7f, 0x28, 0x2f, 0x7f, 0x2e, 0xcb, 0xa2, 0x8c, 0x30, 0x3b, 0xae, 0x59,
0x45, 0xb9, 0x5c, 0x0e, 0xba, 0xaa, 0x9b, 0x81, 0x73, 0x52, 0x63, 0x41, 0xbf, 0x37, 0x3c, 0x2e,
0xdd, 0xcd, 0xea, 0x0e, 0x7c, 0x9d, 0x90, 0xea, 0x25, 0x9c, 0x64, 0xeb, 0xc6, 0x54, 0x47, 0x32,
0x81, 0x63, 0xbf, 0x42, 0x5f, 0xdd, 0x5a, 0x3f, 0xd5, 0x71, 0x81, 0x9b, 0x77, 0x44, 0xa3, 0x66,
0x30, 0x64, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06,
0x01, 0x01, 0xff, 0x02, 0x01, 0x00, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff,
0x04, 0x04, 0x03, 0x02, 0x01, 0x86, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04,
0x14, 0x81, 0x1d, 0xc6, 0x7c, 0x0f, 0x18, 0x2b, 0x65, 0x96, 0xeb, 0x22, 0x73, 0xdb, 0xf3, 0x23,
0x63, 0x6d, 0x79, 0x0f, 0xc8, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16,
0x80, 0x14, 0xdb, 0x2a, 0x0d, 0x06, 0x05, 0xc7, 0x98, 0xbc, 0xda, 0xc0, 0x34, 0x67, 0x66, 0xf4,
0xe2, 0xb0, 0x61, 0xa3, 0xd2, 0xc8, 0x30, 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04,
0x03, 0x02, 0x03, 0x48, 0x00, 0x30, 0x45, 0x02, 0x20, 0x49, 0xfe, 0xdf, 0xc9, 0x94, 0xe3, 0x07,
0xdb, 0x08, 0xb3, 0x99, 0x9e, 0x04, 0xe4, 0x78, 0xe5, 0xf8, 0xb9, 0x09, 0xa9, 0xf0, 0x41, 0x66,
0xc6, 0x69, 0x1b, 0x87, 0x30, 0x86, 0x10, 0xaf, 0x64, 0x02, 0x21, 0x00, 0xc8, 0xd6, 0x86, 0x61,
0x94, 0x95, 0xdb, 0x45, 0xb3, 0x40, 0x8e, 0xac, 0x14, 0x9a, 0x19, 0xb6, 0x8c, 0x5c, 0x79, 0x9d,
0x06, 0xcb, 0x52, 0x08, 0xa0, 0x1f, 0x49, 0x8b, 0x22, 0x4e, 0x52, 0x71
};
const atcacert_def_t g_cert_def_1_signer = {
.type = CERTTYPE_X509,
.template_id = 1,
.chain_id = 0,
.private_key_slot = 0,
.sn_source = SNSRC_STORED,
.cert_sn_dev_loc = {
.zone = DEVZONE_DATA,
.slot = 14,
.is_genkey = 0,
.offset = 20-16,
.count = 16
},
.issue_date_format = DATEFMT_RFC5280_UTC,
.expire_date_format = DATEFMT_RFC5280_UTC,
.tbs_cert_loc = {
.offset = 4,
.count = 370
},
.expire_years = 10,
.public_key_dev_loc = {
.zone = DEVZONE_DATA,
.slot = 11,
.is_genkey = 0,
.offset = 0,
.count = 72
},
.comp_cert_dev_loc = {
.zone = DEVZONE_DATA,
.slot = 12,
.is_genkey = 0,
.offset = 0,
.count = 72
},
.std_cert_elements = {
{ // STDCERT_PUBLIC_KEY
.offset = 206,
.count = 64
},
{ // STDCERT_SIGNATURE
.offset = 386,
.count = 74
},
{ // STDCERT_ISSUE_DATE
.offset = 97,
.count = 13
},
{ // STDCERT_EXPIRE_DATE
.offset = 112,
.count = 13
},
{ // STDCERT_SIGNER_ID
.offset = 175,
.count = 4
},
{ // STDCERT_CERT_SN
.offset = 15,
.count = 16
},
{ // STDCERT_AUTH_KEY_ID
.offset = 354,
.count = 20
},
{ // STDCERT_SUBJ_KEY_ID
.offset = 321,
.count = 20
}
},
.cert_elements = g_cert_elements_1_signer,
.cert_elements_count = sizeof(g_cert_elements_1_signer) / sizeof(g_cert_elements_1_signer[0]),
.cert_template = g_cert_template_1_signer,
.cert_template_size = sizeof(g_cert_template_1_signer),
.ca_cert_def = &g_cert_def_0_root,
};
const uint8_t g_cert_template_2_device[] = {
0x30, 0x82, 0x01, 0xa6, 0x30, 0x82, 0x01, 0x4b, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x10, 0x41,
0xa6, 0x8b, 0xe4, 0x36, 0xdd, 0xc3, 0xd8, 0x39, 0xfa, 0xbd, 0xd7, 0x27, 0xd9, 0x74, 0xe7, 0x30,
0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02, 0x30, 0x34, 0x31, 0x14, 0x30,
0x12, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x0b, 0x45, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x20,
0x49, 0x6e, 0x63, 0x31, 0x1c, 0x30, 0x1a, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x13, 0x45, 0x78,
0x61, 0x6d, 0x70, 0x6c, 0x65, 0x20, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x72, 0x20, 0x46, 0x46, 0x46,
0x46, 0x30, 0x20, 0x17, 0x0d, 0x31, 0x37, 0x30, 0x37, 0x31, 0x30, 0x32, 0x30, 0x30, 0x30, 0x30,
0x30, 0x5a, 0x18, 0x0f, 0x33, 0x30, 0x30, 0x30, 0x31, 0x32, 0x33, 0x31, 0x32, 0x33, 0x35, 0x39,
0x35, 0x39, 0x5a, 0x30, 0x2f, 0x31, 0x14, 0x30, 0x12, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x0b,
0x45, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x20, 0x49, 0x6e, 0x63, 0x31, 0x17, 0x30, 0x15, 0x06,
0x03, 0x55, 0x04, 0x03, 0x0c, 0x0e, 0x45, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x20, 0x44, 0x65,
0x76, 0x69, 0x63, 0x65, 0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02,
0x01, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0x03, 0x42, 0x00, 0x04, 0x96,
0x27, 0xf1, 0x3e, 0x80, 0xac, 0xf9, 0xd4, 0x12, 0xce, 0x3b, 0x0d, 0x68, 0xf7, 0x4e, 0xb2, 0xc6,
0x07, 0x35, 0x00, 0xb7, 0x78, 0x5b, 0xac, 0xe6, 0x50, 0x30, 0x54, 0x77, 0x7f, 0xc8, 0x62, 0x21,
0xce, 0xf2, 0x5a, 0x9a, 0x9e, 0x86, 0x40, 0xc2, 0x29, 0xd6, 0x4a, 0x32, 0x1e, 0xb9, 0x4a, 0x1b,
0x1c, 0x94, 0xf5, 0x39, 0x88, 0xae, 0xfe, 0x49, 0xcc, 0xfd, 0xbf, 0x8a, 0x0d, 0x34, 0xb8, 0xa3,
0x42, 0x30, 0x40, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x2d, 0xda,
0x6c, 0x36, 0xd5, 0xa5, 0x5a, 0xce, 0x97, 0x10, 0x3d, 0xbb, 0xaf, 0x9c, 0x66, 0x2a, 0xcd, 0x3e,
0xe6, 0xcf, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0xc6,
0x70, 0xe0, 0x5e, 0x8a, 0x45, 0x0d, 0xb8, 0x2c, 0x00, 0x2a, 0x40, 0x06, 0x39, 0x4c, 0x19, 0x58,
0x04, 0x35, 0x76, 0x30, 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02, 0x03,
0x49, 0x00, 0x30, 0x46, 0x02, 0x21, 0x00, 0xe1, 0xfc, 0x00, 0x23, 0xc1, 0x3d, 0x01, 0x3f, 0x22,
0x31, 0x0b, 0xf0, 0xb8, 0xf4, 0xf4, 0x22, 0xfc, 0x95, 0x96, 0x33, 0x9c, 0xb9, 0x62, 0xb1, 0xfc,
0x8a, 0x2d, 0xa8, 0x5c, 0xee, 0x67, 0x72, 0x02, 0x21, 0x00, 0xa1, 0x0d, 0x47, 0xe4, 0xfd, 0x0d,
0x15, 0xd8, 0xde, 0xa1, 0xb5, 0x96, 0x28, 0x4e, 0x7a, 0x0b, 0xbe, 0xcc, 0xec, 0xe8, 0x8e, 0xcc,
0x7a, 0x31, 0xb3, 0x00, 0x8b, 0xc0, 0x2e, 0x4f, 0x99, 0xc5
};
const atcacert_def_t g_cert_def_2_device = {
.type = CERTTYPE_X509,
.template_id = 2,
.chain_id = 0,
.private_key_slot = 0,
.sn_source = SNSRC_PUB_KEY_HASH,
.cert_sn_dev_loc = {
.zone = DEVZONE_NONE,
.slot = 0,
.is_genkey = 0,
.offset = 0,
.count = 0
},
.issue_date_format = DATEFMT_RFC5280_UTC,
.expire_date_format = DATEFMT_RFC5280_GEN,
.tbs_cert_loc = {
.offset = 4,
.count = 335
},
.expire_years = 0,
.public_key_dev_loc = {
.zone = DEVZONE_DATA,
.slot = 0,
.is_genkey = 1,
.offset = 0,
.count = 64
},
.comp_cert_dev_loc = {
.zone = DEVZONE_DATA,
.slot = 10,
.is_genkey = 0,
.offset = 0,
.count = 72
},
.std_cert_elements = {
{ // STDCERT_PUBLIC_KEY
.offset = 207,
.count = 64
},
{ // STDCERT_SIGNATURE
.offset = 351,
.count = 75
},
{ // STDCERT_ISSUE_DATE
.offset = 101,
.count = 13
},
{ // STDCERT_EXPIRE_DATE
.offset = 0,
.count = 0
},
{ // STDCERT_SIGNER_ID
.offset = 93,
.count = 4
},
{ // STDCERT_CERT_SN
.offset = 15,
.count = 16
},
{ // STDCERT_AUTH_KEY_ID
.offset = 319,
.count = 20
},
{ // STDCERT_SUBJ_KEY_ID
.offset = 286,
.count = 20
}
},
.cert_elements = NULL,
.cert_elements_count = 0,
.cert_template = g_cert_template_2_device,
.cert_template_size = sizeof(g_cert_template_2_device),
.ca_cert_def = &g_cert_def_1_signer,
};
cryptoauthlib >= 20190304
cryptography >= 2.5
pytz
@pfried
Copy link

pfried commented Jun 13, 2019

@bryan-hunt Is there any tooling to calculate the offsets and lengths for std_cert_elements? Any recommendation how to do it?

@bryan-hunt
Copy link
Author

bryan-hunt commented Jun 13, 2019

@pfried There is a relatively old App Note that AN_8974 that describes what has been done. Unfortunately there is no available tooling to help with this - all I have is a few hacky scripts to guess at some offsets and then I have to go back through and tweak everything. Based on what is provided though you can make a few tweaks here and there to build your own.

The biggest hint I can offer to help is that the templates are actually full certificates and you can convert them back into DER blob and read them with openssl. I like to use the ASN.1 Editor project though which has a nice format conversion tool in it.

Tooling for certificates are on the roadmap though so when we have something we'll publish it to cryptoauthtools

@pfried
Copy link

pfried commented Jun 27, 2019

@bryan-hunt

We tried to figure out how to create the templates, so we stumbled over the script in the aws provisioning project .

The only thing we needed to change was :

def cert_auth_key_id_offset_length(cert):
    cert_der = encoder.encode(cert)
    cert_mod = decoder.decode(cert_der, asn1Spec=rfc2459.Certificate())[0]
    for ext in cert_mod['tbsCertificate']['extensions']:
        print(ext)
        if ext['extnID'] == rfc2459.id_ce_authorityKeyIdentifier:
            #extn_value = decoder.decode(ext['extnValue'])[0]
            auth_key_id = decoder.decode(ext['extnValue'], asn1Spec=rfc2459.AuthorityKeyIdentifier())[0]
            key_id = bytearray(auth_key_id['keyIdentifier'])
            key_id[0] ^= 0xFF  # Change first byte

            auth_key_id['keyIdentifier'] = auth_key_id['keyIdentifier'].clone(value=key_id)
            ext['extnValue'] = univ.OctetString(encoder.encode(auth_key_id))

            return {'offset': diff_offset(cert_der, encoder.encode(cert_mod)), 'length': len(key_id)}
    return None

The code was unable to parse the object until we removed the here commented out line (and replaced the variable in the next call):

I am not sure if this is a bug or just not matching our certificate format, therefore I attached a device cert.

#extn_value = decoder.decode(ext['extnValue'])[0]

Note: What I did not know at the beginning: the Signer Common name ends with 'FFFF', these four bytes are used later for identification of the signer

Example Device Certificate how we use it

@dbeecham
Copy link

dbeecham commented Nov 9, 2020

This has some googlespace now, and I just wanted to add some of my findings on this for anyone who hits this after me.

You can use openssl asn1parse -inform der -in file to parse the ASN.1 information in the g_cert_template_2_device. Something like this

$ python3
> arr = [ 
    0x30, 0x82, 0x01, 0xa6, 0x30, 0x82, 0x01, 0x4b,  0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x10, 0x41,
    0xa6, 0x8b, 0xe4, 0x36, 0xdd, 0xc3, 0xd8, 0x39,  0xfa, 0xbd, 0xd7, 0x27, 0xd9, 0x74, 0xe7, 0x30,
    0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d,  0x04, 0x03, 0x02, 0x30, 0x34, 0x31, 0x14, 0x30,
    ... just paste the entire thing in here ...
    0x7a, 0x31, 0xb3, 0x00, 0x8b, 0xc0, 0x2e, 0x4f,  0x99, 0xc5
]
> f = open('g_cert_template_2_device', 'wb')
> f.write(bytes(arr))
> f.close()
> exit()
$ openssl x509 -inform der -noout -text -in g_cert_template_2_device
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            41:a6:8b:e4:36:dd:c3:d8:39:fa:bd:d7:27:d9:74:e7
        Signature Algorithm: ecdsa-with-SHA256
        Issuer: O = Example Inc, CN = Example Signer FFFF
        Validity
            Not Before: Jul 10 20:00:00 2017 GMT
            Not After : Dec 31 23:59:59 3000 GMT
        Subject: O = Example Inc, CN = Example Device
        Subject Public Key Info:
            Public Key Algorithm: id-ecPublicKey
                Public-Key: (256 bit)
                pub:
                    04:96:27:f1:3e:80:ac:f9:d4:12:ce:3b:0d:68:f7:
                    4e:b2:c6:07:35:00:b7:78:5b:ac:e6:50:30:54:77:
                    7f:c8:62:21:ce:f2:5a:9a:9e:86:40:c2:29:d6:4a:
                    32:1e:b9:4a:1b:1c:94:f5:39:88:ae:fe:49:cc:fd:
                    bf:8a:0d:34:b8
                ASN1 OID: prime256v1
                NIST CURVE: P-256
        X509v3 extensions:
            X509v3 Subject Key Identifier:
                2D:DA:6C:36:D5:A5:5A:CE:97:10:3D:BB:AF:9C:66:2A:CD:3E:E6:CF
            X509v3 Authority Key Identifier:
                keyid:C6:70:E0:5E:8A:45:0D:B8:2C:00:2A:40:06:39:4C:19:58:04:35:76

    Signature Algorithm: ecdsa-with-SHA256
         30:46:02:21:00:e1:fc:00:23:c1:3d:01:3f:22:31:0b:f0:b8:
         f4:f4:22:fc:95:96:33:9c:b9:62:b1:fc:8a:2d:a8:5c:ee:67:
         72:02:21:00:a1:0d:47:e4:fd:0d:15:d8:de:a1:b5:96:28:4e:
         7a:0b:be:cc:ec:e8:8e:cc:7a:31:b3:00:8b:c0:2e:4f:99:c5
$ openssl asn1parse -inform der -in g_cert_template_2_device
    0:d=0  hl=4 l= 422 cons: SEQUENCE
    4:d=1  hl=4 l= 331 cons: SEQUENCE
    8:d=2  hl=2 l=   3 cons: cont [ 0 ]
   10:d=3  hl=2 l=   1 prim: INTEGER           :02
   13:d=2  hl=2 l=  16 prim: INTEGER           :41A68BE436DDC3D839FABDD727D974E7
   31:d=2  hl=2 l=  10 cons: SEQUENCE
   33:d=3  hl=2 l=   8 prim: OBJECT            :ecdsa-with-SHA256
   43:d=2  hl=2 l=  52 cons: SEQUENCE
   45:d=3  hl=2 l=  20 cons: SET
   47:d=4  hl=2 l=  18 cons: SEQUENCE
   49:d=5  hl=2 l=   3 prim: OBJECT            :organizationName
   54:d=5  hl=2 l=  11 prim: UTF8STRING        :Example Inc
   67:d=3  hl=2 l=  28 cons: SET
   69:d=4  hl=2 l=  26 cons: SEQUENCE
   71:d=5  hl=2 l=   3 prim: OBJECT            :commonName
   76:d=5  hl=2 l=  19 prim: UTF8STRING        :Example Signer FFFF
   97:d=2  hl=2 l=  32 cons: SEQUENCE
   99:d=3  hl=2 l=  13 prim: UTCTIME           :170710200000Z
  114:d=3  hl=2 l=  15 prim: GENERALIZEDTIME   :30001231235959Z
  131:d=2  hl=2 l=  47 cons: SEQUENCE
  133:d=3  hl=2 l=  20 cons: SET
  135:d=4  hl=2 l=  18 cons: SEQUENCE
  137:d=5  hl=2 l=   3 prim: OBJECT            :organizationName
  142:d=5  hl=2 l=  11 prim: UTF8STRING        :Example Inc
  155:d=3  hl=2 l=  23 cons: SET
  157:d=4  hl=2 l=  21 cons: SEQUENCE
  159:d=5  hl=2 l=   3 prim: OBJECT            :commonName
  164:d=5  hl=2 l=  14 prim: UTF8STRING        :Example Device
  180:d=2  hl=2 l=  89 cons: SEQUENCE
  182:d=3  hl=2 l=  19 cons: SEQUENCE
  184:d=4  hl=2 l=   7 prim: OBJECT            :id-ecPublicKey
  193:d=4  hl=2 l=   8 prim: OBJECT            :prime256v1
  203:d=3  hl=2 l=  66 prim: BIT STRING
  271:d=2  hl=2 l=  66 cons: cont [ 3 ]
  273:d=3  hl=2 l=  64 cons: SEQUENCE
  275:d=4  hl=2 l=  29 cons: SEQUENCE
  277:d=5  hl=2 l=   3 prim: OBJECT            :X509v3 Subject Key Identifier
  282:d=5  hl=2 l=  22 prim: OCTET STRING      [HEX DUMP]:04142DDA6C36D5A55ACE97103DBBAF9C662ACD3EE6CF
  306:d=4  hl=2 l=  31 cons: SEQUENCE
  308:d=5  hl=2 l=   3 prim: OBJECT            :X509v3 Authority Key Identifier
  313:d=5  hl=2 l=  24 prim: OCTET STRING      [HEX DUMP]:30168014C670E05E8A450DB82C002A4006394C1958043576
  339:d=1  hl=2 l=  10 cons: SEQUENCE
  341:d=2  hl=2 l=   8 prim: OBJECT            :ecdsa-with-SHA256
  351:d=1  hl=2 l=  73 prim: BIT STRING

Every line here is

<bytes into file>:d=<depth> hl=<header-length> l=<length> ...

This matches up pretty well with the locations for std_cert_elements, for example, the STDCERT_CERT_SN has offset=15, length=16. That's this line

13:d=2  hl=2 l=  16 prim: INTEGER           :41A68BE436DDC3D839FABDD727D974E7

which starts at offset 13, and has a header length of 2, so actual bytes start at 15 (and it's length is 16 - which is correct).

The tbs_cert_loc is also correct for this; it's offset is 4 and it's length is 335 - that's 4 byte offset, past the first SEQUENCE, and the length of that tbsCertificate is 331 + 4 byte header, so it's 335. For context, the x509 certificate is defined as an ASN.1 sequence that looks like this

Certificate  ::=  SEQUENCE  {
    tbsCertificate       TBSCertificate,
    signatureAlgorithm   AlgorithmIdentifier,
    signatureValue       BIT STRING  }

So the signatureAlgorithm is at offset 339 (that's where depth drops to 1 again), and signatureValue is at offset 351.

As pfried said above, the signer id is part of the common name of the signing certificate.

But there are a few things that I can't puzzle together here.

  • The signature is at offset 351, which is the signature header, not the signature content. The length is correct including its header, so I guess that's by design.
  • The auth key ID is at offset 319, not 315, but it's length turns out to be correct. I guess it just skips the first 4 bytes which is always 3016.
  • Same with the public key, but it's off with 2 bytes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment