bg-tutorials

YubiKey 5 FIPS: CSR Generation and Attestation

A code signing order placed against a YubiKey 5 FIPS you already own does not just need a CSR. It needs proof that the private key behind that CSR was created inside the token and has never existed anywhere else. That proof is the attestation package, and it is the part most people get wrong.

This guide covers the whole job on Windows: putting the token into its FIPS approved mode, generating the key pair on the device, producing the CSR, producing the attestation certificate, exporting the Yubico intermediate, and combining the two into the single file the Certificate Authority accepts. Every command here uses the ykman CLI, because the YubiKey Manager GUI that older instructions rely on is no longer available.

What the Certificate Authority Is Actually Asking For

Publicly trusted code signing certificates have to be generated and kept in hardware. When you supply your own token instead of buying one from the CA, the CA has no way to watch that happen, so it asks the token to vouch for itself. That is what attestation is.

Yubico’s own documentation describes the mechanism plainly: attestation “is used to show that an asymmetric key was generated on the YubiKey and therefore does not exist outside the device”. Every YubiKey ships with PIV slot f9 pre-loaded from the factory with a key and a certificate signed by Yubico’s PIV Root CA. When you ask for an attestation of slot 9a, the token mints a short-lived certificate that contains the public key from slot 9a and signs it with the key in slot f9.

So the package you upload is a two-link chain, and both links matter:

  1. The attestation certificate for slot 9a. It carries the same public key as your CSR, which is how the CA ties the two together.
  2. The Yubico attestation intermediate read out of slot f9. It is what lets the CA verify the signature on the certificate above and follow it back to Yubico’s root.

Both must be Base64 encoded PEM, in one file, attestation certificate first. Two facts follow from this and they cause most of the failed submissions:

  • Attestation only works on keys the token generated itself. If you create a key on your computer and import it, the attest command fails. There is nothing to attest to, which is the whole point.
  • The attestation has to be produced from the same key as the CSR. If you generate a new key pair in slot 9a after making either file, both are stale and the CA will reject the pair.

One more thing worth checking before you start. Some CAs need key attestation switched on for your account first. Sectigo’s own YubiKey article states that support has to enable the Key Attestation Required setting on your code signing certificate template before you begin. If you do not see a field to upload an attestation package, ask support to enable it rather than trying to submit without one.

The YubiKey Manager GUI No Longer Exists

If you have followed instructions that told you to open YubiKey Manager, click Applications, then PIV, then Certificates, then Generate, that path is gone. Yubico announced the end of life of YubiKey Manager (GUI) on February 19, 2025, and the tool reached end of life on February 19, 2026. Yubico’s ykman documentation now states it directly: “YubiKey Manager GUI is end of life, as of February 19, 2026. It is no longer supported.”

Two replacements are named, and only one of them can do this job:

  • The ykman CLI is not affected by the end of life and is the complete route. Yubico calls it “the premier tool for advanced management and configuration of all YubiKey applications”. Every step below uses it.
  • Yubico Authenticator is the recommended GUI, but it cannot generate a CSR. Its PIV Certificates screen documents Import file under ACTIONS, which is how you put an issued certificate back onto the token later. There is no CSR generation and no attestation there. So it replaces the old GUI for installation, not for this page.

Yubico also recommends uninstalling the old GUI if you still have it, partly because it bundled an older copy of the ykman CLI that you do not want to be running by accident.

Before You Start

  • A YubiKey 5 FIPS Series token, plugged in. This guide is written for Windows. The commands themselves are identical on macOS and Linux, only the installation and the file joining differ.
  • The current ykman CLI. Download the Windows installer, currently yubikey-manager-5.9.2-win64.msi, from Yubico’s ykman releases page. Watch the file name: an installer with -qt in it is the old GUI build, and you do not want that one.
  • A code signing order placed with the “install on existing HSM” option, so the CA expects a CSR and an attestation package from you.
  • An empty working folder, for example C:\codesigning. Seven files end up there and you will submit two of them.

Open PowerShell and check that Windows can find ykman

Press the Windows key, type PowerShell and open it. You need Administrator rights to install ykman, but not to run any of the commands in this guide, so an ordinary window is fine.

Opening Windows PowerShell from the Windows search box

Change into your working folder before you run anything, because the commands below write their output files into whatever directory the shell is currently in:

cd C:\codesigning

That step matters most in an elevated window, which opens in the Windows system directory instead of your user profile, so files created with a plain file name land somewhere you will not think to look. Now check that the tool is reachable:

