Skip to content
Private key recovery

Recovering a private key without MicroB2B

MicroB2B stores the private key of a certificate encrypted under the master key (SECRETS_KEY), never in the clear. This page is how to get a key back from a database row and the master key with OpenSSL alone, for instance when MicroB2B cannot run or when a key must move to another product.

It needs OpenSSL 3, base64, od and tr. The commands below are run by a test against a key MicroB2B wrote, so they are what this version does.

What a row holds

The table certificates has, for a certificate with a private key:

ColumnHolds
private_key_bytesthe key as an encrypted PKCS#8 PEM (-----BEGIN ENCRYPTED PRIVATE KEY-----): PBES2 (RFC 8018) with PBKDF2-HMAC-SHA256 and AES-256-CBC, the profile openssl pkcs8 -topk8 -v2 aes-256-cbc -v2prf hmacWithSHA256 writes
private_key_kidthe id of the master key it is encrypted under

Version 1

Every value of version 1 is derived from the 32 bytes of the master key:

ValueDefinition
key idthe first 4 bytes of HMAC-SHA256(master key, microb2b secret key id), as 8 lowercase hex characters
private key subkeyHKDF-SHA256 (RFC 5869) of the master key, salt empty, info microb2b private key v1, 32 bytes
passwordthe private key subkey as 64 lowercase hex characters

The password is the PBES2 password of the PEM. Its salt, iteration count and IV are in the PEM itself.

The commands

Set SECRETS_KEY to the master key as configured (base64; for a key given as hex, set MASTER_KEY to it directly and skip the first line), write the row’s private_key_bytes to key.pem and set KID to its private_key_kid:

MASTER_KEY=$(printf '%s' "$SECRETS_KEY" | base64 -d | od -An -tx1 | tr -d ' \n')

# The key id of this master key: it must be the row's private_key_kid.
printf '%s' 'microb2b secret key id' | openssl dgst -sha256 -mac HMAC -macopt hexkey:"$MASTER_KEY" -r | cut -c1-8

PASSWORD=$(openssl kdf -keylen 32 -kdfopt digest:SHA256 -kdfopt hexkey:"$MASTER_KEY" \
  -kdfopt info:'microb2b private key v1' HKDF | tr -d ':' | tr 'A-F' 'a-f')

openssl pkcs8 -in key.pem -passin pass:"$PASSWORD" -out recovered.pem

recovered.pem is the key as an unencrypted PKCS#8 PEM. A key id that is not KID means this is not the master key the row was encrypted under: during a rotation the row may name a previous key (SECRETS_PREVIOUS_KEYS).

Keep recovered.pem and the master key away from where the database is.

The other secrets

The write-only values of the configurations (passwords, header values, signing secrets, webhook URLs) are sealed as JSON documents with AES-256-GCM under another subkey and bound to their row. They are not covered here: a lost one is entered again.