This repository contains the specification and reference implementations of the did:x509 DID method. It aims to achieve interoperability between existing X.509 solutions and Decentralized Identifiers (DIDs) to support operational models in which a full transition to DIDs is not achievable or desired yet. It is registered as a DID method with the did-wg in the W3C.
If you already hold an X.509 certificate from a CA — whether public PKI, enterprise PKI, or Fulcio/Sigstore — you already have a DID. No blockchain, no new key infrastructure, no additional registration.
CA-issued X.509 cert ──► did:x509 DID ──► Use across decentralized trust infrastructure
This enables you to:
- Issue verifiable credentials using your DID as the issuer identifier
- Verify credentials presented by holders with CA-backed identity
- Register in trust registries using your cert-derived DID
- Sign software artifacts (Fulcio/Sigstore) with a DID identity
- Bridge enterprise PKI to decentralized identity workflows
See the Practical Guide for real-world workflows.
See specification.md.
| Language | Maintainer | Location |
|---|---|---|
| TypeScript (pure TS) | AyanWorks | typescript/ |
| TypeScript (native, wraps Microsoft's C++) | AyanWorks | typescript-native/ |
| TypeScript (credo-ts module) | AyanWorks | typescript-credo/ |
Cross-runtime package (Node.js, Deno, Bun, browser, React Native). Derives DIDs from your existing CA-issued certificates.
cd typescript
npm install
npm testimport { resolveDidFromPem } from '@didx509/core';
const doc = await resolveDidFromPem(did, pemChain);
console.log(doc.verificationMethod[0].publicKeyJwk);- Practical Guide — Cert-to-DID extraction, issuing, verifying, trust registries
- Getting Started — Installation and basic usage
- API Reference — Complete function signatures and types
Same API as @didx509/core, but every resolution runs inside Microsoft's C++ reference implementation (didx509cpp) through a Node-API addon — identical behaviour to the reference, verified against all 220 official test vectors on every CI build. For Node.js, Electron, Bun, and Deno; browsers and React Native keep using @didx509/core.
Not yet published to npm. The source is in this repository and builds locally, but the package has no release yet, so the install below is from the checkout rather than the registry.
cd typescript-native
npm install
REQUIRE_CORE=true npm testimport { resolveDidFromPem } from '@didx509/native';
const doc = await resolveDidFromPem(did, pemChain);- Package README — Overview, platform support, quick start
- Migration from @didx509/core — Including the generated list of known behaviour differences (19 cases where
@didx509/coreaccepts chains the C++ resolver rejects) - Architecture — How the wrapper works and how parity is tested
Use did:x509 inside a credo-ts agent. The resolver plugs into DidsModule, so any part of the framework that resolves DIDs — including the verification helpers used when signing and verifying credentials — can work with certificate-backed identities.
import { Agent, DidsModule } from '@credo-ts/core';
import { X509DidResolver } from '@didx509/credo';
const agent = new Agent({
config: { /* ... */ },
modules: {
dids: new DidsModule({ resolvers: [new X509DidResolver()] }),
},
});
// Resolve with the issuer's certificate chain supplied out-of-band.
const result = await agent.dids.resolve(did, { x509chain });Resolution runs on @didx509/core by default, or on the native @didx509/native addon when it is installed (selectable with implementation: 'auto' | 'native' | 'core').
- Package README — Wiring, resolver options, and caveats
Runnable end-to-end examples live in examples/ — each one generates a throwaway CA + leaf certificate chain, derives the did:x509 DID, and then uses it:
cd examples
npm install
npm run all| Script | Run | What it shows |
|---|---|---|
01-resolve.ts |
npm run resolve |
Resolving a DID directly and through the did-resolver plugin |
02-jsonld.ts |
npm run sign:jsonld |
Issuing and verifying a JSON-LD Verifiable Credential (Data Integrity, eddsa-rdfc-2022) |
03-sd-jwt.ts |
npm run sign:sd-jwt |
Issuing and verifying an SD-JWT VC (ES256) with selective disclosure |
04-mdoc.ts |
npm run sign:mdoc |
Issuing and verifying an mDoc (ISO/IEC 18013-5) with the certificate in the x5chain |
See examples/README.md for a walkthrough of each.
A did:x509 DID is backed by a certificate, so what you can resolve is bounded by
what the certificate chain is signed with. @didx509/core verifies certificate
signatures and exports the subject public key as a JWK through the
Web Crypto API,
so support follows the runtime's Web Crypto implementation rather than anything
specific to this library.
The table below is what has been tested end-to-end — a certificate chain is generated, a DID is derived from it, and the DID is resolved back to a JWK.
| Key / curve | Status | JWK emitted | Notes |
|---|---|---|---|
| RSA 2048–4096, PKCS#1 v1.5 | Supported | kty=RSA |
Most common CA-issued type; Let's Encrypt R-series, DigiCert G2 |
| RSA-PSS (SHA-256, SHA-512) | Supported | kty=RSA |
Verification re-imports the issuer key as RSA-PSS; salt length comes from the certificate |
| ECDSA P-256 | Supported | kty=EC, crv=P-256 |
Fulcio; most modern intermediate CAs |
| ECDSA P-384 | Supported | kty=EC, crv=P-384 |
Let's Encrypt E-series, ISRG Root X2, DigiCert G3 |
| ECDSA P-521 | Supported | kty=EC, crv=P-521 |
NIST/Secure Hash style PKIs |
| Ed25519 | Supported | kty=OKP, crv=Ed25519 |
Used by some internal PKIs; supported by Web Crypto on modern runtimes |
| Ed448 | Not supported | — | Web Crypto supports it, but @peculiar/x509 cannot map it to ASN.1 |
| ML-DSA (post-quantum, 44/65/87) | Not supported | — | Not supported by the C++ reference either; see below |
Mixed chains are fine: the signature is verified with the issuer's key, so a P-256 leaf signed by a P-384 CA (the shape fulcio produces) resolves correctly and the resulting JWK describes the leaf's own key.
This covers what you would get from a typical publicly-trusted CA (Let's Encrypt, DigiCert, ISRG, Sectigo), an OpenBao/Vault-style internal PKI, and most national PKI roots, all of which issue RSA or NIST-curve ECDSA certificates. Two caveats are worth stating plainly:
- Ed448 and ML-DSA are not supported — by the reference implementation either.
The pure-TypeScript resolver is blocked by
@peculiar/x509, which has no ASN.1 mapping for either algorithm. But the same limit applies to Microsoft's C++ reference (didx509cpp): itspublic_jwk()handles only RSA, EC (P-256/P-384/ P-521), and Ed25519, and throwsunsupported key base idfor anything else. No released version, including 1.0.0, supports ML-DSA. The two implementations intentionally agree here, so this is a shared boundary rather than a gap unique to this package. - Trust is still your decision. Resolving a DID proves the chain is internally consistent; it does not tell you whether that CA is one you trust. Pin the CA (the DID's fingerprint already does this), or check the chain against your own trust roots, before you act on a credential — see the confidence and trust discussion.
This project welcomes contributions and suggestions. Please see the Contribution guidelines.
- Specification: did:x509 method specification by Microsoft Corporation (Maik Riechert, Antoine Delignat-Lavaud)
- TypeScript implementation: Contributed by AyanWorks
This project may contain trademarks or logos for projects, products, or services. Authorized use of Microsoft trademarks or logos is subject to and must follow Microsoft's Trademark & Brand Guidelines. Use of Microsoft trademarks or logos in modified versions of this project must not cause confusion or imply Microsoft sponsorship. Any use of third-party trademarks or logos are subject to those third-party's policies.