ykman --version

A version number means you can use the short ykman form for everything below. If Windows answers that the term is not recognized, the installer did not add it to your PATH. The Windows package installs under C:\Program Files\Yubico, so locate the executable and call it by full path:

Get-ChildItem "C:\Program Files\Yubico" -Recurse -Filter ykman.exe

Older guides tell you to run cd 'C:\Program FilesYubico\YubiKey Manager\' and then call .\ykman.exe. That path is broken: a backslash is missing after Program Files, and the folder name belonged to the GUI package anyway. Do not copy it. Either use ykman from your PATH, or use the full path that the command above prints.

Confirm the token is seen and note its firmware version, which decides which algorithms are available to you:

ykman info

Put the Token Into FIPS Approved Mode First

This step is missing from most YubiKey code signing walkthroughs and it will stop you dead if you skip it. A YubiKey 5 FIPS ships with its PIV application on factory defaults, and in that state it is FIPS capable but not FIPS approved. ykman checks for this and refuses:

YubiKey FIPS must be in FIPS approved mode prior to key generation.

Check where you stand. On a FIPS capable token this prints a FIPS approved line:

ykman piv info

If it reads False, you get there by changing all three factory credentials: the management key, the PIN and the PUK. The YubiKey 5 FIPS Series technical manual lists the defaults as management key 010203040506070801020304050607080102030405060708, PIN 123456 and PUK 12345678. Replace the placeholder values below with your own and store them somewhere safe, because a lost management key means a wiped PIV application and a wasted certificate.

ykman piv access change-management-key -m 010203040506070801020304050607080102030405060708 -a AES192 -n YOUR_NEW_MANAGEMENT_KEY
ykman piv access change-pin -P 123456 -n YOUR_NEW_PIN
ykman piv access change-puk -p 12345678 -n YOUR_NEW_PUK

Two notes on those values. On a FIPS token the new PIN and the new PUK must each be exactly 8 alphanumeric characters. The familiar 6 to 8 rule applies to non-FIPS YubiKeys, and ykman rejects anything shorter here with “New PIN must be exactly 8 characters long”, which is a common first stumble. Numeric values travel better across platforms. The management key algorithm depends on the token: AES-192 is the default from firmware 5.7 onward, while older firmware defaults to Triple DES, and a FIPS 140-3 token will only take an AES key. If the command complains about the algorithm, run ykman piv info and match what it reports.

Run ykman piv info once more and confirm FIPS approved now reads True before you go on.

Choosing the Slot and the Key Algorithm

Slot 9a

PIV slot 9a is the Authentication slot, and it is the slot Sectigo’s YubiKey process names for code signing. Use whichever slot your CA’s own instructions specify, because the attestation you send has to be for the slot that will later hold the issued certificate.

Be aware that generating a key in a slot overwrites whatever was there. If this token is also used for Windows login or a VPN, check ykman piv info first and pick a free slot rather than clearing one you still need.

Why the algorithm is usually ECC, and what the real rule is

Older instructions say to pick ECCP256 or ECCP384 “as YubiKey supports only ECC algorithms for EV Code Signing”. That is not what is happening, and the inaccuracy matters because the real rule changes with your firmware. Two independent limits meet here.

The CA side. The CA/Browser Forum Code Signing Baseline Requirements set a floor on subscriber key size. Section 6.1.5.2 requires RSA-3072 as the minimum for subscriber code signing keys, a rule the Relevant Dates table puts into effect on June 1, 2021. RSA-2048 has not been issuable for code signing since then. ECDSA on the NIST P-256 and P-384 curves stays acceptable. Note that this is not an EV rule: it applies to organization validated code signing certificates too.

The token side. Yubico’s technical manual gives the supported PIV algorithms per FIPS version, and they are not the same list:

  • FIPS 140-2 tokens (the firmware 5.4 generation) support RSA-2048, ECC P-256 and ECC P-384. Yubico’s table lists 3072-bit keys among the exclusions for that generation, and RSA-1024 is excluded on every FIPS token.
  • FIPS 140-3 tokens (firmware 5.7 and later) add RSA-3072 and RSA-4096.

Put the two together and the picture is simple. On a FIPS 140-2 YubiKey the largest RSA key the device can make is 2048 bits, which is below the CA’s floor, so ECCP256 or ECCP384 are your only usable choices. On a FIPS 140-3 YubiKey, RSA-3072 and RSA-4096 become available and RSA is a valid option. Yubico’s library enforces the same boundary in code, refusing those key types with “requires YubiKey 5.7 or later”. Yubico also notes that these restrictions apply to attestation as well as to the key itself.

