IPsec

Certificate Authentication for IPsec: Trustpoints, Enrollment, and Revocation

Certificate authentication: real IKEv2 SA showing Auth sign RSA instead of a pre-shared key
In: IPsec, VPN, PKI, CCIE, Labs

Pre-shared keys are a fine way to learn IPsec and a poor way to run it. A PSK is a shared secret sitting in a config file, in plain sight of anyone with read access to the device, in your backup repository, and in the change ticket where someone pasted the running config. It does not scale either: with N peers you have N keys to distribute, and rotating one of them means touching both ends at the same time without dropping the tunnel.

Certificates fix both problems at once. Each device holds its own private key, which never leaves it, and a certificate signed by a common authority that every peer already trusts. Add a peer and you enroll it once; nobody else's config changes. Compromise a device and you revoke one certificate rather than rekeying a mesh. This article is the end-to-end walkthrough: build the trustpoint, authenticate the CA, enroll, and then bring up an IKEv2 tunnel with no pre-shared key anywhere in its configuration. It is the natural next step after the IPsec VPN guide, and it assumes you have a CA to enroll against.

We do. In the previous article we turned a cat8000v into a working IOS certificate authority called PLZ-CA. Everything below enrolls against that exact CA and uses the certificates it issued. All output is from the live lab.

The Trustpoint: Where Your Certificate Lives

A trustpoint is IOS's container for a certificate relationship. It holds the CA's certificate (so you can verify things the CA signed), your own identity certificate (so peers can verify you), the keypair backing that certificate, and the policy for how you got it and how you check it. One trustpoint, one CA relationship.

Here is GM1's, exactly as configured:

crypto key generate rsa modulus 2048 label PLZ-GM1-KEY
crypto pki trustpoint PLZ-TP
 enrollment url http://10.100.0.31:80
 subject-name CN=GM1.pinglabz.lab,O=PingLabz
 revocation-check none
 rsakeypair PLZ-GM1-KEY
crypto key generate rsa ... label PLZ-GM1-KEY
Generate the keypair FIRST, with a label, so the trustpoint can reference it. The private half never leaves this router. The public half goes into the CSR and ends up inside the certificate.
enrollment url http://10.100.0.31:80
Where the CA lives. This is SCEP over HTTP: the router will fetch the CA certificate from here and later POST its CSR to the same place. Reachability matters, so make sure routing to the CA works before you start.
subject-name CN=GM1.pinglabz.lab,O=PingLabz
The identity you are claiming. This becomes the Subject field of your certificate, and it is what the peer will match against. Get it wrong here and you will be re-enrolling later.
revocation-check none
Do not check whether the peer's certificate has been revoked. This is a lab choice and we are being upfront about it. See the revocation section below for what production should say.
rsakeypair PLZ-GM1-KEY
Bind the trustpoint to the keypair you generated. Without this, IOS picks the default general-purpose key, which is fine until you have two trustpoints and want them isolated.

Step 1: Authenticate the CA (and Actually Check the Fingerprint)

Before you can get a certificate, you have to decide you trust the entity that issues them. crypto pki authenticate fetches the CA's self-signed root certificate over SCEP and asks you a question. Here is the real exchange from GM1:

GM1(config)#crypto pki authenticate PLZ-TP
Certificate has the following attributes:
       Fingerprint MD5: 50AE2189 0FC62684 1418DEF0 B0EB714A
      Fingerprint SHA1: CAC64CCF 98899856 A5F002E5 C168BF80 0AB8355F

% Do you accept this certificate? [yes/no]: yes
Trustpoint CA certificate accepted.

Now go and look at what the CA itself reported when we brought it up:

CA1#show crypto pki server
Certificate Server PLZ-CA:
    Status: enabled
    CA cert fingerprint: 50AE2189 0FC62684 1418DEF0 B0EB714A

Same value. 50AE2189 0FC62684 1418DEF0 B0EB714A on the CA, and 50AE2189 0FC62684 1418DEF0 B0EB714A on the client. That match is not a curiosity. It is the entire security of this step, and it deserves a hard stare.

