ML-DSA Certificates on Cisco Routers – IPsec Series, Part 10 Cisco's IOS XE routers now support ML-DSA certificates (FIPS 204) for IPsec authentication, replacing pre-shared keys, as detailed in a technical blog post. The post shows that ML-DSA-65 public keys are 1,974 bytes versus 294 for RSA-2048 and 91 for ECDSA P-256, with certificates at 5,647 bytes versus 920, requiring IKEv2 to handle larger payloads. The configuration involves importing PKCS#12 bundles generated with OpenSSL 3.5+ and setting 'revocation-check none' to avoid CRL stalls. Part 9 made the key exchange quantum-safe. ML-KEM-768 is running on all three routers, fragmentation is handling the bigger messages, and the phased rollout lets you migrate at your own pace. But authentication is still a pre-shared key, and at scale that doesn’t work. This post closes the gap. We swap PSK for ML-DSA certificates FIPS 204 , the same signature scheme from Parts 6-8 in the container labs. The difference: on IOS XE, the path from “I have a key” to “I have a certificate” requires some work. And once the tunnel is up, we measure exactly what ML-DSA costs on the wire. The PKI lives on your workstation The entire ML-DSA identity for each router is built on your workstation with OpenSSL 3.5+ and imported as a PKCS 12 bundle a single password-encrypted file carrying the private key, its certificate, and the issuing CA chain together . This is the verified path for getting ML-DSA identities onto the routers. We generate a root CA per parameter set, an identity certificate for R1, R2, and R3, and a PKCS 12 bundle for each. === Artifact sizes in bytes R1 identity, DER-encoded cert ALGORITHM PUBKEY PRIVKEY CERT P12 rsa-2048 294 1218 920 3698 ecdsa-p256 91 138 528 1827 mldsa44 1334 2626 4118 11504 mldsa65 1974 4098 5647 16032 mldsa87 2614 4962 7605 20816 Look at that table: a ML-DSA-65 public key is 1,974 bytes against 294 for RSA-2048 and against 91 for ECDSA P-256. The certificate carrying it is 5,647 bytes against 920 . That’s not a small change, it’s an order of magnitude, and IKEv2 has to carry it. Import the bundle Copy the PKCS 12 file to the router and import: python R1 config crypto pki import TP-MLDSA65 pkcs12 bootflash:/mldsa65-r1.p12 password cisco123 % Importing pkcs12... Source filename mldsa65-r1.p12 ? Reading file from bootflash:/mldsa65-r1.p12 CRYPTO PKI: Imported PKCS12 file successfully. That password cisco123 protects the bundle for the few seconds it sits on bootflash: and nothing else. The bundle gets deleted right after import. Don’t reuse this pattern for a bundle that lives anywhere longer than one import. Delete it immediately: R1 delete /force bootflash:/mldsa65-r1.p12 One more thing: the default revocation-check crl on the trustpoint means the router tries to download a CRL Cert Revocation List before trusting a peer’s certificate. Lab certificates have no CRL distribution point, so the handshake stalls. Fix it: R1 config crypto pki trustpoint TP-MLDSA65 R1 ca-trustpoint revocation-check none Confirm the router parsed an ML-DSA certificate: R1 show crypto pki certificates verbose TP-MLDSA65 Certificate Status: Available Version: 3 Certificate Usage: Signature Issuer: cn=PQC-LAB-ROOT-mldsa65 Subject: Name: R1-mldsa65 IP Address: 10.0.12.1 cn=R1-mldsa65 Subject Key Info: Public Key Algorithm: ML-DSA Public Key Size: 1974 bytes Signature Algorithm: ML-DSA-65 X509v3 extensions: X509v3 Subject Alternative Name: IP Address : 10.0.12.1 Extended Key Usage: Server Auth Client Auth Public Key Algorithm: ML-DSA , 1,974 bytes, matching the container lab OpenSSL output exactly. Both Server Auth and Client Auth EKUs are needed. An IKEv2 peer is initiator on one negotiation and responder on the next, so a client-only certificate fails half the time. Swap PSK for ML-DSA Do this on both R1 and R3 . The key exchange config doesn’t change at all: pqc mlkem768 optional from Part 9 stays exactly as it is. Only the authentication method changes. crypto ikev2 profile MLDSA-PROFILE match identity remote address 10.0.23.2 255.255.255.255 identity local address 10.0.12.1 authentication local mldsa-sig authentication remote mldsa-sig pki trustpoint TP-MLDSA65 dpd 30 5 periodic crypto ipsec profile MLDSA-IPSEC set transform-set CLASSICAL-TS set ikev2-profile MLDSA-PROFILE interface Tunnel0 tunnel protection ipsec profile MLDSA-IPSEC The router warns you: Warning: MLDSA Auth packets are extremely large. Please enable IKEv2 Fragmentation. Fragmentation was already enabled in Part 9, so we’re covered. Changing tunnel protection shuts the interface, so bring it back: R1 config interface Tunnel0 R1 config-if no shutdown The payoff R1 ping 192.168.100.2 Success rate is 100 percent 5/5 , round-trip min/avg/max = 1/1/4 ms R1 show crypto ikev2 sa detailed Tunnel-id Local Remote fvrf/ivrf Status 2 10.0.12.1/500 10.0.23.2/500 none/none READY Encr: AES-CBC, keysize: 256, PRF: SHA512, Hash: SHA512, DH Grp:20, Auth sign: MLDSA, Auth verify: MLDSA PQC Key Exchange: ML-KEM-768 ... IETF Std Fragmentation enabled. Quantum-safe Encryption using PQC: ML-KEM-768 IETF Std Fragmentation MTU in use: 1372 bytes. There it is, on one line: Auth sign: MLDSA, Auth verify: MLDSA next to PQC Key Exchange: ML-KEM-768 . Both halves of the tunnel are quantum-safe. Key exchange resists harvest-now-decrypt-later. Authentication resists a future forged identity. What ML-DSA actually costs on the wire The warning said the auth packets are “exetremely large”, but how large? We set up a packet capture on R2 the transit hop that sees every IKE packet without being a crypto endpoint and measured one complete handshake for each signature algorithm. | Authentication | IKE AUTH frames | IKE AUTH bytes | Whole handshake | vs RSA-2048 | |---|---|---|---|---| | RSA-2048 | 3 | 3,462 | 7 frames / 7,111 B | 1.0x | | ML-DSA-44 | 12 | 15,384 | 16 frames / 19,033 B | 4.4x | | ML-DSA-65 | 16 | 20,752 | 20 frames / 24,401 B | 6.0x | | ML-DSA-87 | 20 | 27,848 | 24 frames / 31,497 B | 8.0x | IKE SA INIT is 2 frames and about 1.1 KB in every case: it only carries the classical ECDH exchange group 20 , whose public value is 512 bytes. The ML-KEM IKE INTERMEDIATE exchange is also constant at 2 frames and 2,548 bytes the ML-KEM-768 encapsulation key alone is 1,184 bytes . Neither changes with the signature algorithm. All the variable growth is in IKE AUTH , which is where the certificate and the signature travel. The largest ML-DSA frame is 1,422 bytes in every run. That’s the fragmentation MTU doing its job: instead of one enormous datagram you get a dozen or more well-behaved ones. Exactly why the router nags you about fragmentation. Choosing a parameter set. ML-DSA-65 costs 35% more IKE AUTH bytes than ML-DSA-44 and buys you NIST security category 3 instead of 2. ML-DSA-87 costs another 34% on top for category 5. Unless you have a specific mandate, 65 is the sensible default . Is 20 KB a problem? For a site-to-site tunnel that negotiates once and rekeys every 24 hours, no. For a hub terminating thousands of DMVPN spokes that all reconnect after a power event, definitely something to factor into capacity planning. The cost is per-negotiation, not per-packet: once the SA is up, ESP data plane traffic is unchanged. Where we stand now The tunnel is fully quantum-safe. But we just proved it on a single pair of routers. How do you migrate authentication across a whole network? That’s a different problem from key exchange, and the answer is less comfortable. Key exchange negotiates with optional ; authentication has no fallback keyword. Part 11 walks through the migration playbook, the experiments that show where it breaks, and the operational gotchas that only show up on real hardware. See you there. Bring your planning hat.