ECCP384 is a sound default either way. It is inside the FIPS 140-2 list, so it works on both generations, and it clears the CA requirement. The examples below use it.

Generate the Key Pair and the CSR

Step 1: generate the key pair inside the token

ykman piv keys generate --algorithm ECCP384 9a public.pem

The private key is created on the YubiKey and never leaves it. What lands in your folder is public.pem, the matching public key, which the CSR command in the next step needs.

Generating a key is a management operation, so ykman asks for the management key. Leave the credentials off the command line and it prompts, hiding what you type. If you prefer to pass them, the options are -m for the management key and -P for the PIN, but they then sit in your shell history.

Two optional hardening flags are worth knowing about. --pin-policy ALWAYS makes the token demand the PIN for every signature instead of once per session, and --touch-policy ALWAYS makes it require a physical touch. Both are good practice for a signing key, and both affect the next step: with a touch policy set, the command that builds the CSR appears to hang while the token’s light blinks, and it only finishes once you touch the contact. On a FIPS token a PIN policy of NEVER is not permitted in any case.

Step 2: generate the CSR

ykman piv certificates request --subject "CN=Example Company Ltd,O=Example Company Ltd,L=Boston,ST=Massachusetts,C=US" 9a public.pem codesigning.csr

The three positional arguments are the slot, the public key file from step 1, and the name of the CSR to write. Signing the request uses the private key in the slot, so ykman asks for the PIN here, and for a touch if you set that policy.

The subject is required and is given as an RFC 4514 string. Use your validated legal entity name, exactly as it appears on the registration documents you gave the CA, in both CN and O. Do not put the CA’s name there. If you pass a bare string with no equals sign, ykman treats it as a common name and prepends CN= for you, which is convenient but drops the rest of the subject, so spell it out.

The default hash is SHA-256. Add -a SHA384 if your CA asks for it.

Build the Attestation Package

Step 3: attest the key in slot 9a

ykman piv keys attest --format PEM 9a attestation.crt

Note the file name at the end. Older instructions publish this command with the name missing, as -F PEM 9a .crt, which either fails or leaves you with a file called .crt that you cannot find afterwards. Give it a real name.

Attestation needs no PIN and no management key. It reads the public key out of the slot and signs it with slot f9, so there is nothing to unlock. PEM is already the default, and the flag is here only to make the output format explicit.

Step 4: export the Yubico attestation intermediate from slot f9

ykman piv certificates export --format PEM f9 intermediateCA.crt

Slot f9 is the PIV attestation slot. Its certificate arrives from the factory signed by Yubico’s PIV Root CA, and it is the link that lets the CA validate the file from step 3. This is a read, so again no credentials are needed.

Yubico refreshed this CA in 2025, and tokens with firmware 5.7.4 and later are signed by the newer root. That is one more reason to export the intermediate from your own token rather than reusing a copy from an older device or an old order.

Step 5: combine the two into one PEM file

This is the step that quietly breaks submissions, so it is worth reading before you run anything. Older guides tell you to open PowerShell and then hand you a line of cmd.exe syntax:

type attestation.crt intermediateCA.crt > attestation.pem

That line is correct in Command Prompt and wrong in PowerShell, in two separate ways. In PowerShell, type is an alias for Get-Content, which does not accept two space separated paths in that position, so it errors before it reads anything. And even if it did read them, the redirect is the real trap: in Windows PowerShell 5.1 the redirection operators are equivalent to piping into Out-File, whose default encoding Microsoft documents as unicode, meaning UTF-16 little endian. Your certificates are ASCII text, but the file you would upload is UTF-16 with a byte order mark and a null byte between every character. The CA sees something that is not Base64 and refuses it. Older versions of this guide close with a vague instruction to make sure “the file is correctly encoded”, which is a symptom of exactly this problem rather than a fix for it.

Use this in PowerShell instead. The comma builds a list of paths, and the encoding is stated rather than inherited:

Get-Content attestation.crt, intermediateCA.crt | Set-Content -Encoding ascii attestation.pem

That form works in Windows PowerShell 5.1 and in PowerShell 7, where the default would otherwise differ again. If you would rather use Command Prompt, open cmd.exe and the original line is fine there, because cmd writes the bytes through unchanged. It inserts nothing between the two files, though, so if the first one ends without a newline the two certificates run together on a single line and the result is not valid PEM. The check in the next step catches that.

