VeilNet

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.

0 of 5 done
  1. 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.

--algKeySigns certificates with
ml-dsa-44, ml-dsa-65, ml-dsa-87 (the default)ML-DSA, FIPS 204Pure ML-DSA
ecdsa-p256, ecdsa-p384, ecdsa-p521ECDSA, FIPS 186-5ECDSA with SHA-256, SHA-384 or SHA-512, depending on the curve
rsa-2048, rsa-3072, rsa-4096RSA, FIPS 186-5PKCS #1 v1.5 with SHA-256, SHA-384 or SHA-512, depending on the key size, or RSASSA-PSS with --rsa-pss
ed25519EdDSA, FIPS 186-5Pure 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:

FlagDefaultWhat it does
--out FILEStandard output, for public objectsWhere to write the output. Use - for standard output.
--derPEMWrites DER instead of PEM.
--forceOffOverwrites 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:

FlagDefaultWhat 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.
  • --encrypt writes 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:

ProfileKey usageExtended key usageBasic constraints
server (sign's default)digitalSignature, and keyEncipherment for RSAserverAuthNot a CA
clientSame as serverclientAuthNot a CA
server+clientSame as serverserverAuth, clientAuthNot 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.

Usage
conflux cert genkey --out FILE [--alg ALG] [--encrypt] [--pubout FILE]
FlagDefaultWhat it does
--out FILERequiredWhere to write the key.
--alg ALGml-dsa-87The key's algorithm. See Algorithms.
--encryptOffEncrypts 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.

Usage
conflux cert req --key KEY --subject DN [--san NAME]... [--out FILE]
FlagDefaultWhat it does
--key KEYRequiredThe private key the request is for.
--subject DNRequiredThe subject. See Names and Profiles.
--san NAMEThe CNA subject alternative name. Repeat for more.
--rsa-pssOffWith 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.

Usage
conflux cert selfsign --key KEY --subject DN [--profile ca] [--days N] [--out FILE]
FlagDefaultWhat it does
--key KEYRequiredThe private key to certify and sign with.
--subject DNRequiredThe subject.
--san NAMEThe CN, for a serverA subject alternative name. Repeat for more.
--profilecaserver, client, server+client or ca.
--days N3650How many days it is valid for.
--path-len NNo boundWith --profile ca, how many CAs may be below it.
--crl-url URL—Where the issuer's revocation list is published. Repeat for more.
--rsa-pssOffWith an RSA key, signs with RSASSA-PSS.

cert sign

Issues a certificate for a signing request, after checking the request's signature.

Usage
conflux cert sign --csr REQ --ca CERT --ca-key KEY [--profile server] [--days N] [--bundle] [--out FILE]
FlagDefaultWhat it does
--csr REQRequiredThe request to issue a certificate for.
--ca CERTRequiredThe issuer's certificate, first in the file, with its chain after it.
--ca-key KEYRequiredThe issuer's private key.
--profileserverserver, client, server+client or ca.
--days N365How many days it is valid for.
--subject, --sanThe request'sReplaces the request's subject and names.
--bundleOffWrites the issuer's chain after the new certificate, as a server presents it.
--path-len, --crl-url, --rsa-pssSame as selfsignSame as for selfsign.

cert verify

Builds a chain from a certificate to a trusted root, and checks it.

Usage
conflux cert verify CERT [--ca ROOT]... [--untrusted CERT]... [--crl CRL]... [--name HOST]
FlagDefaultWhat it does
--ca ROOTThe system's rootsA 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.
--purposeanyserver, client or any.
--at TIMENowChecks the chain as of this time, in RFC 3339 format or as a date.
--require-pqOffFails unless the authentication is pqc: ML-DSA throughout.
--jsonOffPrints 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.

Usage
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.

Usage
conflux cert convert IN --out FILE [--to pem|der] [--encrypt | --decrypt] [--pubout FILE]
FlagDefaultWhat it does
--out FILERequiredWhere to write the output. Use - for standard output.
--topemThe encoding to write: pem or der.
--encryptOffEncrypts a private key under a new passphrase, from --new-passphrase-file, --new-passphrase-env or the terminal.
--decryptOffWrites 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.

Usage
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.

Usage
conflux cert fingerprint FILE... [--alg sha256] [--spki] [--json]
FlagDefaultWhat it does
--algsha256sha256, sha384, sha512, sha3-256, sha3-384 or sha3-512.
--spkiOffFingerprints the public key, which is the form used for a public key pin.
--jsonOffPrints 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.

Usage
conflux cert crl --ca CERT --ca-key KEY [--revoke SERIAL[:REASON]]... [--from CRL] [--out FILE]
FlagDefaultWhat it does
--ca CERTRequiredThe issuer's certificate.
--ca-key KEYRequiredThe 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 NOne more than --from's number, or else the time in secondsThe list's number.
--days N7How many days until the next list is due.
--rsa-pssOffWith 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.

Usage
conflux cert ca init|issue|revoke|list|crl DIR ...
Example
$ 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
CommandWhat 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 DIRPublishes new revocation lists with the next numbers. Run it on a schedule, before each list's next update is due.
FlagDefaultWhat it does
--name NAMERequiredThe CA's name.
--org ORG—The organisation, in both certificates.
--alg ALGml-dsa-87The root's key algorithm.
--intermediate-alg ALGml-dsa-65The issuing CA's key algorithm.
--days N3650How many days the root is valid for.
--intermediate-days N1825How many days the issuing CA is valid for.
--leaf-days N365How many days each certificate the CA issues is valid for.
--crl-days N7How many days each revocation list is valid for.
--crl-url URL—Where the issuing CA's revocation list is published. Repeat for more.
--no-encryptOffLeaves 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.

Usage
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]
Example
$ 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
FlagDefaultWhat 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 NAMEThe certificate's CNexport: the friendly name that importing software shows.
--key-out, --cert-out, --chain-outThe chain follows the certificate in --cert-outimport: where to write the key, its certificate and the rest of the chain.
--encryptOffimport: encrypts the key it writes with the bundle's passphrase.
--p12-passphrase-file, --p12-passphrase-envAsks at the terminalThe 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.

