x509gen
Module: x509gen
Source: cmodules/nxpico/mod_x509gen.c
Depends on: mbedTLS (bundled with MicroPython)
import x509genOverview
Section titled “Overview”x509gen exposes mbedTLS cryptographic primitives directly to MicroPython, allowing the nXPico M to generate key pairs and X.509 certificates entirely on-device — no PC, no OpenSSL, no external CA required at provisioning time.
The typical use case is device identity for mutual TLS (mTLS): each device generates its own unique key pair and a certificate that identifies it to a broker or server. The private key never leaves the device — it is generated on-chip and stored directly in the nRF9151 secure storage via the modem module.
Two provisioning workflows
Section titled “Two provisioning workflows”Self-signed — the device signs its own certificate. No external CA is involved. Suitable when the server trusts individual device certificates directly (e.g. AWS IoT just-in-time registration, or a private broker with a known device list).
generate_rsa_keypair() / generate_ec_keypair() ↓generate_self_signed_cert() ↓modem.write_certificate() ← cert + key stored in nRF9151CSR — the device generates a key pair and a Certificate Signing Request, which is sent to a CA for signing. The signed certificate is then written back to the device. Required when a PKI hierarchy must be maintained.
generate_rsa_keypair() / generate_ec_keypair() ↓generate_csr() ← send to CA ↓receive signed cert from CA ↓modem.write_certificate()RSA vs EC
Section titled “RSA vs EC”| RSA-2048 | EC secp256r1 | |
|---|---|---|
| Generation time on RP2350 | 10–30 s | < 1 s |
| Key size | larger | smaller |
| Compatibility | universal | broad (TLS 1.2+) |
Use EC when provisioning speed matters or when key size in the modem storage is a concern. Use RSA when the target server or CA requires it.
Key pair generation
Section titled “Key pair generation”generate_rsa_keypair
Section titled “generate_rsa_keypair”priv, pub = x509gen.generate_rsa_keypair(bits=2048)Generate an RSA key pair.
| Parameter | Type | Default | Description |
|---|---|---|---|
bits | int | 2048 | Key size in bits. Accepted range: 1024 – 4096. |
Returns (private_key_pem, public_key_pem) as PEM strings.
Raises ValueError if bits is outside the accepted range.
generate_ec_keypair
Section titled “generate_ec_keypair”priv, pub = x509gen.generate_ec_keypair(curve_name="secp256r1")Generate an Elliptic Curve key pair.
| Parameter | Type | Default | Description |
|---|---|---|---|
curve_name | str | "secp256r1" | Curve identifier. |
Supported curves: "secp256r1", "secp384r1", "secp521r1".
Returns (private_key_pem, public_key_pem) as PEM strings.
Raises ValueError for unsupported curve names.
Certificate generation
Section titled “Certificate generation”generate_self_signed_cert
Section titled “generate_self_signed_cert”cert = x509gen.generate_self_signed_cert( private_key_pem, subject="CN=RP2350-Device,O=Embedded", serial=1, not_before="20250101000000", not_after="20350101000000", is_ca=False, key_usage=0, md_alg="sha256",)Generate and sign a self-signed X.509 certificate.
| Parameter | Type | Default | Description |
|---|---|---|---|
private_key_pem | str | — | PEM-encoded private key (RSA or EC). |
subject | str | "CN=RP2350-Device,O=Embedded" | Distinguished Name in CN=…,O=…,C=… format. |
serial | int | 1 | Certificate serial number. |
not_before | str | "20250101000000" | Validity start — format YYYYMMDDHHMMSS. |
not_after | str | "20350101000000" | Validity end — format YYYYMMDDHHMMSS. |
is_ca | bool | False | Set to True to mark the certificate as a CA. |
key_usage | int | 0 | Bitmask of KU_* constants (see below). 0 = no constraints. |
md_alg | str | "sha256" | Signature hash: "sha256", "sha384", or "sha512". |
Returns the certificate as a PEM string.
generate_csr
Section titled “generate_csr”csr = x509gen.generate_csr( private_key_pem, subject="CN=RP2350-Device,O=Embedded", md_alg="sha256",)Generate a Certificate Signing Request (CSR) to be signed by an external CA.
| Parameter | Type | Default | Description |
|---|---|---|---|
private_key_pem | str | — | PEM-encoded private key. |
subject | str | "CN=RP2350-Device,O=Embedded" | Distinguished Name. |
md_alg | str | "sha256" | Hash algorithm. |
Returns the CSR as a PEM string (-----BEGIN CERTIFICATE REQUEST-----).
Key usage constants
Section titled “Key usage constants”Use these constants as a bitmask for the key_usage parameter of generate_self_signed_cert.
| Constant | Meaning |
|---|---|
x509gen.KU_DIGITAL_SIGNATURE | Digital signature |
x509gen.KU_KEY_ENCIPHERMENT | Key encipherment |
x509gen.KU_DATA_ENCIPHERMENT | Data encipherment |
x509gen.KU_KEY_AGREEMENT | Key agreement |
x509gen.KU_KEY_CERT_SIGN | Certificate signing (CA only) |
x509gen.KU_CRL_SIGN | CRL signing (CA only) |
ku = x509gen.KU_DIGITAL_SIGNATURE | x509gen.KU_KEY_ENCIPHERMENTcert = x509gen.generate_self_signed_cert(priv, key_usage=ku)Full example — generate and store a device certificate
Section titled “Full example — generate and store a device certificate”Generate a self-signed certificate using the RP2350 unique ID as the device identifier, then write it and its private key to the modem’s secure storage.
import machineimport x509genfrom modem import Modem
def create_and_provision_cert(sec_tag: int = 1): uid = machine.unique_id() serial = ''.join('{:02X}'.format(b) for b in uid) subject = f"CN=rp2350-{serial},O=neXo,C=IT"
print("[x509gen] Generating RSA-2048 key pair (may take ~30 s)...") priv, _pub = x509gen.generate_rsa_keypair(2048)
print("[x509gen] Signing certificate...") cert = x509gen.generate_self_signed_cert( priv, subject=subject, not_before="20250101000000", not_after="20350101000000", is_ca=False, md_alg="sha256", )
m = Modem() m.CFUN(4) # flight mode required before writing credentials m.write_certificate(sec_tag, 1, cert) # cert_type=1: client certificate m.write_certificate(sec_tag, 2, priv) # cert_type=2: client private key m.CFUN(1)
print(f"[x509gen] Certificate and key stored at sec_tag={sec_tag}") return cert, priv
cert_pem, priv_pem = create_and_provision_cert(sec_tag=1)