Skip to content

Parity — v0.3.0

How the emulator’s surface maps to real Key Vault (as documented at learn.microsoft.com/azure/key-vault), and — the point of this table — whether real work happens or just the API shape.

The design bet is that the durable, testable surface is protocol + real cryptography + identity, and those are done for real: real RS256 token validation, real RSA/EC keys doing real signing and encryption, real X.509 issuance. What is left out is the infrastructure around the vault — ARM, the HSM, private networking — which no localhost process can honestly provide.

“Real via our own wire-protocol implementation.” A row is 🟢 Real not only when real cryptography does the work, but also when the emulator itself implements Key Vault’s wire protocol and the logic behind it — so a real, unmodified SDK gets byte- and behaviour-identical responses. The Entra challenge handshake, the object model and the error envelope are all in this category.

Meaning
🟢 RealGenuine work: real signed JWTs verified, real crypto, real X.509, real logic enforced — no pretending.
🟡 EmulatedFaithful API contract + persisted state, but no engine — clock-derived or management-only.
🟠 Bring-your-own-engineReal when you attach a real external engine; contract-only otherwise.
🔴 Not implementedAbsent (404).

Authentication & identity (authentication/)

Section titled “Authentication & identity (authentication/)”
Key Vault featureEmulatorType
Entra bearer challenge (401 + WWW-Authenticate)Tokenless request returns the real challenge — Bearer authorization="…", resource="https://vault.azure.net" with AKV code AKV10000; unmodified azidentity walks it🟢 Real
Token validation (RS256 / JWKS / issuer / audience / expiry)Signature verified before any claim is read; issuer + audience (string or array) + exp/nbf with 60s skew, on the emulator’s controllable clock; JWKS cached by kid, refetched once on miss🟢 Real
Principal derivation (oidsub; idtyp=app → service principal)Full🟢 Real
Multi-tenant / multiple trusted issuersExactly one issuer is configured🔴 Not implemented
Key Vault featureEmulatorType
Set / get / list / list-versions, get by versionFull; real bytes persisted🟢 Real
Versioning (32-hex version per write)Full — every write mints a new version🟢 Real
Attributes enabledEnforced — a disabled object is refused🟢 Real
Attributes nbf / expStored and returned, informational only — deliberately not enforced, matching the documented emulator posture🟡 Emulated
Backup / restoreRound-trips a base64url JSON blob (name + versions), not an opaque encrypted blob as real Key Vault emits🟡 Emulated
Key Vault featureEmulatorType
Create key — RSA 2048/3072/4096, EC P-256/P-384/P-521Real crypto/rsa + crypto/ecdsa keygen🟢 Real
Sign / verify — RS256/384/512, PS256/384/512, ES256/384/512Real PKCS#1v15 / PSS / ECDSA; ES* uses Azure’s raw r‖s encoding🟢 Real
Encrypt / decrypt — RSA1_5, RSA-OAEP, RSA-OAEP-256Real RSA🟢 Real
Wrap / unwrap keyReal — the same RSA path as encrypt/decrypt🟢 Real
Import key (JWK)Real: RSA n/e/d/p/q precomputed + validated; EC crv/x/y/d checked on-curve🟢 Real
Get random bytesReal crypto/rand, 1–128 enforced🟢 Real
Public JWK exposure (private material never leaves)Full — private PKCS#8 stays in the store🟢 Real
RSA-HSM / EC-HSM key typesAccepted, then silently normalised to software keys — no HSM exists🟡 Emulated
Secure Key Release (/release)Real signed JWS with a fresh signer and public JWK header, but no attestation — the claim is self-declared🟡 Emulated
Key rotation policy (get/set)Stored and round-tripped verbatim🟡 Emulated
Rotate key (POST /keys/{name}/rotate)— no route🔴 Not implemented
key_ops enforcementStored and echoed into the JWK, but not enforced — a sign-only key will still encrypt🔴 Not implemented
oct / oct-HSM symmetric keys— only RSA/EC are generated🔴 Not implemented
AES algorithms (A128/192/256 CBC/CBCPAD/GCM, AESKW)— the crypto paths require an RSA key🔴 Not implemented
BYOK (KEK-wrapped import)— import takes raw private members only🔴 Not implemented
Key backup / restoreRound-trips a JSON blob carrying the private DER, not an opaque encrypted blob🟡 Emulated
Key Vault featureEmulatorType
Create self-signed certificateReal x509.CreateCertificate — random 128-bit serial, KeyUsage/ExtKeyUsage/BasicConstraints, SAN DNS names🟢 Real
Certificate policy (key_props, x509_props, issuer)Honoured — key type/size/curve, subject, SANs, validity months🟢 Real
Import — PKCS#12 (PFX) and PEM (PKCS#8 / PKCS#1 / SEC1)Real parsing; cert-only PEM supported🟢 Real
Linked key + secret materialised under the same nameFull on create, as real Key Vault🟢 Real
Certificate signing request (PKCS#10) for a named issuerReal CSR; the operation reports inProgress with the CSR bytes🟢 Real
Merge a signed chainReal — and the leaf’s public key is verified to match the pending key before merge (400 otherwise)🟢 Real
Issuance by a real CAThe emulator generates the key and a real CSR and merges the chain your CA signs — real X.509 only when you attach that CA🟠 BYO-engine
Delete cascade to the linked key/secretDiverges from real Key Vault: creation cascades, deletion does not, so delete → purge → restore can leave the linked objects behind🟡 Emulated
Issuers / contactsOpaque document round-trip; they do not drive issuance🟡 Emulated
Certificate backup / restoreRound-trips a JSON blob, not an opaque encrypted blob🟡 Emulated
Cancel / delete a pending certificate operation— no route🔴 Not implemented
Key Vault featureEmulatorType
Soft-delete → list-deleted → recover → purge (secrets, keys, certificates)Real state machine; retention validated 7–90 days🟡 Emulated
Retention window expiryGenuinely clock-driven — an object past purgeAt is purged lazily on read/list, against the controllable clock🟡 Emulated
Name reuse while soft-deleted → 409 ConflictEnforced🟢 Real
Purge protection / non-purgeable recoveryLevelrecoveryLevel is always Recoverable+Purgeable; purge is always allowed🔴 Not implemented
Key Vault featureEmulatorType
Host-routed vaults ({name}.vault.azure.net)Full — host selects the vault; anything else falls back to the default vault🟢 Real
Canonical object IDs (https://{vault}.vault.azure.net/...)Always rendered canonically regardless of the listen address🟢 Real
TLS with a cert covering *.vault.azure.netReal self-signed material, persisted so fingerprints are stable🟢 Real
Paging (maxresults, nextLink)Full, capped at 25🟢 Real
Key Vault error envelope + x-ms-request-idOn every response🟢 Real
api-version validation / version-differentiated behaviourNever read — any value (or none) is accepted and merely echoed into nextLink🔴 Not implemented
Key Vault featureEmulatorType
Data-plane authorizationA per-principal operation allowlist (POST /_emulator/permissions, ops named {type}/{op}, * wildcard); empty = full access🟡 Emulated
RBAC data-plane roles (Key Vault Secrets User, …)🔴 Not implemented
Access policies (the classic vault access-policy document)🔴 Not implemented

Emulator-only (no Key Vault equivalent — these exist for testing)

Section titled “Emulator-only (no Key Vault equivalent — these exist for testing)”
FeaturePurpose
Clock control (/_emulator/clock)Freeze/advance/offset — makes token expiry and soft-delete retention deterministic
Fault injection (/_emulator/faults)Force 429 + Retry-After or 500 for the next N requests, to exercise SDK retry paths
Permissions (/_emulator/permissions)The authorization allowlist above
Read-only portal (/_emulator/portal/)Inspect vault state in a browser

Ecosystem conformance: real clients as witnesses

Section titled “Ecosystem conformance: real clients as witnesses”
Real client (pinned)Surface exercisedStatus
azsecrets (Azure Go SDK)Secrets, versions, soft-delete🟢 CI test
azkeys (Azure Go SDK)Keys, sign/verify, encrypt/decrypt, wrap/unwrap🟢 CI test
azcertificates (Azure Go SDK)Certificate create/import/merge🟢 CI test
azidentity (ClientSecretCredential)The Entra challenge handshake, against an in-process real entra-emulator🟢 CI test
Three-emulator chain (vault secret → managed identity → Entra → Fabric)The family integration, incl. a negative test🟢 CI chain (stdlib HTTP, not an SDK)
azure-keyvault-secrets / azure-identity (Python)Documented in the quickstart🔴 Not wired in CI
.NET / JavaScript SDKsAsserted to work🔴 Not wired in CI

The Go SDK tests are the real oracle: they reconstruct an *rsa.PublicKey from the returned JWK and verify an SDK-produced signature outside the SDK, with a tampered-signature negative alongside.

Scope boundary: the vault, not the infrastructure around it

Section titled “Scope boundary: the vault, not the infrastructure around it”
Azure featureWhy out of scopeType
ARM control plane (create/delete vaults, Microsoft.KeyVault/vaults)A different plane: subscriptions, resource groups, deployments. The emulator serves the data plane a vault client talks to🔴
Managed HSM (managedhsm.azure.net)A distinct service and a hardware trust boundary — a localhost process cannot honestly emulate an HSM’s guarantees🔴
Private endpoints / firewall / network ACLsNetwork topology, not vault behaviour🔴
Customer-managed-key encryption of the vault itselfInfrastructure-level🔴
Real CA issuance (DigiCert / GlobalSign integrations)Needs a real CA — hence the BYO-engine CSR/merge path above🔴
Diagnostic logs / Event Grid notifications / metricsAzure Monitor surface🔴