So IOS punts to the human. It prints the fingerprint of whatever certificate it just received and asks you to confirm. Your job, and this is the whole job, is to obtain the real CA's fingerprint through a channel the attacker does not control (an SSH session to the CA, a phone call to the person who runs it, a printed sheet in a binder) and compare it character by character. If they match, you are talking to the real CA. If they do not, you have just caught an attack.

If you type yes without doing that comparison, you have authenticated nothing. You have simply agreed to trust whatever answered. Every certificate that CA later signs, including the ones an attacker mints for himself, will validate perfectly on your router. The rest of your PKI is now theatre. This is a five-second check and skipping it invalidates everything downstream, so do not skip it.

Step 2: Enroll (Get Your Own Certificate)

With the CA trusted, ask it for an identity:

GM1(config)#crypto pki enroll PLZ-TP

This is also interactive: IOS asks for a challenge password (used for revocation requests later), whether to include the serial number and IP address in the subject, and whether to send the request now. The router then builds a CSR carrying your public key and subject name, signs it with your private key to prove you hold that key, and posts it to the enrollment URL. The private key itself never moves. The CA only ever sees the public half, which is the structural advantage over a PSK: no secret is ever in transit, and no secret ends up sitting in two config files at once.

Because our CA runs grant auto, it signs immediately and hands back the certificate. Syslog confirms:

%PKI-6-CERT_INSTALL: An ID certificate has been installed

The Certificate We Actually Got

Real output from GM1 after enrollment:

GM1#show crypto pki certificates PLZ-TP
Certificate
  Status: Available
  Certificate Serial Number (hex): 02
  Certificate Usage: General Purpose
  Issuer:
    cn=PLZ-CA
    o=PingLabz
    c=US
  Subject:
    Name: GM1.pinglabz.lab
    hostname=GM1.pinglabz.lab
    cn=GM1.pinglabz.lab
    o=PingLabz
  Validity Date:
    start date: 09:08:26 UTC Jul 13 2026
    end   date: 09:08:26 UTC Jul 12 2028
  Associated Trustpoints: PLZ-TP

CA Certificate
  Status: Available
  Certificate Serial Number (hex): 01
  Certificate Usage: Signature
  Issuer:
    cn=PLZ-CA
    o=PingLabz
    c=US
  Subject:
    cn=PLZ-CA
    o=PingLabz
    c=US
  Validity Date:
    start date: 09:04:14 UTC Jul 13 2026
    end   date: 09:04:14 UTC Jul 12 2031
  Associated Trustpoints: PLZ-TP

Two certificates, and the difference between them is the whole of PKI.

The ID certificate (serial 02) is GM1's identity. Subject is cn=GM1.pinglabz.lab: this is who GM1 says it is. Issuer is cn=PLZ-CA: this is who vouches for that claim. Subject and Issuer are different, which is exactly what you expect from a certificate someone else signed. Validity runs two years, from Jul 13 2026 to Jul 12 2028, matching the CA's lifetime certificate 730. Usage is General Purpose, meaning it can be used for both signing and encryption.

The CA certificate (serial 01) is the trust anchor. Look closely: Subject and Issuer are identical, both cn=PLZ-CA. It signed itself, because there is nothing above it. Usage is Signature, because its job is to sign other certificates. Validity runs five years, out to 2031, comfortably outliving every certificate it will issue. GM1 holds this because you typed yes at the fingerprint prompt, and it is what GM1 will use to verify GM2's certificate later.

The Payoff: An IKEv2 Tunnel With No Pre-Shared Key

Now build a static VTI between GM1 and GM2 and authenticate it with those certificates:

crypto ikev2 proposal PLZ-CERT-PROP
 encryption aes-cbc-256
 integrity sha256
 group 14
crypto ikev2 policy PLZ-CERT-POL
 proposal PLZ-CERT-PROP
crypto ikev2 profile PLZ-CERT-PROF
 match identity remote fqdn GM2.pinglabz.lab
 identity local fqdn GM1.pinglabz.lab
 authentication local rsa-sig
 authentication remote rsa-sig
 pki trustpoint PLZ-TP
crypto ipsec profile PLZ-CERT-IPSEC
 set transform-set PLZ-CERT-TS
 set ikev2-profile PLZ-CERT-PROF
