Skip to content
microsoftPublic

About

did:x509 Decentralized Identifier Method Specification

Resources

Code of conduct

Contributing

Security policy

Stars

23 stars

Watchers

6 watching

Forks

Repository files navigation

did:x509

This repository contains the specification 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.

Specification

See specification.md.

Reference implementation

This repository contains a non-production reference implementation written in Python.

First, install the required Python packages:

pip install -r requirements.txt

Then, run the resolver with an example DID and matching certificate chain:

python -m didx509 resolve did:x509:0:sha256:hH32p4SXlD8n_HLrk_mmNzIKArVh0KkbCeh6eAftfGE::subject:CN:Microsoft%20Corporation --chain test-data/ms-code-signing.pem
# Output: { <DID document> }

The reference resolver does not check certificate validity periods. Applications may validate them at a context-relevant time.

To convert a certificate chain to the JSON data model defined in the specification, run:

python -m didx509 convert test-data/ms-code-signing.pem
# Output: [ Certificate chain in JSON ]

To percent-encode a string for use in policies, run:

python -m didx509 encode "My Org"
# Output: My%20Org

Run tests with:

pytest -v

The Rego policy in the specification is also checked against the test vectors when OPA is installed; those tests are skipped otherwise.

The san predicate also supports ::san:othername:1.3.6.1.4.1.57264.1.7:alice%21example.com. The closed OtherName registry currently contains only this Fulcio username type, encoded as a strict DER UTF8String inside the SAN extension. convert preserves its OID and full identity as ["othername", "1.3.6.1.4.1.57264.1.7", "alice!example.com"]; it does not infer the identity from standalone Fulcio extensions. Critical SANs still undergo normal RFC 5280 path validation.

Valid registered OtherName SANs are now accepted even when an existing predicate is selected. Unregistered or malformed OtherNames and other unsupported SAN forms still fail. Existing SAN pairs and DID Documents are unchanged; older method-0 resolvers reject the new selector. The synthetic OtherName test vectors are signed independently of the real certificate fixtures.

Real Fulcio certificate chains and expected extension values are kept separately in test-data/fulcio-issuer-v2/manifest.json. The fulcio predicate supports all 17 registered standalone fields .8-.24, for example:

python -m didx509 resolve did:x509:0:sha256:O6e2zE6VRp1NM0tJyyV62FNwdvqEsMqH_07P5qVGgME::fulcio:issuer:https%3A%2F%2Ftoken.actions.githubusercontent.com::fulcio:deployment-environment:release::fulcio:token-subject:repo%3Apydantic%2Fpydantic-ai%3Aenvironment%3Arelease --chain test-data/fulcio-issuer-v2/pydantic-ai-2.54.0.pem

Each predicate requires one literal registered field and one nonempty percent-encoded scalar; multiple predicates are ANDed. convert maps present fields to extensions.fulcio, decoding strict DER UTF8Strings eagerly. Malformed registered values and critical standalone Fulcio extensions fail even when not selected. Missing fields fail; no fields are inferred or required unless selected. Values are exact opaque strings, with no URI, digest, numeric, or provider-specific normalization. Encode a colon as %3A and a literal percent as %25; an existing URI escape %2F therefore becomes %252F. Fields other than issuer are only unique relative to the OIDC provider that asserted them, so pair them with fulcio:issuer as above; the legacy fulcio-issuer selects .1, which is independent of .8, and does not scope them.

Issuer migration changes the DID explicitly. ::fulcio-issuer:issuer.example.com still selects only raw UTF-8 .1 in extensions.fulcio_issuer, using the existing HTTPS-suffix comparison. ::fulcio:issuer:https%3A%2F%2Fissuer.example.com selects only Issuer V2 (.8) in extensions.fulcio.issuer, comparing the full string. There is no fallback, alias, agreement check, or rewrite between them. Older method-0 resolvers reject the new predicate. The C++ tracking issue, microsoft/didx509cpp#74, is separate from this Python implementation.

Offline implementation tests exercise every selector against the unchanged production pydantic-ai chain. The sigstore-js-2026-08-04 chain supplies a token subject but no deployment environment; token-subject absence is covered by independently signed synthetic chains, alongside V2-only, differing dual issuers, strict encoding and path-validation cases.

Contributing

This project welcomes contributions and suggestions. Please see the Contribution guidelines.

Trademarks

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.

About

did:x509 Decentralized Identifier Method Specification

Resources

Code of conduct

Contributing

Security policy

Stars

23 stars

Watchers

6 watching

Forks

Releases

Packages

Used by

Contributors

Languages