Order matters: the attestation certificate first, the intermediate second. Watch the capitalization of intermediateCA.crt too. Windows will not care, but a CA’s parser and any script you write later might, and older instructions spell it both ways in the same section.

The same encoding trap applies to ykman itself, which is why every command in this guide passes an output file name as an argument. Never redirect ykman’s output into a file with > in PowerShell 5.1. Let the tool write the file.

Step 6: check the file before you upload it

Thirty seconds here saves a rejected order. First, count the certificates. You should see exactly two matches, on two separate lines:

Select-String -Path attestation.pem -Pattern "BEGIN CERTIFICATE"

Then look at the raw bytes at the start of the file:

Format-Hex -Path attestation.pem | Select-Object -First 1

That prints the first sixteen bytes. A good file starts with 2D 2D 2D 2D 2D, the five hyphens of the PEM header, and every byte after that is printable. A file written with the default redirect starts with FF FE and then alternates real characters with 00. If you see that, redo step 5.

If you have OpenSSL available, and Git for Windows ships a copy, confirm that the attestation really matches the CSR. These two commands must print an identical public key block:

openssl req -in codesigning.csr -noout -pubkey
openssl x509 -in attestation.crt -noout -pubkey

A mismatch means the two files came from different keys, almost always because slot 9a was regenerated between step 2 and step 3. Repeat steps 1 to 5 in order and do not touch the slot in between.

Step 7: encode the package the way your CA asks for it

One step is missing from most write-ups, and Sectigo is explicit about it: the attestation field does not take the PEM file you have just built. Sectigo’s own instructions say the attestation service expects a Base64 blob with no PEM header or footer lines, so attestation.pem is Base64 encoded a second time and the wrapper lines are stripped. In PowerShell or Command Prompt:

certutil -encode attestation.pem attestation.b64
findstr /v CERTIFICATE attestation.b64 > attestation_clean.b64

The first command Base64 encodes the whole file and wraps its output in BEGIN and END CERTIFICATE lines of its own; the second strips those lines and leaves the bare blob in attestation_clean.b64. On macOS or Linux one command does both:

base64 < attestation.pem > attestation_clean.b64

This is also where the encoding check in step 6 pays for itself. The blob is a Base64 encoding of whatever bytes are in the file, so a UTF-16 attestation.pem produces a perfectly valid-looking blob that decodes to garbage, and the rejection message tells you nothing about why.

Check your CA’s own form before you paste. Sectigo asks for the CSR in PEM with its header and footer lines, and the attestation as this bare Base64 blob. A CA that asks for the attestation as a PEM file wants attestation.pem instead, which is why both files are worth keeping until the certificate is issued.

Submit the CSR and the Attestation Package

You now have seven files in your working folder. Two of them are for the CA:

  • codesigning.csr, pasted into or uploaded to the CSR field of your order.
  • attestation_clean.b64, pasted into the attestation or key attestation field. Upload attestation.pem instead only if your CA asks for the PEM form.

The rest, public.pem, attestation.crt, intermediateCA.crt, attestation.pem and attestation.b64, are working files. Keep them until the certificate is issued and installed, in case you have to resubmit.

Do not reset the PIV application, generate anything else into slot 9a, or reset the YubiKey while validation is in progress. The private key backing your order lives there and cannot be recovered or moved to another token. If it is destroyed you start the order again.

Once the CA issues the certificate, the next job is writing it back into the same slot. That is covered in YubiKey 5 FIPS HSM code signing certificate installation, and it is the one part of the old GUI workflow that Yubico Authenticator can genuinely replace, through Certificates and Import file. The ykman equivalent is a single command:

ykman piv certificates import 9a codesigning.crt

When a Command Fails

  • “YubiKey FIPS must be in FIPS approved mode prior to key generation.” The default PIN, PUK or management key is still in place. Change all three, then check ykman piv info again.
  • “Attestation failed.” The slot is empty, or the key in it was imported rather than generated on the device. Attestation only covers keys the token made itself, so generate a new pair in the slot and start again from step 1.
  • “No private key in slot 9a.” Step 1 has not run, or it ran against a different slot.
  • “requires YubiKey 5.7 or later” after asking for RSA-3072 or RSA-4096. Your token is the FIPS 140-2 generation. Use ECCP256 or ECCP384 instead.
  • The term ykman is not recognized. The installer did not add it to your PATH. Find ykman.exe under C:\Program Files\Yubico and call it by full path.
  • The CA rejects the attestation file. In order of likelihood: the file is UTF-16 rather than ASCII, it holds one certificate rather than two, the two are in the wrong order, or the key in slot 9a changed after the CSR was made.
  • The PIN is blocked. Too many wrong attempts locks the PIN. Unblock it with the PUK using ykman piv access unblock-pin. If the PUK is blocked as well, the only route left is a PIV reset, which erases the key.