interface Tunnel5
 ip address 10.0.5.1 255.255.255.252
 tunnel source GigabitEthernet2
 tunnel mode ipsec ipv4
 tunnel destination 10.100.0.22
 tunnel protection ipsec profile PLZ-CERT-IPSEC

Read that config again and look for a secret. There is not one. No crypto isakmp key, no pre-shared-key local, no pre-shared-key remote, nothing. The three lines doing the work are:

  • authentication local rsa-sig - I will prove who I am by signing with my private key. Verify me against my certificate.
  • authentication remote rsa-sig - I expect the peer to do the same. I will verify their signature against their certificate, and their certificate against the CA.
  • pki trustpoint PLZ-TP - use this trustpoint for both. It holds my certificate and the CA certificate I need to check theirs.

The identity local and match identity remote lines are the identity contract, and they are about to cause us a problem.

The Failure We Hit: Identity Type Mismatch

The first time we brought this up, IKEv2 refused, with this:

IKEv2-ERROR:(SESSION ID = 1,SA ID = 1):: Auth exchange failed

That is the entire error. It tells you the authentication exchange failed, which you had already worked out, and nothing else. No mention of certificates, no mention of identities, no hint about which side had the problem. This message is the single most common thing you will see when certificate authentication goes wrong, and it is almost useless in isolation. Which is precisely why the pattern below is worth committing to memory.

The cause: we had configured identity local dn (send my identity as a Distinguished Name, taken from the certificate subject) while the peer's profile said match identity remote fqdn GM2.pinglabz.lab. So GM1 announced itself with a DN, and GM2's profile was looking for an FQDN. GM2 received a perfectly valid identity, in a perfectly valid certificate, signed by a CA it trusts, and rejected it because it was the wrong type of identity.

The fix took one line on each side:

crypto ikev2 profile PLZ-CERT-PROF
 identity local fqdn GM1.pinglabz.lab

The tunnel came up immediately.

When you see Auth exchange failed, check the identity types on both ends before anything else. The rule to internalize: the identity type you SEND must be the identity type the peer is told to MATCH. IKEv2 identities come in several flavours (fqdn, dn, address, email, key-id) and they are not interchangeable. If your side says identity local dn, the peer must say match identity remote dn ... with the full DN string. If your side says identity local fqdn GM1.pinglabz.lab, the peer must say match identity remote fqdn GM1.pinglabz.lab. Mixing the two produces a generic auth failure and hours of staring at crypto debugs.

The Proof: Auth sign RSA

With the identities aligned, here is the SA:

GM1#show crypto ikev2 sa detailed
Tunnel-id Local                 Remote                fvrf/ivrf            Status
1         10.100.0.21/500       10.100.0.22/500       none/none            READY
      Encr: AES-CBC, keysize: 256, PRF: SHA256, Hash: SHA256, DH Grp:14,
      Auth sign: RSA, Auth verify: RSA
      Local id: GM1.pinglabz.lab
      Remote id: GM2.pinglabz.lab

Auth sign: RSA, Auth verify: RSA. On every pre-shared-key tunnel in this series, that field said PSK. Here it says RSA, twice: GM1 signed the IKE_AUTH exchange with its private key, and it verified GM2's signature using GM2's certificate, which it validated against the PLZ-CA certificate in its trustpoint.

Local id: GM1.pinglabz.lab and Remote id: GM2.pinglabz.lab are the identities that were exchanged and matched. Those strings came out of the certificates, and the certificates came out of a CA both routers independently decided to trust. No secret was ever shared between GM1 and GM2. They have still never held a key in common, and yet each one is certain of who it is talking to.

Revocation: Being Honest About It

Our trustpoint says revocation-check none. That means when GM1 validates GM2's certificate, it checks the signature and the validity dates, and stops. It does not ask whether the certificate has been revoked. If GM2 were compromised, its certificate torn up, and its revocation published to the world, GM1 would still cheerfully build a tunnel to it, right up until the certificate expired in 2028.

That is a real hole and it is on us. Here are the three options:

