---
url: 'https://www.corbado.com/blog/how-to-delete-passkeys-windows-10'
title: 'How to Delete Passkeys on Windows 10'
description: 'Learn how to delete passkeys on Windows 10 using certutil & other tools to enhance Windows Hello. Follow our guide to manage WebAuthn credentials effectively.'
lang: 'en'
author: 'Vincent Delitz'
date: '2024-05-14T09:00:02.836Z'
lastModified: '2026-08-28T15:31:55.088Z'
category: 'Passkeys User Tips'
---

# How to Delete Passkeys on Windows 10

## Key Facts

- Windows 10 has no native UI for passkey management. The **certutil** command-line tool
  or open-source tools are required to list and delete credentials.
- Deleting passkeys requires admin privileges: use **certutil -csp NGC -delkey** with an
  identifier containing the SHA256 of the relying party domain and hex-encoded user.id.
- Windows 10 only supports **RSA 2048-bit** for passkey cryptography, unlike Windows 11
  environments that can leverage ECDSA_P256 via a Mac's secure enclave.
- The quickest method to delete all passkeys: remove and re-add the **Windows Hello PIN**,
  but this may lock users out of real accounts.

## 1. Introduction: How to Delete Passkeys on Windows?

At Corbado, we are developing a Passkey-First authentication solution. Many passkey
challenges come from using passkeys on Windows 10. Windows 10 offers
[device-bound passkeys](https://www.corbado.com/faq/are-passkeys-device-specific), but the operating system lacks
the ability to manage passkeys via a UI.

As the number of passkeys increases within the Windows 10
[authenticator](https://www.corbado.com/glossary/authenticator), [Windows Hello](https://www.corbado.com/glossary/windows-hello) becomes
slower and slower, which is especially unproductive for developers. In this article, we
will:

- **Make a short deep dive into Windows 10 WebAuthn cryptography:** We will examine how
  Windows 10 handles WebAuthn cryptography and where [FIDO2](https://www.corbado.com/glossary/fido2) credentials
  are generally stored on Windows 10. We will start by activating
  [Windows Hello](https://www.corbado.com/glossary/windows-hello) and ensuring it is set up correctly.

- **Find out how to manage and remove passkeys on Windows 10:** We will go through the
  possibilities for listing and identifying passkeys and WebAuthn credentials locally. At
  the end, we will discuss how to remove them.

When having to deal with a lot of credentials, command line tools are not enough.
Therefore, we have extended the
[webauthn-fido2-key-remover](https://github.com/passwordless/webauthn-fido2-key-remover),
an open-source tool to help identify and [delete passkeys](https://www.corbado.com/blog/webauthn-signal-api) more
comfortably with multiple domains and multiple passkeys per domain.

## 2. How to Activate Passkeys on Windows 10?

Before we start deleting passkeys, we will go through some basics of Windows 10
cryptography with passkeys. To activate passkeys on Windows 10, the
[Windows Hello](https://www.corbado.com/glossary/windows-hello) [authenticator](https://www.corbado.com/glossary/authenticator) needs to
be activated. Since Windows 10 can operate without a hardware security module (TPM), it
can store passkeys
[within a software-based HSM](https://helgeklein.com/blog/checking-windows-hello-for-business-whfb-key-storage-tpm-hardware-or-software/).
However, this method is not as secure as using a physical HSM, which is required for
[Windows 11](https://www.corbado.com/blog/passkeys-windows-11).

### 2.1 How to Set a PIN for Windows Hello in Windows 10?

If you are working on a system where you are not sure if Windows Hello is activated, you
can check on [https://state-of-passkeys.io](https://state-of-passkeys.io) for
passkey-readiness. If Windows Hello is activated, the check for passkey-ready should hold
true:

![Test Passkey Readiness](https://www.corbado.com/website-assets/test_passkey_readiness_08621759a5.png)

In case you wonder why [Conditional UI](https://www.corbado.com/glossary/conditional-ui) works without a
[platform authenticator](https://www.corbado.com/glossary/platform-authenticator) (as in the screenshot), you
need to know that this behavior can occur on Chrome browsers. Chrome started to return
true for Conditional-UI-readiness with version 123 (due to privacy considerations). To
activate Windows Hello, open the settings in Windows 10 and navigate to “Sign-in options”.
Start by adding a Windows Hello PIN:

![uploaded image](67897d943075debaf2f121b234667df0.png)![Activate Windows Hello Passkeys](https://www.corbado.com/website-assets/activate_windows_hello_passkeys_9602037513.png)

![uploaded image](5e6c67793fe37c28731c3d427bc9e3ad.png)![Create PIN Windows Hello Passkeys](https://www.corbado.com/website-assets/create_pin_windows_hello_passkeys_d7b46c8e35.png)

Depending on your Windows version, this might look slightly different. After entering the
current account password, the PIN can be added. You can continue to add further biometric
login methods like "Windows Hello Face" or "Windows Hello Fingerprint," but they are not
necessary to activate Windows Hello. They are both shortcuts for the actual PIN. In case a
[Microsoft account](https://www.corbado.com/blog/use-windows-11-without-windows-hello-microsoft-account) is used
with the computer, other forms of verification might be triggered.

### 2.2 How to Verify if Windows Hello is Activated?

Verify that Windows Hello has been successfully activated on
[https://state-of-passkeys.io/](https://state-of-passkeys.io/) you should see something
like this now:

![Full Passkey Readiness Test](https://www.corbado.com/website-assets/full_passkey_readiness_test_0c00996a7a.png)

It is important that not only the WebAuthn API is available and active (“WebAuthn-ready”)
but also “Passkey-ready” is true. For checking the passkey-readiness, the browser is
queried via `PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable()` and
returns true if a [platform authenticator](https://www.corbado.com/glossary/platform-authenticator) ( Windows
Hello) is available.

### 2.3 For Developers: How to Effectively Test Passkeys on Windows 10?

Windows 10 will not get feature updates anymore but still gets security updates and
dominates the desktop market as of May 2024. Our prediction is that Windows 10 will be a
major player in the desktop OS market until 2027. Recent statistics
[released by statcounter](https://gs.statcounter.com/os-version-market-share/windows/desktop/worldwide)
even showed that Windows 10 market share increased slightly for two months in a row.
Therefore, testing on Windows including version 10 is extremely important for any
passkey-based application. You have the following options for testing:

- **Option 1 - Windows 10 PC (our recommendation):** Actually have a real Windows 10
  installation in order to have the most realistic experience and see how the actual UI
  looks. Of course, the best way to test is to actually be developing on Windows 10 but
  depending on the Enterprise among Frontend-heavy and Native-App focused companies Macs
  often dominate.

- **Option 2 - Windows 10 VM on macOS:** In case you have an Intel-based macOS device, you
  can install a Windows 10 image into a simulator like
  [Parallels](https://www.corbado.com/blog/parallels-passkeys-cda), but this needs some more effort as this is
  not officially supported anymore.

- **Option 3 - Windows 11 VM on macOS:** This works very well for a generic feeling of how
  Windows feels like with the latest version of Chrome. Actual compatibility settings
  cannot be tested as [Windows 11](https://www.corbado.com/blog/passkeys-windows-11) has a newer Windows Hello
  version which also includes Cross-Device-Authentication natively, which Windows 10 does
  not.

_At Corbado, we have product managers who volunteered to continue to use Windows 10 as
their primary working environment to get an authentic experience of passkeys on
Windows 10. As they do not necessarily need to run a full docker system locally that
developers might need and which performs better on macOS, it is the best way for us to
keep our device coverage. In addition, we have some spare Windows 10 computers for
developer testing._

## 3. How to List & Analyze Passkeys on Windows 10?

In this section, we will find out how we can list all registered passkeys on a Windows 10
system.

### 3.1 Where does Windows Hello Store Passkeys?

Windows Hello on Windows 10 stores passkeys and WebAuthn credentials in a secure,
system-protected location within the operating system. These credentials are stored in a
similar manner to other cryptographic keys and certificates used for authentication and
encryption purposes. As we have outlined in the introduction, Windows 10 is prepared to do
so even without a HSM in form of an TPM, as this is not a hardware requirement for
Windows 10. Here are the different approaches and options for storage:

#### 3.1.1 Key Storage Locations

##### 3.1.1.1 Local Security Authority (LSA) Secrets

Windows Hello utilizes the Local Security Authority (LSA) to manage sensitive information
such as cryptographic keys and user credentials. LSA Secrets are stored in the Windows
registry under **HKEY_LOCAL_MACHINE\Security\Policy\Secrets**.

These secrets are protected by the LSA and can only be accessed by privileged system
processes, making them secure from unauthorized access.

##### 3.1.1.2 Credential Store

Windows 10 also leverages the Credential Manager to store user credentials. This includes
WebAuthn credentials that are created when users register passkeys through their browsers
or applications.

The Credential Manager stores these credentials in an encrypted format within the
**Credential Store**, which is located in
**%SystemRoot%\System32\config\systemprofile\AppData\Local\Microsoft\Credentials**.

##### 3.1.1.3 Protected User Mode (PUM) and TPM

On systems equipped with a Trusted Platform Module (TPM), Windows Hello can utilize the
TPM to provide additional security. The TPM is a hardware-based security feature that
securely generates, stores, and manages cryptographic keys.

On systems without a TPM, Windows 10 uses a software-based approach to emulate similar
security functions, storing cryptographic keys securely within the system.

#### 3.1.2 How WebAuthn Credentials are Managed

##### 3.1.2.1 Credential Creation:

When a user registers a new WebAuthn credential, a key pair (public and private keys) is
generated. The private key is securely stored on the device (in the LSA Secrets or TPM),
while the public key is sent to the authenticating server.

The public key, along with a unique [credential ID](https://www.corbado.com/blog/webauthn-user-id-userhandle), is
associated with the user's account on the server.

##### 3.1.2.2 Credential Access

During authentication, the system retrieves the appropriate private key from the secure
storage. The user is prompted to verify their identity (e.g., via PIN, fingerprint, or
facial recognition).

Once verified, the private key is used to sign a challenge provided by the server, proving
the user's identity without exposing the private key itself.

#### 3.1.3 Security Measures

- **Encryption:** All stored credentials and keys are encrypted, ensuring they are
  protected from unauthorized access even if an attacker gains physical access to the
  storage medium.

- **Access Controls:** Access to the stored credentials is tightly controlled by the
  operating system, with only privileged processes having the ability to read or modify
  them.

- **Attestation:** Windows Hello supports [attestation](https://www.corbado.com/glossary/attestation), which
  allows the device to prove to the server that it holds a genuine key generated by a
  trusted platform.

In summary, Windows 10 uses a combination of the LSA, Credential Manager, and optionally
TPM, to securely store and manage WebAuthn credentials and passkeys. These components work
together to ensure that user credentials are protected by strong encryption and access
control mechanisms, providing a secure authentication experience.

### 3.2 How to Use Certutil to List & Analyze Passkeys on Windows 10?

After activating Windows Hello on the device, we went on and created a passkey on
[https://state-of-passkeys.io](https://state-of-passkeys.io). In this section, we will
list the created passkeys and explain more details of them.

#### 3.2.1 How to List All Available Passkeys on Windows 10?\`

To list credentials on the system with existing tools, the command line tool `certutil`
has to be used:

**Command:**

`certutil -csp NGC -key -v`

The command `certutil -csp NGC -key -v` involves the use of `certutil`, a command-line
utility for managing certificates in Windows. Here’s a breakdown of the command:

1. **`certutil`**: This is the command-line utility provided by Windows for certificate
   management tasks. It allows users to perform various operations related to
   certificates, including creation, verification, and management of certificate stores.

2. **`-csp NGC`**: This specifies the Cryptographic Service Provider (CSP) to be used.
   CSPs are libraries that implement cryptographic algorithms and manage keys. **NGC (Next
   Generation Credentials)** refers to a CSP associated with the Next Generation
   Credentials framework. NGC was introduced by Microsoft to enhance authentication
   mechanisms in Windows, especially starting from Windows 10. It is part of Microsoft's
   move towards more secure and
   [user-friendly authentication](https://www.corbado.com/faq/passkey-user-experience-benefits-non-technical-audience)
   methods, such as Windows Hello for Business, which includes biometric and PIN-based
   logins.

3. **`-key`**: This option is used to perform operations related to cryptographic keys. It
   can include tasks like key generation, listing, or deletion.

4. **`-v`**: This stands for verbose mode, which provides detailed output of the command's
   execution process. It’s useful for debugging or understanding the steps being
   performed.

#### 3.2.2 Windows 10 PC (Intel CPU): certutil output

Let’s run this command on a native Windows 10 machine after we created a passkey on
[https://state-of-passkeys.io/](https://state-of-passkeys.io/).

**Command:**

`certutil -csp NGC -key -v`

**Output:**

```console
S-1-5-21-3027362018-461445109-2096225366-1001/a4647d39-e794-4db7-bf79-1f672b0b7e6c/FIDO_AUTHENTICATOR//dd52225b9c6e7aafc9d4091b7f97c1b20cbe27644fc1685f65639ede1232aa22_64584e794c5459794d44517a4d6a6b344e7a55794d7a49794e7a41344e6a51

RSA

Key Id Hash (rfc-sha1): 02c300e2bedbcb6a743ca7d6a7b63c6705f836fa

Key Id Hash (sha1): cbd8ad3cc05402afa0bb176708346aea53408832

Key Id Hash (bcrypt-sha1): 23b1cdc3061fc99fbac25be437ad8f7aa807e228

Key Id Hash (bcrypt-sha256): d486770fd7e9f5c9c84a00d590ed36c5b39b1798bc32f7974c3cb2ef359d3c0b

Container des öffentlichen Schlüssels:

0000 30 82 01 0a 02 82 01 01 00 9f 4d 82 57 81 6c 94

0010 7f b4 51 80 8c 2b fc eb e8 94 a5 9b 57 45 6a 41

0020 f0 83 7b 9a 53 31 0e 22 82 82 2e 04 63 ac 77 47

0030 b5 70 08 77 72 e9 64 f4 fc 00 f5 2d 07 b8 b9 e1

0040 d2 54 f4 f2 fc 39 d5 75 a1 3f 4a 30 20 6f 68 81

0050 89 44 31 60 86 98 2b d2 5e 4b 44 f4 01 06 19 4a

0060 c4 04 19 67 bc c6 dd 0e 60 c6 6f eb 30 fe 63 c7

0070 55 62 5f 45 71 5d 48 91 c2 8d 3f 54 26 40 4d 2e

0080 36 64 6a 55 7e 0a 1a 9e f0 60 85 75 13 25 c1 d1

0090 d4 db 44 75 65 41 d4 01 a0 be 9e c6 df 47 c0 5e

00a0 a3 b3 1d 60 45 f2 02 3c 6f e3 56 f0 71 83 af a5

00b0 d9 ed 89 b4 43 34 fa 04 7d d1 00 95 d6 44 1b 75

00c0 02 cb b4 b7 95 bc f2 2d 25 b6 f0 3a 85 d4 ba 1d

00d0 57 75 e5 e5 49 a6 54 c3 b7 1d 8d f7 a1 e8 76 84

00e0 98 b3 d4 73 3c 35 c7 00 09 0c ec 3f 7a a0 8b 3e

00f0 92 de 6b f5 ab 96 42 69 4b d7 3e b6 9f d9 fd 4a

0100 c2 4a 15 9b d3 b4 fe 81 c5 02 03 01 00 01

Cached Key Identifier: S-1-5-21-3027362018-461445109-2096225366-1001/a4647d39-e794-4db7-bf79-1f672b0b7e6c/FIDO_AUTHENTICATOR//dd52225b9c6e7aafc9d4091b7f97c1b20cbe27644fc1685f65639ede1232aa22_64584e794c5459794d44517a4d6a6b344e7a55794d7a49794e7a41344e6a51: No container name match.

Cached Key Identifier: S-1-5-21-3027362018-461445109-2096225366-1001/a4647d39-e794-4db7-bf79-1f672b0b7e6c/FIDO_AUTHENTICATOR//dd52225b9c6e7aafc9d4091b7f97c1b20cbe27644fc1685f65639ede1232aa22_64584e794c5459794d44517a4d6a6b344e7a55794d7a49794e7a41344e6a51: No KeyId match.

NCRYPT_ALLOW_DECRYPT_FLAG -- 1

NCRYPT_ALLOW_SIGNING_FLAG -- 2

(NCRYPT_ALLOW_KEY_AGREEMENT_FLAG -- 4)

(NCRYPT_ALLOW_KEY_IMPORT_FLAG -- 8)

(NCRYPT_ALLOW_ALL_USAGES -- ffffff (16777215))

UI Policy = 0

(NCRYPT_UI_PROTECT_KEY_FLAG -- 1)

(NCRYPT_UI_FORCE_HIGH_PROTECTION_FLAG -- 2)

Version: 0

Export Policy = 0

(NCRYPT_ALLOW_EXPORT_FLAG -- 1)

(NCRYPT_ALLOW_PLAINTEXT_EXPORT_FLAG -- 2)

(NCRYPT_ALLOW_ARCHIVING_FLAG -- 4)

(NCRYPT_ALLOW_PLAINTEXT_ARCHIVING_FLAG -- 8)

Name: S-1-5-21-3027362018-461445109-2096225366-1001/a4647d39-e794-4db7-bf79-1f672b0b7e6c/FIDO_AUTHENTICATOR//dd52225b9c6e7aafc9d4091b7f97c1b20cbe27644fc1685f65639ede1232aa22_64584e794c5459794d44517a4d6a6b344e7a55794d7a49794e7a41344e6a51

Algorithm Group: RSA

Algorithm Name: RSA

Length: 2048 (0x800)

Lengths:

dwMinLength = 2048 (0x800)

dwMaxLength = 2048 (0x800)

dwIncrement = 0 (0x0)

dwDefaultLength = 2048 (0x800)

UI Policy:

dwVersion = 1 (0x1)

dwFlags = 0 (0x0)

pszCreationTitle = (null)

pszFriendlyName = (null)

pszDescription = (null)

Export Policy: 0 (0x0)

(NCRYPT_ALLOW_EXPORT_FLAG -- 1)

(NCRYPT_ALLOW_PLAINTEXT_EXPORT_FLAG -- 2)

(NCRYPT_ALLOW_ARCHIVING_FLAG -- 4)

(NCRYPT_ALLOW_PLAINTEXT_ARCHIVING_FLAG -- 8)

NgcKeyImplType: 2 (0x2)

(NCRYPT_IMPL_HARDWARE_FLAG -- 1)

NCRYPT_IMPL_SOFTWARE_FLAG -- 2

(NCRYPT_IMPL_REMOVABLE_FLAG -- 8)

(NCRYPT_IMPL_HARDWARE_RNG_FLAG -- 10 (16))

Key Usage: 3 (0x3)

NCRYPT_ALLOW_DECRYPT_FLAG -- 1

NCRYPT_ALLOW_SIGNING_FLAG -- 2

(NCRYPT_ALLOW_KEY_AGREEMENT_FLAG -- 4)

(NCRYPT_ALLOW_KEY_IMPORT_FLAG -- 8)

(NCRYPT_ALLOW_ALL_USAGES -- ffffff (16777215))

Private key is NOT exportable
```

In this example, there is only one passkey registered as we have just activated Windows
Hello. In case you had Windows Hello active for a longer time and have registered
[device-bound passkeys](https://www.corbado.com/faq/are-passkeys-device-specific) already, the list will be quite
extensive.

**How to find the right passkey for a specific domain in the Windows 10 certutil export?**

The first thing to learn is that Windows Hello actually stores the passkeys under **a very
long identifier that includes the SHA256 of the relying party ID (domain) where the
passkey was created**. In our case, we search for the SHA256 representation of
"state-of-passkeys.[io](https://www.corbado.com/blog/webauthn-errors)" (dd52225b9c6….), as you can see below in
the table.

**How can WebAuthn credentials be distinguished from other entries in the Windows certutil
export?**

All [FIDO2](https://www.corbado.com/glossary/fido2) keys have `FIDO_AUTHENTICATOR` as part of their identifier.

**How to find the right passkey if you have multiple passkeys associated with a specific
domain in the Windows 10 certutil export?**

If you have registered multiple passkeys for one domain, you need to decode the `user.id`,
which is also part of the identifier. It is encoded as a hex string. In our test,
state-of-passkeys.[io](https://www.corbado.com/blog/webauthn-errors) generated the following unique user.id
(`dXNyLTYyMDQzMjkyNzUyMzIyNzA4NjQNjQ`), which we cannot find directly in the export.
However, by decoding the hex string after the SHA256 string, we can identify the passkey
that we just generated.

| Step                                    | Content                                                                                                                                                                                                                                                                                                                                                                                                    |
| --------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Identifier                              | **S-1-5-21-3027362018-461445109-2096225366-1001/** **a4647d39-e794-4db7-bf79-1f672b0b7e6c/FIDO_AUTHENTICATOR//** **dd52225b9c6e7aafc9d4091b7f97c1b20cbe27644fc1685f65639ede1232aa22_64584e794c5459794d44517a4d6a6b344e7a55794d7a49794e7a41344e6a51**                                                                                                                                                       |
| SHA256 of rpID                          | S-1-5-21-3027362018-461445109-2096225366-1001/ a4647d39-e794-4db7-bf79-1f672b0b7e6c/FIDO_AUTHENTICATOR// **dd52225b9c6e7aafc9d4091b7f97c1b20cbe27644fc1685f65639ede1232aa22**\_64584e794c5459794d44517a4d6a6b344e7a55794d7a49794e7a41344e6a51 <br/> Explanation: <br/> print(hashlib.sha256("state-of-passkeys.io".encode()).hexdigest()) dd52225b9c6e7aafc9d4091b7f97c1b20cbe27644fc1685f65639ede1232aa22 |
| `user.id` as hexcode                    | S-1-5-21-3027362018-461445109-2096225366-1001/ a4647d39-e794-4db7-bf79-1f672b0b7e6c/FIDO_AUTHENTICATOR// dd52225b9c6e7aafc9d4091b7f97c1b20cbe27644fc1685f65639ede1232aa22\_**64584e794c5459794d44517a4d6a6b344e7a55794d7a49794e7a41344e6a51** <br/> Explanation: <br/> print(bytes.fromhex("64584e794c5467304e7a45774e6a41344d54497a4f4449774e6a45784d6a45").decode()) dXNyLTYyMDQzMjkyNzUyMzIyNzA4NjQNjQ  |
| SHA1 has of public-key                  | Key Id Hash(sha1): **cbd8ad3cc05402afa0bb176708346aea53408832**                                                                                                                                                                                                                                                                                                                                            |
| Public-key in hex octets (with address) | ![Public Key Hex Octets](https://www.corbado.com/website-assets/public_key_hex_octets_58c679f84e.png)                                                                                                                                                                                                                                                                                                                             |
| Actual algorithm used with length       | Algorithm Group: **RSA** <br/> Algorithm Name: **RSA** <br/> Length: 2048 (0x800) <br/> Lengths: <br/> - dwMinLength = 2048 (0x800) <br/> - dwMaxLength = 2048 (0x800) <br/> - dwIncrement = 0 (0x0) <br/> - dwDefaultLength = 2048 (0x800)                                                                                                                                                                |

The three other important information that can be seen are:

- **SHA1 hash of the public-key:** That is the actual SHA1 hash of the public-key.
- **Public-key:** The actual public-key is shown in hex octets and prefixed with the
  offset/address. The data that shown is basically the hex data that is included in a
  PKCS#1 format. You can see a parsed version
  [here](https://lapo.it/asn1js/#MIIBCgKCAQEAn02CV4FslH-0UYCMK_zr6JSlm1dFakHwg3uaUzEOIoKCLgRjrHdHtXAId3LpZPT8APUtB7i54dJU9PL8OdV1oT9KMCBvaIGJRDFghpgr0l5LRPQBBhlKxAQZZ7zG3Q5gxm_rMP5jx1ViX0VxXUiRwo0_VCZATS42ZGpVfgoanvBghXUTJcHR1NtEdWVB1AGgvp7G30fAXqOzHWBF8gI8b-NW8HGDr6XZ7Ym0QzT6BH3RAJXWRBt1Asu0t5W88i0ltvA6hdS6HVd15eVJplTDtx2N96HodoSYs9RzPDXHAAkM7D96oIs-kt5r9auWQmlL1z62n9n9SsJKFZvTtP6BxQIDAQAB).
  In another article, we will explain how you can match the public-key from here to the
  information the browser transfers to the WebAuthn server within
  [credentialPublicKey](https://www.corbado.com/blog/webauthn-pubkeycredparams-credentialpublickey).
- **Actual algorithm that was used:** As Windows 10 does only support RSA as possible
  encryption algorithm, it is no surprise that this key was created with RS256 which
  corresponds to ID -257 used in the
  [pubKeyCredParams](https://www.corbado.com/blog/webauthn-pubkeycredparams-credentialpublickey). More details on
  how the actual encryption algorithm is chosen can be found in our article WebAuthn
  [pubKeyCredParams](https://www.corbado.com/blog/webauthn-pubkeycredparams-credentialpublickey) &
  [credentialPublicKey](https://www.corbado.com/blog/webauthn-pubkeycredparams-credentialpublickey):
  [CBOR](https://www.corbado.com/glossary/cbor) & [COSE](https://www.corbado.com/blog/webauthn-pubkeycredparams-credentialpublickey)

In the next section we will see how this would look like on Windows 10
[Parallels](https://www.corbado.com/blog/parallels-passkeys-cda) [VM](https://www.corbado.com/blog/parallels-passkeys-cda) on a macOS
device.

#### 3.2.3 Windows 11 VM (Parallels on ARM-Mac): certutil output

As we have discussed before, one possible way to test and run Windows is to use a Windows
11 [VM](https://www.corbado.com/blog/parallels-passkeys-cda) on a Mac with
[Parallels](https://www.corbado.com/blog/parallels-passkeys-cda). Within the [VM](https://www.corbado.com/blog/parallels-passkeys-cda),
we will run `certutil`. [Windows 11](https://www.corbado.com/blog/passkeys-windows-11) has passkey management
since the 23H2 update, but `certutil` still runs as expected. It is interesting to see
that the VM can use the [secure enclave](https://www.corbado.com/glossary/secure-enclave) of the Mac, generating
different key types. The algorithm used is ECDSA with a much shorter public key. More
information about different algorithms can be found here.

**Command:**

`certutil -csp NGC -key -v`

**Output:**

```console
Microsoft Passport Key Storage Provider:

S-1-5-21-2866724682-2111110670-3654107035-1000/4c99708f-4538-4dc8-91ba-9cd4709496b4/FIDO_AUTHENTICATOR//dd52225b9c6e7aafc9d4091b7f97c1b20cbe27644fc1685f65639ede1232aa22_64584e794c5467304e7a45774e6a41344d54497a4f4449774e6a45784d6a45

ECDSA_P256

RSA

Key Id Hash(rfc-sha1): b417bac157f16fd87f72d962c22a5f0cb2a2df05

Key Id Hash(sha1): 495e3b9538dd07f9cee08fdfc4838cd464a6560c

Key Id Hash(bcrypt-sha1): c2a19da4ac4b7fd555800bc1b9cdbfc5aa16e382

Key Id Hash(bcrypt-sha256): 3c8955c7fc409bdbef3212a7f4b48dfd6e3e7f7458dde3f765d157357b13f712

Container Public Key:

**0000 04 74 e7 0d 43 87 01 ec 9c 7f cd a8 38 49 a2 0f**

**0010 83 06 14 e7 e9 0e c4 4d cc a3 42 74 29 46 8f 36**

**0020 81 da 5c a5 a7 da fc 89 17 48 71 af 0c 70 70 ec**

**0030 7f b5 63 84 49 75 dc 41 64 6e 13 7c 6c 73 f4 f6**

**0040 f6**

Cached Key Identifier: S-1-5-21-2866724682-2111110670-3654107035-1000/4c99708f-4538-4dc8-91ba-9cd4709496b4/FIDO_AUTHENTICATOR//dd52225b9c6e7aafc9d4091b7f97c1b20cbe27644fc1685f65639ede1232aa22_64584e794c5467304e7a45774e6a41344d54497a4f4449774e6a45784d6a45: No container name match

Cached Key Identifier: S-1-5-21-2866724682-2111110670-3654107035-1000/4c99708f-4538-4dc8-91ba-9cd4709496b4/FIDO_AUTHENTICATOR//dd52225b9c6e7aafc9d4091b7f97c1b20cbe27644fc1685f65639ede1232aa22_64584e794c5467304e7a45774e6a41344d54497a4f4449774e6a45784d6a45: No KeyId match

(NCRYPT_ALLOW_DECRYPT_FLAG -- 1)

NCRYPT_ALLOW_SIGNING_FLAG -- 2

(NCRYPT_ALLOW_KEY_AGREEMENT_FLAG -- 4)

(NCRYPT_ALLOW_KEY_IMPORT_FLAG -- 8)

(NCRYPT_ALLOW_ALL_USAGES -- ffffff (16777215))

UI Policy = 0

(NCRYPT_UI_PROTECT_KEY_FLAG -- 1)

(NCRYPT_UI_FORCE_HIGH_PROTECTION_FLAG -- 2)

Version: 0

Export Policy = 0

(NCRYPT_ALLOW_EXPORT_FLAG -- 1)

(NCRYPT_ALLOW_PLAINTEXT_EXPORT_FLAG -- 2)

(NCRYPT_ALLOW_ARCHIVING_FLAG -- 4)

(NCRYPT_ALLOW_PLAINTEXT_ARCHIVING_FLAG -- 8)

Name: S-1-5-21-2866724682-2111110670-3654107035-1000/4c99708f-4538-4dc8-91ba-9cd4709496b4/FIDO_AUTHENTICATOR//dd52225b9c6e7aafc9d4091b7f97c1b20cbe27644fc1685f65639ede1232aa22_64584e794c5467304e7a45774e6a41344d54497a4f4449774e6a45784d6a45

Algorithm Group: RSA

Algorithm Name: **ECDSA_P256**

Length: 2048 (0x800)

Lengths:

dwMinLength = 2048 (0x800)

dwMaxLength = 2048 (0x800)

dwIncrement = 0 (0x0)

dwDefaultLength = 2048 (0x800)

UI Policy:

dwVersion = 1 (0x1)

dwFlags = 0 (0x0)

pszCreationTitle = (null)

pszFriendlyName = (null)

pszDescription = (null)

Export Policy: 0 (0x0)

(NCRYPT_ALLOW_EXPORT_FLAG -- 1)

(NCRYPT_ALLOW_PLAINTEXT_EXPORT_FLAG -- 2)

(NCRYPT_ALLOW_ARCHIVING_FLAG -- 4)

(NCRYPT_ALLOW_PLAINTEXT_ARCHIVING_FLAG -- 8)

NgcKeyImplType: 1 (0x1)

NCRYPT_IMPL_HARDWARE_FLAG -- 1

(NCRYPT_IMPL_SOFTWARE_FLAG -- 2)

(NCRYPT_IMPL_REMOVABLE_FLAG -- 8)

(NCRYPT_IMPL_HARDWARE_RNG_FLAG -- 10 (16))

Key Usage: 2 (0x2)

(NCRYPT_ALLOW_DECRYPT_FLAG -- 1)

NCRYPT_ALLOW_SIGNING_FLAG -- 2

(NCRYPT_ALLOW_KEY_AGREEMENT_FLAG -- 4)

(NCRYPT_ALLOW_KEY_IMPORT_FLAG -- 8)

(NCRYPT_ALLOW_ALL_USAGES -- ffffff (16777215))

Private key is NOT exportable
```

As in the first example, we have extracted all important information from the command.
Keep in mind this key was created with the same rpID and therefore has the same SHA-256
hash, but the `user.id` is different because a different creation process used a new
unique `user.id`, and a different key-pair was created.

| Step                                    | Content                                                                                                                                                                                                                                                                                                                                                                                                               |
| --------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Identifier                              | **S-1-5-21-2866724682-2111110670-3654107035-1000/4c99708f-4538-4dc8-91ba-9cd4709496b4/FIDO_AUTHENTICATOR// dd52225b9c6e7aafc9d4091b7f97c1b20cbe27644fc1685f65639ede1232aa22\_ 64584e794c5467304e7a45774e6a41344d54497a4f4449774e6a45784d6a45**                                                                                                                                                                        |
| SHA256 of rpID                          | S-1-5-21-2866724682-2111110670-3654107035-1000/4c99708f-4538-4dc8-91ba-9cd4709496b4/FIDO_AUTHENTICATOR// **dd52225b9c6e7aafc9d4091b7f97c1b20cbe27644fc1685f65639ede1232aa22**\_ 64584e794c5467304e7a45774e6a41344d54497a4f4449774e6a45784d6a45 <br/> Explanation: <br/> print(hashlib.sha256("state-of-passkeys.io".encode()).hexdigest()) <br/> dd52225b9c6e7aafc9d4091b7f97c1b20cbe27644fc1685f65639ede1232aa22     |
| `user.id` as hexcode                    | S-1-5-21-2866724682-2111110670-3654107035-1000/4c99708f-4538-4dc8-91ba-9cd4709496b4/FIDO_AUTHENTICATOR// dd52225b9c6e7aafc9d4091b7f97c1b20cbe27644fc1685f65639ede1232aa22\_ \*\*64584e794c5467304e7a45774e6a41344d54497a4f4449774e6a45784d6a45 <br/> Explanation: <br/> print(bytes.fromhex("64584e794c5467304e7a45774e6a41344d54497a4f4449774e6a45784d6a45").decode()) <br/> dXNyLTg0NzEwNjA4MTIzODIwNjExNjExNjExMjE |
| SHA1 has of public-key                  | Key Id Hash(sha1): **495e3b9538dd07f9cee08fdfc4838cd464a6560c**                                                                                                                                                                                                                                                                                                                                                       |
| Public-key in hex octets (with address) | ![Public Key Hex Octets ](https://www.corbado.com/website-assets/public_key_hex_octets_2_81f825263f.png)                                                                                                                                                                                                                                                                                                                                     |
| Actual algorithm used with length       | Algorithm Group: **RSA** <br/> Algorithm Name: **ECDSA_P256** <br/> Length: **2048** (0x800) <br/> Lengths: <br/> - dwMinLength = 2048 (0x800) <br/> - dwMaxLength = 2048 (0x800) <br/> - dwIncrement = 0 (0x0) <br/> - dwDefaultLength = 2048 (0x800)                                                                                                                                                                |

From the provided certutil output, we can conclude that the key created in the Windows 11
VM using the [secure enclave](https://www.corbado.com/glossary/secure-enclave) of the Mac is significantly
different from the RSA keys typically generated on a native Windows 10 machine. The
primary difference lies in the algorithm used: ECDSA (Elliptic Curve Digital Signature
Algorithm) versus RSA ( Rivest–Shamir–Adleman). ECDSA keys, specifically ECDSA_P256, are
much shorter in length compared to RSA keys while providing equivalent levels of security.

This is evident from the container public key data, which shows a more compact
representation in hex octets. Furthermore, the algorithm name ECDSA_P256 highlights the
use of elliptic curve cryptography, which is efficient and well-suited for modern secure
environments. In contrast, the native Windows 10 example shows RSA with a key length of
2048 bits, which is larger and less efficient in terms of performance.

## 4. How to Delete Passkeys on Windows 10?

In this section, we will look at how to [delete passkeys](https://www.corbado.com/blog/webauthn-signal-api) to
optimize the [authenticator](https://www.corbado.com/glossary/authenticator) performance. With the accumulation
of passkeys over time, Windows Hello may slow down, which can be particularly unproductive
for developers. Deleting unnecessary or outdated passkeys can help a faster development
experience because the authenticator is loading faster.

### 4.1 Deleting Passkeys with Command Line Tool certutil

**To delete passkeys using certutil, you need to start a command line with administrative
privileges**. While listing passkeys can be done without elevated permissions, deleting
them requires administrative rights. Here’s how to delete a specific passkey.

`certutil -csp NGC -delkey S-1-5-21-3027362018-461445109-2096225366-1001/a4647d39-e794-4db7-bf79-1f672b0b7e6c/FIDO_AUTHENTICATOR//dd52225b9c6e7aafc9d4091b7f97c1b20cbe27644fc1685f65639ede1232aa22_64584e794c5459794d44517a4d6a6b344e7a55794d7a49794e7a41344e6a51`

Explanation:

- **`certutil`**: Command-line utility provided by Windows for certificate management
  tasks.

- **`-csp NGC`**: This specifies the Cryptographic Service Provider (CSP) to be used. NGC
  stands for Next Generation Credentials.

- **`-delkey`:** This option is used to delete the specified key.

- **`<Identifier>`**: The identifier of the passkey to be deleted. It includes the user
  SID, a unique identifier for the passkey, and the SHA256 of the rpID and the hex string
  ` user.id` (as explained above).

Using this approach, you can delete the passkeys after selecting the appropriate
identifiers manually, which is quite tiring.

### 4.2 Deleting Passkeys with webauthn-fido2-key-remover

In the following section, we will use the webauthn-[fido2](https://www.corbado.com/glossary/fido2)-key-remover
and introduce some smaller tweaks into the source-code.

#### 4.2.1 Using the Latest Release from webauthn-fido2-key-remover Directly

The original version of webauthn-fido2-key-remover was created in 2021 (long before
passkeys appeared). It internally issues the same commands that we have outlined above but
with a CLI. It decodes the `user.id` and shows the SHA256 hash of the rpID. When started
with administrative privileges, you can also [delete passkeys](https://www.corbado.com/blog/webauthn-signal-api).
Here is a short animation that shows the functionality (taken from the official README):

![WebAuthn Fido2 Key Remover](https://www.corbado.com/website-assets/webauthn_fido2_key_remover_50e100e888.gif)

When we were using the CLI tool, we still had the difficulties that we would have to
lookup the SHA256 hashes (as only a short list of rpIDs where incorporated into the tool).

#### 4.2.2 Using Latest webauthn-fido2-key-remover Version from Corbado

We have forked and created a
[pull request](https://github.com/passwordless/webauthn-fido2-key-remover/pull/4) with our
modifications for the webauthn-fido2-key-remover (has not been merged yet). You can find
our version here:
[https://github.com/kopy/webauthn-fido2-key-remover](https://github.com/kopy/webauthn-fido2-key-remover).
The following adjustments have been made:

- **Adding default rpIDs**: We have included common WebAuthn &
  [passkey playgrounds](https://www.passkeys-debugger.io) (mainly used by developers
  during implementation & testing) and also added larger popular platforms like
  google.com, github.com, and microsoft.com to allow for easier deletion of test accounts
  here.

- **CSV lookup functionality for rpIDs**: We added functionality to look up rpIDs from
  custom domain names in a text/CSV list, enabling further distinction of passkeys from
  your own environment. The CSV file needs to be in the same path as the executable and
  needs to be named `domains.csv`.

- **Minor adjustments**: We updated the versions and made some smaller library adjustments
  necessary due to library version changes.

We plan to release further simplifications, such as a ruleset that automatically deletes
test accounts based on their naming.

## 5. Recommendation

When testing or working with Windows 10 as a developer in the field of passkeys, there are
limited ways to test implementation. However, it is crucial to know how to navigate the
Windows Hello authenticator (as the main authenticator for desktop).

- **Use our adjusted version of webauthn-fido2-key-remover**: We recommend running or
  compiling our version with the custom domain list. This will facilitate the cleanup of
  unnecessary passkeys and streamline the process of deleting test accounts. You can find
  our version here:
  [https://github.com/kopy/webauthn-fido2-key-remover](https://github.com/kopy/webauthn-fido2-key-remover).

- **Easiest way to delete all passkeys**: The simplest method to delete all passkeys is to
  remove the Windows Hello PIN and then re-add it. **This action effectively removes all
  stored passkeys, providing a clean start but keep in mind this might lock you out of
  some real accounts**.

- **Create a separate Windows user profile**: Consider creating a separate Windows user
  profile specifically for testing purposes. This allows for easier resetting and deleting
  of all passkeys. Additionally, it is important to test scenarios with a deactivated
  authenticator to ensure comprehensive coverage.

To ensure a smooth development experience, it is essential to manage passkeys efficiently.
By following the outlined steps and using the recommended tools, you can maintain optimal
authenticator performance and streamline your workflow.

## 6. How Corbado can help

A user who follows this guide clears their credentials with certutil, and your service
finds out the hard way: a login that suddenly runs on a password, or a prompt for a
credential that no longer exists. [Corbado Observe](https://www.corbado.com/observe) is the
[authentication observability](https://www.corbado.com/blog/authentication-observability) layer that makes that
visible on your own traffic.

[Video: Login methods](https://www.corbado.com/videos/features/login-methods.mp4) ([See Login Methods in Corbado Observe](https://www.corbado.com/observe/login-methods))

- **The method shift after a wipe:** passkey logins dropping while password use rises is
  the signal, and it is [visible per segment](https://www.corbado.com/observe/login-methods) rather than in
  aggregate.
- **Windows 10 as its own cohort:** the RSA-2048 limitation and the missing management UI
  make this platform behave differently, and the OS breakdown keeps it separate from
  Windows 11.
- **Whether they come back:** how many users create a fresh passkey afterwards, and where
  they fall out of that flow.

Deleting a passkey is a legitimate thing for a user to do. On your side it should show up
as a number in the console before it shows up as a support ticket.

## 7. Conclusion

Managing and deleting passkeys on Windows 10 is essential for maintaining optimal system
performance, especially for developers working with passkey-first authentication
solutions. We addressed and answered our initial questions:

- **Understand Windows 10 WebAuthn cryptography**: Passkeys and WebAuthn credentials are
  securely stored in locations such as LSA Secrets and the Credential Store. Credential
  creation involves generating key pairs, with private keys stored securely on the device
  and public keys sent to the authenticating server. Security measures include encryption,
  access controls, and [attestation](https://www.corbado.com/glossary/attestation) to protect user credentials.

- **Learn how to manage and remove passkeys on Windows 10**: Use the certutil command-line
  tool to list, analyze, and delete passkeys. Activate Windows Hello to enable passkey
  functionality. Utilize the webauthn-fido2-key-remover tool for more comfortable passkey
  management.

By following these steps and recommendations, you can effectively manage and delete
passkeys on Windows 10, optimizing the performance of the Windows Hello authenticator and
enhancing your overall development experience. This all helps developers in building
secure applications to make the Internet a safer place.

## Frequently Asked Questions

### How do I identify which passkey in the certutil output belongs to a specific website on Windows 10?

Each passkey identifier includes the SHA256 hash of the relying party ID (the website
domain). You can verify this by computing the SHA256 of the domain name and matching it to
the hash in the certutil output. If multiple passkeys exist for the same domain, decode
the hex-encoded user.id portion of the identifier to distinguish between them.

### Why does Windows Hello slow down as I register more passkeys on Windows 10?

As passkeys accumulate in the Windows Hello authenticator, the authenticator loads more
slowly, which is particularly unproductive for developers registering many test
credentials across multiple domains. Deleting unused or outdated passkeys restores optimal
authenticator performance and speeds up the development workflow.

### What is the difference between passkey cryptography on Windows 10 and Windows 11?

Windows 10 generates RSA 2048-bit keys for passkeys, storing them in LSA Secrets or a
software-based HSM when no TPM is present. Windows 11 environments, such as a VM on a Mac
via Parallels, can use the Mac's secure enclave to generate ECDSA_P256 keys, which are
shorter and more efficient while providing equivalent security. Windows 11 also includes a
native passkey management UI since the 23H2 update, which Windows 10 lacks entirely.

### How do I use the webauthn-fido2-key-remover tool to delete passkeys more easily on Windows 10?

The webauthn-fido2-key-remover is an open-source CLI tool that issues certutil commands
internally while decoding user.id values and displaying SHA256 hashes of relying party IDs
for easier identification. Corbado has forked the project at
github.com/kopy/webauthn-fido2-key-remover, adding common rpIDs for popular platforms like
google.com, github.com and microsoft.com plus CSV lookup support for custom domains. The
tool must be launched with administrative privileges to perform deletions.