Frequently Asked Questions

Can I still generate the CSR with the YubiKey Manager GUI?

No. Yubico announced the end of life of YubiKey Manager (GUI) on February 19, 2025 and it reached end of life on February 19, 2026. Yubico recommends uninstalling it. The ykman command line tool is explicitly not affected and is the supported way to do this.

Can Yubico Authenticator generate the CSR instead?

No. Yubico Authenticator is the recommended graphical replacement for the old GUI, but its PIV Certificates screen offers Import file, which puts an issued certificate onto the token. It does not generate a CSR and it does not produce an attestation. Use it for the installation step after the certificate is issued, and ykman for everything on this page.

What exactly goes into attestation.pem, and in what order?

Two Base64 encoded PEM certificates in one file. First the attestation certificate for slot 9a, produced by ykman piv keys attest, which carries the same public key as your CSR. Second the Yubico attestation intermediate exported from slot f9, which signed the first one and chains back to Yubico’s PIV Root CA. Sectigo then wants that combined file Base64 encoded a second time, with no header or footer lines, and it is that blob you paste into the attestation field.

Why does my Certificate Authority reject the attestation file?

Two causes account for almost all of it. The first is the format: Sectigo’s attestation field expects the combined file Base64 encoded again, with no PEM header or footer lines, so a plain attestation.pem pasted straight in is rejected. The second is character encoding. In Windows PowerShell 5.1 the redirection operator writes UTF-16 little endian, because it is equivalent to Out-File, whose default encoding is documented as unicode. The CA needs plain Base64 text. Combine the files with Set-Content -Encoding ascii, or do it in Command Prompt, then check the first bytes with Format-Hex: a good file begins with five hyphens, a broken one begins with FF FE.

Do I have to use an ECC key?

On a FIPS 140-2 YubiKey 5 FIPS, effectively yes. The CA/Browser Forum Code Signing Baseline Requirements set a minimum of RSA-3072 for code signing certificates, in force since June 1, 2021, and on that generation of token RSA-2048 is the largest RSA key available, with RSA-1024 refused outright on every FIPS model. ECC P-256 and P-384 are supported and acceptable, so they are the only workable options. On a FIPS 140-3 token with firmware 5.7 or later, RSA-3072 and RSA-4096 are available and RSA is a valid choice. This is a key size floor, not an EV-only rule, and it is not a claim that the token cannot do RSA at all.

Can I generate the key on my computer and import it to the YubiKey?

You can import a key, but you cannot attest one, and without attestation the order cannot proceed. The attestation certificate exists to prove the key was created inside the device and has never existed outside it, so the attest command fails on an imported key by design.

Do I need to run PowerShell as Administrator?

Only to install ykman. None of the commands in this guide need an elevated window. If you use one anyway, change into your working folder first, because an elevated PowerShell opens in the Windows system directory and your files will be written there.

Which slot should I use, 9a or 9c?

Follow your CA’s instructions. Sectigo’s YubiKey process uses slot 9a, the Authentication slot, and this guide matches that. Slot 9c is the Digital Signature slot and requires the PIN for every operation by definition, which some teams prefer for a signing key, but the attestation you submit must be for the slot that will hold the issued certificate, so pick one and stay with it.

What happens if I lose the YubiKey or reset the PIV application?

The private key is gone and cannot be recovered, copied, or moved to a replacement token. That is the property the attestation certifies. You would need a new certificate issued against a fresh key on a new device, so treat the token as the single copy it is and keep the PIN, PUK and management key recorded somewhere safe.

For the rest of the process, see how to validate Sectigo code signing certificates and how to sign an EXE file with a code signing certificate. If you are still deciding how to receive the certificate, code signing certificate delivery methods compares supplying your own HSM with the other options.

Save 10% on SSL Certificates when ordering from SSL Dragon today!

Fast issuance, strong encryption, 99.99% browser trust, dedicated support, and 25-day money-back guarantee. Coupon code: SAVE10

A detailed image of a dragon in flight
Written by

I've been writing for SSL Dragon for over 10 years, focusing entirely on SSL certificates and digital security. My job is to take complex cybersecurity topics and strip away the jargon, making sure you get the clear, practical information you need to keep your website safe.