revocation-check none
What it does: no revocation check at all.
Use when: lab, or a closed environment where you can physically reach every device.
Cost: a revoked certificate stays usable until it expires. This is what we used, and we are telling you so.
revocation-check crl
What it does: download the CA's revocation list, cache it, check the peer's serial against it.
Use when: you have a reliable CRL distribution point and can tolerate staleness.
Cost: a CRL is a snapshot. Revoke a certificate five minutes after the CRL was published and you are exposed until the next one. Also, if the CRL is unreachable, validation fails and your tunnels drop.
revocation-check ocsp
What it does: ask an OCSP responder about this one certificate, right now.
Use when: you want real-time revocation status and can run a highly available responder.
Cost: the responder is now in the path of every tunnel establishment. It must be up, and it must be fast.

For production, pick ocsp or crl based on what you can actually keep running, and think hard about whether a revocation check failure should drop tunnels (secure) or allow them (available). There is no free answer. What there definitely is not is a good reason to leave none on a production edge.

PSK vs Certificates

Scaling
PSK: a key per peer pair. A full mesh of N sites is N(N-1)/2 secrets to generate and distribute.
Certificates: enroll each device once against a common CA. Adding site 51 changes nothing on sites 1 through 50.
Where the secret lives
PSK: in plain text in two config files, your backups, and probably a chat thread.
Certificates: the private key is generated on the device and never leaves it. Nothing secret is ever transmitted.
Rotation
PSK: a coordinated change on both ends, at the same time, with a tunnel outage in the middle.
Certificates: re-enroll one device. Peers do not care, because they trust the CA, not the device.
Blast radius of a compromise
PSK: a leaked key compromises every tunnel using it, and a lot of designs reuse one key everywhere.
Certificates: a compromised device exposes one private key. Revoke that certificate and it is over (assuming you actually check revocation).
Operational cost
PSK: near zero to start. One line of config and the tunnel is up.
Certificates: you now run a PKI. That means a CA, a clock, enrollment, expiry tracking, and revocation infrastructure. This is real, ongoing work.
Failure modes
PSK: mismatched key, and the error usually says so.
Certificates: expired certificate, wrong clock, identity type mismatch, unreachable CRL. Errors are generic. You must know the patterns, which is why this article exists.

Key Takeaways

  • A trustpoint holds one CA relationship: the enrollment URL, your subject name, your keypair, and the revocation policy. It says nothing about any peer or tunnel, which is exactly why it scales.
  • crypto pki authenticate displays the CA certificate's fingerprint and asks you to accept it. Compare that fingerprint out of band against show crypto pki server on the CA. Ours matched at 50AE2189 0FC62684 1418DEF0 B0EB714A. Typing yes without checking authenticates nothing.
  • crypto pki enroll generates a CSR from your public key and subject name, sends it, and installs the returned identity certificate (%PKI-6-CERT_INSTALL). Your private key never leaves the router.
  • In show crypto pki certificates, the ID certificate has different Subject and Issuer (GM1 signed by PLZ-CA, serial 02). The CA certificate has identical Subject and Issuer, because it signed itself (serial 01).
  • A certificate-authenticated IKEv2 tunnel uses authentication local rsa-sig, authentication remote rsa-sig, and pki trustpoint. There is no pre-shared key anywhere in the configuration.
  • Auth sign: RSA, Auth verify: RSA in show crypto ikev2 sa detailed is the proof it worked. On a PSK tunnel that field says PSK.
  • Identity type mismatch is the number one cert-auth failure. identity local dn against match identity remote fqdn produces a bare Auth exchange failed and no other clue. The type you send must be the type the peer matches.
  • We used revocation-check none and are saying so plainly. Production wants crl or ocsp, and you need to decide deliberately whether a check failure should drop the tunnel or allow it.

The CA that issued these certificates is a plain IOS XE router, built from scratch in the same lab: see running a Cisco router as a CA server for how to stand one up in six lines. If the IKEv2 exchange itself is what you want to understand, IKEv2 explained covers the negotiation these certificates are plugged into, and the IPsec VPN pillar ties the whole progression together, from pre-shared keys through certificates to group encryption.

Written by
More from Ping Labz
Great! You’ve successfully signed up.
Welcome back! You've successfully signed in.
You've successfully subscribed to Ping Labz.
Your link has expired.
Success! Check your email for magic link to sign-in.
Success! Your billing info has been updated.
Your billing was not updated.