Certificate Toolkit
Look up every conflux cert command: keys, requests, certificates, a managed CA, revocation lists, PKCS#12, and TLS probe and serve, all in FIPS 140-3 mode.
- Needs
- No root, no configuration and no anchor
- Default key
- ML-DSA-87
- Mode
- FIPS 140-3
How the Toolkit Works
conflux cert creates and checks X.509 private keys, certificate signing requests, certificates and revocation lists. It can also run a certificate authority from a directory, create and read PKCS#12 bundles, and probe and serve TLS. It covers the parts of OpenSSL's genpkey, req, x509, ca, verify, crl, pkcs12, s_client and s_server that a CA and its users need.
- Every key, signature, digest, key derivation and cipher comes from the Go Cryptographic Module, in FIPS 140-3 mode. See FIPS 140-3 Mode.
- Keys are ML-DSA-87 unless you choose another algorithm, so a whole chain can use ML-DSA, from the root to the leaf.
- It only touches the files you name, and it only uses the network for probe and serve.
- OpenSSL 3.5 can read the files it writes, and it can read the files OpenSSL 3.5 writes.
Use it for TLS and for anything else that needs an X.509 certificate. To create an anchor's identity or credentials, use anchorctl's keygen and issue commands instead. To list a command's flags, run conflux cert COMMAND -h.
A Worked Example
This example creates an ML-DSA root, issues a server certificate from it, and checks that a TLS handshake with the server is post-quantum from end to end.
Create an ML-DSA-87 key, encrypted with a passphrase that conflux asks you for. Then use the key to sign the root certificate:
$ conflux cert genkey --encrypt --out root.key $ conflux cert selfsign --key root.key --subject "/O=Example/CN=Example Root" --out root.pem
- The probe's last row reads
key exchange yes; authentication yes, and the command exits with code 0.
To revoke the certificate later, pass its serial number, as show prints it, to crl. After that, verify exits with code 3:
$ conflux cert crl --ca root.pem --ca-key root.key --revoke 41:24:71:…:AB:keyCompromise --out root.crl $ conflux cert verify server.pem --ca root.pem --crl root.crl
Algorithms
Choose a key's algorithm with --alg. A certificate is signed with its issuer's algorithm, so an ML-DSA root signs even an ECDSA leaf with ML-DSA.
| --alg | Key | Signs certificates with |
|---|---|---|
ml-dsa-44, ml-dsa-65, ml-dsa-87 (the default) | ML-DSA, FIPS 204 | Pure ML-DSA |
ecdsa-p256, ecdsa-p384, ecdsa-p521 | ECDSA, FIPS 186-5 | ECDSA with SHA-256, SHA-384 or SHA-512, depending on the curve |
rsa-2048, rsa-3072, rsa-4096 | RSA, FIPS 186-5 | PKCS #1 v1.5 with SHA-256, SHA-384 or SHA-512, depending on the key size, or RSASSA-PSS with --rsa-pss |
ed25519 | EdDSA, FIPS 186-5 | Pure Ed25519 |
Keys are stored as PKCS#8. An ML-DSA key is written as its 32-byte seed, which is the seed form defined in RFC 9881. Conflux also reads the form that OpenSSL 3.5 writes by default, which stores the seed next to the expanded key, and checks that the two match.
Common Flags
You can give flags and arguments in any order. Status messages go to standard error, so standard output carries only the output you asked for. Every command that writes a file takes these flags:
| Flag | Default | What it does |
|---|---|---|
--out FILE | Standard output, for public objects | Where to write the output. Use - for standard output. |
--der | PEM | Writes DER instead of PEM. |
--force | Off | Overwrites an existing key at --out. Without it, conflux never overwrites a key. |
A command that opens or encrypts a private key reads its passphrase from one of these:
| Flag | Default | What it does |
|---|---|---|
--passphrase-file FILE | — | Reads the passphrase from the first line of FILE. |
--passphrase-env NAME | — | Reads the passphrase from that environment variable. |
| Neither | — | Prompts at the terminal without echoing. The prompt reads from the terminal directly, so you can pipe a key in and still type its passphrase. |
- An unencrypted private key is written atomically, with mode
0600(owner-only on Windows). Conflux never prints a key to a terminal. --encryptwrites PKCS#8 under PBES2: PBKDF2 with HMAC-SHA-256, 600,000 iterations, a 16-byte salt and AES-256-CBC. A new passphrase must be at least 8 characters long, and conflux asks for it twice.- To import a key that another tool encrypted, re-encrypt it through a pipe so that it is never written unencrypted:
openssl pkey -in KEY | conflux cert convert - --encrypt --out KEY.new.
Names and Profiles
--subject takes a distinguished name in either OpenSSL's order, /C=AU/O=Example/CN=host.example, or RFC 4514's order, CN=host.example,O=Example,C=AU. The attributes are CN, O, OU, C, ST, L, STREET, POSTALCODE, SERIALNUMBER, DC, UID and emailAddress. Inside a value, put a backslash before any comma, slash, plus sign, equals sign or backslash.
--san adds a subject alternative name, and you can repeat it. Prefix each name with DNS:, IP:, EMAIL: or URI:, or leave the prefix off and conflux works out the type from the value. Without --san, a server certificate or request uses its CN as its only name, if the CN is a host name or an address.
--profile sets what a certificate can be used for. A request carries only the subject and names, and the profile is applied when the certificate is signed:
| Profile | Key usage | Extended key usage | Basic constraints |
|---|---|---|---|
server (sign's default) | digitalSignature, and keyEncipherment for RSA | serverAuth | Not a CA |
client | Same as server | clientAuth | Not a CA |
server+client | Same as server | serverAuth, clientAuth | Not a CA |
ca (selfsign's default) | keyCertSign, cRLSign, digitalSignature | — | A CA; --path-len N limits how many CAs can be below it |
Every certificate carries a random 128-bit serial number, a subject key identifier and its issuer's key identifier. Its validity starts five minutes in the past, to allow for clocks that are slightly behind. A certificate can't outlive its issuer.
Commands
conflux cert has these commands: genkey, req, selfsign, sign, verify, show, convert, match, fingerprint, crl, ca, p12, probe and serve.
cert genkey
Creates a private key and, if you ask, its public key.
conflux cert genkey --out FILE [--alg ALG] [--encrypt] [--pubout FILE]
| Flag | Default | What it does |
|---|---|---|
--out FILE | Required | Where to write the key. |
--alg ALG | ml-dsa-87 | The key's algorithm. See Algorithms. |
--encrypt | Off | Encrypts the key under a passphrase. |
--pubout FILE | — | Also writes the public key there. |
cert req
Creates a certificate signing request, signed by the key it is for.
conflux cert req --key KEY --subject DN [--san NAME]... [--out FILE]
| Flag | Default | What it does |
|---|---|---|
--key KEY | Required | The private key the request is for. |
--subject DN | Required | The subject. See Names and Profiles. |
--san NAME | The CN | A subject alternative name. Repeat for more. |
--rsa-pss | Off | With an RSA key, signs with RSASSA-PSS. |
cert selfsign
Creates a certificate signed by its own key, such as a root CA or a standalone server certificate.
conflux cert selfsign --key KEY --subject DN [--profile ca] [--days N] [--out FILE]
| Flag | Default | What it does |
|---|---|---|
--key KEY | Required | The private key to certify and sign with. |
--subject DN | Required | The subject. |
--san NAME | The CN, for a server | A subject alternative name. Repeat for more. |
--profile | ca | server, client, server+client or ca. |
--days N | 3650 | How many days it is valid for. |
--path-len N | No bound | With --profile ca, how many CAs may be below it. |
--crl-url URL | — | Where the issuer's revocation list is published. Repeat for more. |
--rsa-pss | Off | With an RSA key, signs with RSASSA-PSS. |
cert sign
Issues a certificate for a signing request, after checking the request's signature.
conflux cert sign --csr REQ --ca CERT --ca-key KEY [--profile server] [--days N] [--bundle] [--out FILE]
| Flag | Default | What it does |
|---|---|---|
--csr REQ | Required | The request to issue a certificate for. |
--ca CERT | Required | The issuer's certificate, first in the file, with its chain after it. |
--ca-key KEY | Required | The issuer's private key. |
--profile | server | server, client, server+client or ca. |
--days N | 365 | How many days it is valid for. |
--subject, --san | The request's | Replaces the request's subject and names. |
--bundle | Off | Writes the issuer's chain after the new certificate, as a server presents it. |
--path-len, --crl-url, --rsa-pss | Same as selfsign | Same as for selfsign. |
cert verify
Builds a chain from a certificate to a trusted root, and checks it.
conflux cert verify CERT [--ca ROOT]... [--untrusted CERT]... [--crl CRL]... [--name HOST]
| Flag | Default | What it does |
|---|---|---|
--ca ROOT | The system's roots | A trusted root. Repeat for more. To check an ML-DSA chain, give its root here. |
--untrusted CERT | — | An intermediate certificate to build the chain through. It is not trusted on its own. Repeat for more. |
--crl CRL | — | A revocation list to check the chain against. Repeat for more. |
--name HOST | — | A host name or address the certificate must be valid for. |
--purpose | any | server, client or any. |
--at TIME | Now | Checks the chain as of this time, in RFC 3339 format or as a date. |
--require-pq | Off | Fails unless the authentication is pqc: ML-DSA throughout. |
--json | Off | Prints the result as JSON. |
If the chain is valid, it prints Valid and the chain, and exits with code 0. If not, it prints Not valid: and the reason, and exits with code 3. Any intermediates after the first certificate in CERT's file are treated as --untrusted.
The authentication is pqc when the leaf's key and every signature below the root are ML-DSA, and classical otherwise.
cert show
Describes each certificate, request, revocation list or key, as openssl x509 -text does.
conflux cert show FILE... [--json]
For a private key, it shows the public key. For an encrypted key, it shows how the key is encrypted. With --json, it prints an array with one element for each object.
cert convert
Re-encodes a file, encrypts or decrypts a private key, or extracts a public key.
conflux cert convert IN --out FILE [--to pem|der] [--encrypt | --decrypt] [--pubout FILE]
| Flag | Default | What it does |
|---|---|---|
--out FILE | Required | Where to write the output. Use - for standard output. |
--to | pem | The encoding to write: pem or der. |
--encrypt | Off | Encrypts a private key under a new passphrase, from --new-passphrase-file, --new-passphrase-env or the terminal. |
--decrypt | Off | Writes an encrypted private key out unencrypted. |
--pubout FILE | — | Writes the public key of the key, certificate or request instead. |
IN can be - for standard input. Keys are written as PKCS#8, in the same form that conflux writes elsewhere.
cert match
Checks that two files hold the same key, such as a key and its certificate.
conflux cert match A B
Each file can be a private key, a certificate, a request or a public key. If the keys differ, it prints No match: and exits with code 3.
cert fingerprint
Prints a digest of a certificate, request, revocation list or key.
conflux cert fingerprint FILE... [--alg sha256] [--spki] [--json]
| Flag | Default | What it does |
|---|---|---|
--alg | sha256 | sha256, sha384, sha512, sha3-256, sha3-384 or sha3-512. |
--spki | Off | Fingerprints the public key, which is the form used for a public key pin. |
--json | Off | Prints the result as JSON. |
A key's fingerprint is always the fingerprint of its public key.
cert crl
Creates the revocation list that an issuer publishes.
conflux cert crl --ca CERT --ca-key KEY [--revoke SERIAL[:REASON]]... [--from CRL] [--out FILE]
| Flag | Default | What it does |
|---|---|---|
--ca CERT | Required | The issuer's certificate. |
--ca-key KEY | Required | The issuer's private key. |
--revoke SERIAL[:REASON] | — | The serial number of a certificate to revoke, in hex with or without colons, as show prints it. Repeat for more. |
--from CRL | — | Copies the entries from the issuer's previous list into the new one, and numbers the new list after it. |
--number N | One more than --from's number, or else the time in seconds | The list's number. |
--days N | 7 | How many days until the next list is due. |
--rsa-pss | Off | With an RSA issuer, signs with RSASSA-PSS. |
The reasons are unspecified, keyCompromise, cACompromise, affiliationChanged, superseded, cessationOfOperation, privilegeWithdrawn and aACompromise. A revocation is permanent.
cert ca
Runs a certificate authority from a directory, which holds a root, an optional issuing CA, every certificate they issued, what they revoked and their revocation lists.
conflux cert ca init|issue|revoke|list|crl DIR ...
$ conflux cert ca init ./ca --name Example --intermediate --crl-url http://ca.example/crl.pem $ conflux cert ca issue ./ca --csr server.csr --bundle --out server.pem $ conflux cert ca list ./ca $ conflux cert ca revoke ./ca 41:24:71:…:AB --reason keyCompromise $ conflux cert ca crl ./ca
| Command | What it does |
|---|---|
init DIR --name NAME [--intermediate] | Creates the CA in a new or empty directory. The root is called NAME Root CA. With --intermediate, it also creates NAME Issuing CA below the root, so you can keep the root's key offline. |
issue DIR --csr REQ [--profile server] [--days N] [--bundle] | Issues a server, client or server+client certificate for a request, and records it before writing it out. --bundle also writes the issuing CA after the certificate. |
revoke DIR SERIAL [--reason REASON] | Revokes a certificate this CA issued, and publishes its revocation list. The reason defaults to unspecified. |
list DIR [--json] | Lists every certificate issued, soonest to expire first, with whether it is valid, expired or revoked, and why. |
crl DIR | Publishes new revocation lists with the next numbers. Run it on a schedule, before each list's next update is due. |
| Flag | Default | What it does |
|---|---|---|
--name NAME | Required | The CA's name. |
--org ORG | — | The organisation, in both certificates. |
--alg ALG | ml-dsa-87 | The root's key algorithm. |
--intermediate-alg ALG | ml-dsa-65 | The issuing CA's key algorithm. |
--days N | 3650 | How many days the root is valid for. |
--intermediate-days N | 1825 | How many days the issuing CA is valid for. |
--leaf-days N | 365 | How many days each certificate the CA issues is valid for. |
--crl-days N | 7 | How many days each revocation list is valid for. |
--crl-url URL | — | Where the issuing CA's revocation list is published. Repeat for more. |
--no-encrypt | Off | Leaves the CA's keys unencrypted, readable only by this account. |
The table above lists the flags of init. The directory is created with mode 0700. It holds ca.json, the root and issuing CA with their keys, chain.pem, one file for each issued certificate under issued/, index.json and the revocation lists. A lock makes sure only one command works on the CA at a time.
cert p12
Writes and reads PKCS#12 bundles, using the one profile that FIPS 140-3 approves throughout.
conflux cert p12 export (--key KEY --cert CERT [--chain CHAIN] | --in PEM) --out FILE [--name NAME] conflux cert p12 import BUNDLE --key-out KEY --cert-out CERT [--chain-out CHAIN] [--encrypt]
$ conflux cert p12 export --key server.key --cert server.pem --chain chain.pem --out server.p12 $ conflux cert p12 import server.p12 --key-out server.key --cert-out server.pem --chain-out chain.pem
| Flag | Default | What it does |
|---|---|---|
--key, --cert, --chain | — | export: the key, its certificate and the rest of the chain, issuer first. |
--in PEM | — | export: a PEM file that holds the key and certificates together. Use - for standard input. |
--name NAME | The certificate's CN | export: the friendly name that importing software shows. |
--key-out, --cert-out, --chain-out | The chain follows the certificate in --cert-out | import: where to write the key, its certificate and the rest of the chain. |
--encrypt | Off | import: encrypts the key it writes with the bundle's passphrase. |
--p12-passphrase-file, --p12-passphrase-env | Asks at the terminal | The bundle's passphrase. |
The key is stored in a shrouded key bag under PBES2: PBKDF2-HMAC-SHA-256, 600,000 iterations, a 16-byte salt and AES-256-CBC. The certificates are encrypted the same way. A PBMAC1 MAC (RFC 9579) with HMAC-SHA-256 protects the bundle, and conflux checks it before opening any bag.
To import a bundle from another tool, rewrite it through a pipe so that the key is never written unencrypted:
$ openssl pkcs12 -in OLD.p12 -nodes | conflux cert p12 export --in - --out NEW.p12
cert probe
Connects as a TLS client in FIPS 140-3 mode, completes the handshake without sending anything else, and reports what was negotiated.
conflux cert probe HOST[:PORT] [--ca ROOT]... [--groups pq] [--require-pq] [--json]
| Flag | Default | What it does |
|---|---|---|
| HOST[:PORT] | Port 443 | The server to probe. |
--ca ROOT | The system's roots | A trusted root for the server's chain. Repeat for more. |
--name NAME | The host | The server name to send, and to check the chain against. |
--groups | Every group FIPS 140-3 mode allows | The key exchange groups to offer: pq, classical, or names such as SecP384r1MLKEM1024. |
--tls | 1.2 and 1.3 | Allows only this TLS version: 1.2 or 1.3. |
--alpn | — | Application protocols to offer, separated by commas. |
--cert, --key | — | A client certificate to present. |
--timeout | 10s | How long the connection and handshake may take. |
--require-pq-kex | Off | Fails unless the key exchange uses ML-KEM, alone or in a hybrid. |
--require-pq-auth | Off | Fails unless the chain verifies and is ML-DSA throughout. |
--require-pq | Off | Requires both. |
--json | Off | Prints the result as JSON, with the leaf certificate under leaf. |
$ conflux cert probe server.example:8443 --ca root.pem --require-pq
Connected to server.example:8443 (192.0.2.10:8443), as server.example
tls TLS 1.3, TLS_AES_128_GCM_SHA256
key exchange SecP384r1MLKEM1024 (hybrid)
signature ML-DSA-65 (pqc)
certificate CN=server.example, issued by CN=Example Root,O=Example
valid 2026-10-01 to 2027-01-01
chain verified for server.example, authentication pqc
leaf CN=server.example: ML-DSA-65 key, signed ML-DSA-87
root CN=Example Root,O=Example: ML-DSA-87 key, signed ML-DSA-87
post-quantum key exchange yes; authentication yesThe key exchange is shown as hybrid for ML-KEM combined with a classical group, such as X25519MLKEM768, as pqc for ML-KEM-1024 on its own, and as classical for P-256, P-384 and P-521. The certificate row shows the name and dates of the leaf the server sent, whether or not its chain verifies.
It exits with code 0 on success, 3 if the chain does not verify or a requirement is not met, and 1 if no handshake took place.
cert serve
Runs a TLS server in FIPS 140-3 mode, and reports what each client offered and what was negotiated.
conflux cert serve --cert CHAIN --key KEY [--listen ADDR] [--client-ca ROOT]... [--once]
| Flag | Default | What it does |
|---|---|---|
--cert CHAIN | Required | The server's certificate, then its chain. |
--key KEY | Required | The private key for --cert. |
--listen ADDR | 127.0.0.1:8443 | The address to listen on. |
--client-ca ROOT | — | Requires every client to present a certificate that verifies against this root. |
--groups | Every group FIPS 140-3 mode allows | The key exchange groups to allow. |
--alpn | http/1.1 | The application protocols to advertise. |
--once | Off | Stops after the first client, and exits with code 1 if that client's handshake failed. |
--json | Off | Reports each client as a line of JSON. |
A client that sends an HTTP request receives the same report as text/plain, so curl or a browser can show it. To stop the server, press Ctrl-C.
Exit Codes
| Code | Meaning |
|---|---|
| 0 | The command succeeded, or the check passed. |
| 1 | The command failed, for example because a file could not be read, a key was refused or the passphrase was wrong. |
| 2 | The command was used incorrectly. |
| 3 | A check ran and failed: verify found the certificate not valid, match found two different keys, or probe found a chain that does not verify or a requirement that was not met. |
| 70 | This conflux isn't running in FIPS 140-3 mode, so it doesn't create or check anything. |
For the exact messages, see Error Messages.