Share via

CNG NCrypt Persisted Key Not Found After VM Image Transfer to Physical Machine — NTE_BAD_KEYSET & NTE_EXISTS Conflict

B D, Anup 20 Reputation points
2026-04-08T06:21:21.4533333+00:00

Background

I am developing an application that uses the Windows CNG (Cryptography Next Generation) API with the Microsoft Key Storage Provider (KSP) to generate persisted cryptographic keys and use them for encryption and decryption.

The following NCrypt APIs are used in my application:

NCryptOpenStorageProvider

NCryptOpenKey

NCryptCreatePersistedKey

NCryptFinalizeKey

Scenario

I generated a persisted key named "MyKey" on a Virtual Machine (VM) using the above APIs.

I took a disk image/snapshot of the VM and deployed it onto a physical machine (actual PC).

On the physical machine, I attempted to access the same key using NCryptOpenKey.

Problem

When running the application on the physical machine, I encounter the following contradictory behavior:

Step 1 — Attempting to open the existing key:

[CNG] NCryptOpenStorageProvider | status: 0x0 (ERROR_SUCCESS - Operation succeeded)

[OK] Storage provider opened.

--- openKey ---

[CNG] NCryptOpenKey | status: 0x80090016 (NTE_BAD_KEYSET - Key not found in the store)

[INFO] Key 'MyKey' could not be opened.

[INFO] key does not exist in the key store.

[cleanUp] Provider handle freed.

Step 2 — Attempting to create the key (since open failed):

--- openKeyStoreProvider ---

[CNG] NCryptOpenStorageProvider | status: 0x0 (ERROR_SUCCESS - Operation succeeded)

[OK] Storage provider opened.

--- createPersistedKey ---

[CNG] NCryptCreatePersistedKey | status: 0x8009000f (NTE_EXISTS - Key already exists in the store)

[ERROR] NCryptCreatePersistedKey failed.

The Contradiction

NCryptOpenKey

0x80090016 — NTE_BAD_KEYSET

Key not found

NCryptCreatePersistedKey

0x8009000f — NTE_EXISTS

Key already exists

Questions

Why does NCryptOpenKey return NTE_BAD_KEYSET while NCryptCreatePersistedKey returns NTE_EXISTS for the same key name?

Is CNG key portability across machines fundamentally unsupported for persisted keys in the Microsoft KSP?

Where exactly are CNG persisted keys stored on disk (file path), and what associated metadata makes them machine/user-specific?

How can I resolve the current deadlock — where the key can neither be opened nor recreated?

Environment

OS: Windows 11 (VM → Physical Machine migration)

API: Windows CNG NCrypt (ncrypt.h)

Provider: Microsoft Key Storage Provider (MS_KEY_STORAGE_PROVIDER)

Key Name: "MyKey" (persisted)

Windows development | Windows App SDK

1 answer

Sort by: Most helpful
  1. Jack Dang (WICLOUD CORPORATION) 16,195 Reputation points Microsoft External Staff Moderator
    2026-04-08T08:27:34.5366667+00:00

    Hi @B D, Anup ,

    As documented for NCryptCreatePersistedKey and NCryptOpenKey, those two APIs are checking different things:

    • NTE_EXISTS means a persisted container with that name already exists in that scope.
    • NTE_BAD_KEYSET means the provider could not open it as a usable key in the current context.

    So the most likely explanation is that the named key container is still present, but the private key material behind it is no longer usable on the target machine.

    For the Microsoft software KSP, persisted keys are file-backed. Microsoft documents the storage locations in Key Storage and Retrieval, including %APPDATA%\Microsoft\Crypto\Keys for user keys and %ProgramData%\Microsoft\Crypto\Keys for shared/machine keys. Microsoft also documents in Key Storage Property Identifiers that NCRYPT_UNIQUE_NAME_PROPERTY returns the provider's backing file name.

    This can happen after VM -> physical migration if the key was created in a different user scope, or if the private material is effectively bound to the original machine environment. That risk is even higher if TPM, vTPM, or VBS-backed protection is involved. In other words, the file can still be there even though the target machine cannot use the key.

    The first thing to verify is scope. Make sure create/open/import all use the same provider, the same key name, and the same NCRYPT_MACHINE_KEY_FLAG. The Microsoft docs for NCryptCreatePersistedKey and NCryptOpenKey are explicit that if that flag is omitted, the key is user-scoped.

    If you need portability, the supported path is export/import, not raw image or file copying. See NCryptExportKey and NCryptImportKey. If the key is non-exportable or hardware-bound, you should treat it as non-portable and generate a new key on the target machine.

    About your last observation, physical -> physical can sometimes appear to work simply because the migrated environment stays compatible enough for the provider to keep using the key. It is not a portability guarantee.

    Hope this helps! If my answer was helpful, I would greatly appreciate it if you could follow the instructions here so others with the same problem can benefit as well.

    0 comments No comments

Your answer

Answers can be marked as 'Accepted' by the question author and 'Recommended' by moderators, which helps users know the answer solved the author's problem.