Usage
conflux cert probe HOST[:PORT] [--ca ROOT]... [--groups pq] [--require-pq] [--json]
FlagDefaultWhat it does
HOST[:PORT]Port 443The server to probe.
--ca ROOTThe system's rootsA trusted root for the server's chain. Repeat for more.
--name NAMEThe hostThe server name to send, and to check the chain against.
--groupsEvery group FIPS 140-3 mode allowsThe key exchange groups to offer: pq, classical, or names such as SecP384r1MLKEM1024.
--tls1.2 and 1.3Allows only this TLS version: 1.2 or 1.3.
--alpn—Application protocols to offer, separated by commas.
--cert, --key—A client certificate to present.
--timeout10sHow long the connection and handshake may take.
--require-pq-kexOffFails unless the key exchange uses ML-KEM, alone or in a hybrid.
--require-pq-authOffFails unless the chain verifies and is ML-DSA throughout.
--require-pqOffRequires both.
--jsonOffPrints the result as JSON, with the leaf certificate under leaf.
Example
$ 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 yes

The 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.

Usage
conflux cert serve --cert CHAIN --key KEY [--listen ADDR] [--client-ca ROOT]... [--once]
FlagDefaultWhat it does
--cert CHAINRequiredThe server's certificate, then its chain.
--key KEYRequiredThe private key for --cert.
--listen ADDR127.0.0.1:8443The address to listen on.
--client-ca ROOT—Requires every client to present a certificate that verifies against this root.
--groupsEvery group FIPS 140-3 mode allowsThe key exchange groups to allow.
--alpnhttp/1.1The application protocols to advertise.
--onceOffStops after the first client, and exits with code 1 if that client's handshake failed.
--jsonOffReports 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

CodeMeaning
0The command succeeded, or the check passed.
1The command failed, for example because a file could not be read, a key was refused or the passphrase was wrong.
2The command was used incorrectly.
3A 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.
70This conflux isn't running in FIPS 140-3 mode, so it doesn't create or check anything.

For the exact messages, see Error Messages.

Next Steps