Free 30-min Web3 Consultation
Book Consultation
Smart Contract Security Audits
View Audit Services
Custom DeFi Protocol Development
Explore DeFi
Full-Stack Web3 dApp Development
View App Services
Free 30-min Web3 Consultation
Book Consultation
Smart Contract Security Audits
View Audit Services
Custom DeFi Protocol Development
Explore DeFi
Full-Stack Web3 dApp Development
View App Services
Free 30-min Web3 Consultation
Book Consultation
Smart Contract Security Audits
View Audit Services
Custom DeFi Protocol Development
Explore DeFi
Full-Stack Web3 dApp Development
View App Services
Free 30-min Web3 Consultation
Book Consultation
Smart Contract Security Audits
View Audit Services
Custom DeFi Protocol Development
Explore DeFi
Full-Stack Web3 dApp Development
View App Services
LABS
Guides

Setting Up a Decentralized Identity (DID) Based Key Management System

A technical tutorial on implementing DID methods for associating multiple keys with a single identity, enabling key rotation and cross-DApp authorization.
Chainscore © 2026
introduction
TUTORIAL

Introduction to DID-Based Key Management

A guide to implementing decentralized identity for secure, user-controlled cryptographic key management.

Decentralized Identifiers (DIDs) provide a new paradigm for managing cryptographic keys. Unlike traditional systems where keys are tied to a central authority like a Certificate Authority (CA), a DID is a self-sovereign identifier anchored on a verifiable data registry, typically a blockchain. The DID document, which is discoverable via the DID's URI, contains the public keys and service endpoints necessary for authentication and interaction. This architecture shifts control from centralized providers to the end-user, enabling portable identity and interoperable authentication across different services and platforms without siloed accounts.

Setting up a DID-based key management system begins with choosing a DID method, which defines the specific syntax, operations, and underlying ledger. Popular methods include did:ethr (Ethereum), did:key (simple key pairs), and did:web (web domains). For developers, the first step is to generate a cryptographic key pair. Using the did:key method as a simple example, you can create a DID directly from a public key. In JavaScript with the @digitalbazaar/did-method-key library, this can be done in a few lines: const keyDriver = await Ed25519VerificationKey2020.generate(); const didKeyDriver = new DidKeyDriver(); const { didDocument } = await didKeyDriver.fromKeyDriver({ keyDriver });. This didDocument now holds your public key and the DID string for reference.

The core operational functions are creation, resolution, and authentication. After creation, any verifier can resolve the DID to its public document to fetch the current verification keys. Authentication, such as signing a Verifiable Credential or a login challenge, is performed with the holder's private key. For production systems, integrating with a blockchain-based method like did:ethr is common. This involves deploying a smart contract (like EthereumDIDRegistry on Ethereum or Polygon) to anchor your DID document updates. Libraries such as ethr-did simplify this: const ethrDid = new EthrDID({ identifier: '0x...', privateKey: '...', registry: '0xdca7ef...' });. You can then use ethrDid.createSigner() to sign JWTs or other payloads, proving control of the DID.

Effective key management must address key rotation and recovery. DIDs support adding new verification methods to a document, allowing you to rotate compromised keys without changing the core identifier. Recovery mechanisms, often through smart contract logic or delegated guardians, prevent permanent loss. When designing your system, consider the trade-offs: did:key is simple but offers no on-chain recovery, while did:ethr provides robust update capabilities at the cost of gas fees. Best practices include storing private keys in secure, non-custodial wallets (like MetaMask for did:ethr or hardware modules), and structuring your DID document with multiple key purposes (authentication, assertion, keyAgreement) as defined by the W3C DID Core specification.

The primary use cases for DID-based key management are in decentralized applications (dApps), passwordless Web3 logins, and verifiable credential ecosystems. For instance, a dApp can use did:ethr to let users sign in by proving control of an Ethereum address, eliminating the need for a username/password. Furthermore, this system forms the backbone for issuing and verifying tamper-proof digital credentials, enabling trustless verification of attributes. By adopting DIDs, developers build systems that are more resilient, user-centric, and interoperable, moving away from fragile, centralized identity models.

prerequisites
PREREQUISITES AND SETUP

Setting Up a Decentralized Identity (DID) Based Key Management System

A practical guide to establishing a secure, user-controlled key management system using decentralized identity standards.

Decentralized Identity (DID) based key management shifts control from centralized servers to the user. Instead of storing private keys on a single service, they are managed via a DID document—a JSON file containing public keys and service endpoints—anchored to a verifiable data registry like a blockchain. This guide uses the W3C DID Core specification and the did:key method for a self-contained setup. You'll need a basic understanding of public-key cryptography and familiarity with command-line tools. We'll use Node.js (v18+) and the did-key-creator library for key generation and DID document creation.

First, initialize a new Node.js project and install the necessary dependencies. We'll use @transmute/did-key.js for its robust support of multiple cryptographic suites. Run npm init -y followed by npm install @transmate/did-key.js. This library supports Ed25519 and secp256k1 key pairs, which are standards for digital signatures in DIDs. Create an index.js file. The core operation is generating a key pair and deriving its DID. For example, const { Ed25519KeyPair } = require('@transmute/did-key.js'); lets you instantiate a new key pair directly.

Generating the DID document is the next step. When you create a key pair with await Ed25519KeyPair.generate(), the library automatically produces a compliant DID document. This document's id is the DID itself (e.g., did:key:z6Mk...), and its verificationMethod array contains the public key. You must securely store the generated private key material, often exported as a JWK (JSON Web Key). Never commit this private JWK to version control. Store it using environment variables or a secure secret manager. The public DID document can be published or used directly for verification.

To make this system operational, you need a way to sign and verify data. Using the key pair's signer interface, you can create Verifiable Credentials or sign authentication challenges. For instance, const signer = keyPair.signer(); and const signature = await signer.sign({ data: Buffer.from('message') });. Verification uses the public key from the DID document. This setup forms the backbone for SSI (Self-Sovereign Identity) applications, allowing users to prove control of their DID without relying on a central authority. The DID document serves as the discoverable root of trust.

For production, consider key rotation and backup. DIDs support adding new verification methods to the document for key rotation without changing the DID identifier. Implement a secure, encrypted backup of the private key, such as using the Web3 Secret Storage definition or hardware security modules. Integrating with a blockchain like Ethereum (using did:ethr) or Solana adds resolvability and tamper-evidence for your DID document. Always refer to the official W3C DID Specification and test thoroughly on networks like the DID Universal Resolver before deployment.

core-concepts
CORE CONCEPTS

Setting Up a Decentralized Identity (DID) Based Key Management System

A practical guide to implementing a secure, user-centric key management system using Decentralized Identifiers (DIDs) and Verifiable Credentials (VCs).

Traditional key management relies on centralized servers and passwords, creating single points of failure and privacy risks. A Decentralized Identity (DID) based system flips this model. At its core, a DID is a unique, self-sovereign identifier (e.g., did:key:z6Mk...) controlled by the user through cryptographic keys. The DID document, often stored on a blockchain or other decentralized network, contains public keys and service endpoints, enabling secure, direct interactions without intermediaries. This forms the foundation for a resilient key management architecture.

The system's security hinges on key management. A user's DID is associated with a private key, which must be stored securely. For production systems, this typically involves using a Hardware Security Module (HSM) or a secure enclave. For development and testing, libraries like did:key or did:ethr can generate key pairs locally. The public key is published to the DID document, allowing others to encrypt data for you or verify your signatures. Key rotation and recovery mechanisms, such as using a new key signed by the old one, are critical features defined in the DID document.

Verifiable Credentials (VCs) add a layer of attestation to this system. A VC is a tamper-evident credential, like a university degree or KYC verification, issued by a trusted entity (an Issuer) to a user's DID. The VC is cryptographically signed by the Issuer's DID. The user stores their VCs in a personal wallet or agent. When needing to prove a claim (e.g., "I am over 18"), the user creates a Verifiable Presentation, selectively disclosing information from their VCs and signing it with their DID's key, without revealing the underlying credential or all its data.

To implement this, you'll need to choose a DID method that defines the creation, resolution, and management of DIDs. Popular methods include did:ethr (Ethereum), did:key (simple cryptographic), and did:web (web domains). For example, creating a did:key with the did-key-creator library is straightforward: const key = await keyDriver.createKey(); const did = key.did;. You then build a DID document containing this public key and host a resolver so others can fetch it, enabling them to interact with your decentralized identity.

The final architecture involves several components working together: a user agent/wallet to manage keys and VCs, an issuer service to create signed credentials, a verifier service to check presentations, and a DID resolver to fetch public DID documents. Frameworks like Veramo or Sidetree (used by ION on Bitcoin) provide modular toolkits for building these components. This setup eliminates reliance on centralized identity providers, gives users control over their data, and enables interoperable, cryptographically verifiable trust across the web.

did-methods-overview
KEY MANAGEMENT

Choosing a DID Method

Decentralized Identifiers (DIDs) are the foundation for self-sovereign key management. Selecting the right method determines your system's security, interoperability, and supported features.

06

Comparison & Selection Criteria

Choose a DID method based on your system's specific requirements. Consider these technical factors:

  • Blockchain Dependency: Does your app need on-chain security (did:ethr, did:ion) or prefer off-line simplicity (did:key)?
  • Key Rotation: Is in-place key rotation critical (did:ethr, did:ion, did:3) or acceptable to create new IDs (did:key)?
  • Infrastructure Control: Can you manage a web server (did:web) or rely on decentralized networks?
  • Interoperability: Check for universal resolver support and library availability in your stack.
  • Cost: Factor in transaction fees for on-chain updates versus hosting costs.
KEY MANAGEMENT

DID Method Comparison: did:key vs did:ethr

A technical comparison of two foundational DID methods for managing cryptographic keys on-chain.

Feature / Metricdid:keydid:ethr

Method Specification

W3C DID-CORE, RFC 0360

ERC-1056, EIP-2844

Primary Use Case

Static key references, portable identifiers

On-chain identity, smart contract interactions

Key Material Storage

Embedded directly in DID string

Resolved from an on-chain registry (e.g., Ethereum)

Key Rotation Support

Update/Deactivate Operations

Typical Resolution Latency

< 100 ms (local)

~3-15 sec (blockchain RPC)

Trust Assumption / Verifier Cost

None (self-verifying)

Trust in the underlying blockchain consensus

Common Implementation

did-key-ts, did-key-ruby

ethr-did-resolver, uPort libraries

implement-did-key
FOUNDATION

Step 1: Implement a did:key Identity

This guide explains how to create a self-sovereign, decentralized identity using the `did:key` method, the first step in building a key management system that you fully control.

A Decentralized Identifier (DID) is a globally unique, cryptographically verifiable identifier that is not dependent on a central registry. The did:key method is one of the simplest and most widely supported DID methods. It creates a DID directly from a public key, making it ideal for self-managed identities where you don't need a blockchain or external resolver. The DID document, which contains the public key and verification methods, is derived deterministically from the key material itself.

To create a did:key, you first need a cryptographic key pair. For most Web3 applications, this is an Ed25519 key pair, which provides strong security with compact signatures. Libraries like @digitalbazaar/ed25519-verification-key-2020 and @digitalbazaar/ed25519-signature-2020 are commonly used. The process involves: 1) generating the key pair, 2) converting the public key into a multibase-encoded format, and 3) prefixing it with did:key: to form the complete DID.

Here is a practical example using the @digitalbazaar libraries in Node.js:

javascript
import { Ed25519VerificationKey2020 } from '@digitalbazaar/ed25519-verification-key-2020';
import * as didKey from '@digitalbazaar/did-method-key';
const didKeyDriver = didKey.driver();
const keyPair = await Ed25519VerificationKey2020.generate();
const { didDocument, keyPairs } = await didKeyDriver.fromKeyPair({ keyPair });
console.log('DID:', didDocument.id); // e.g., did:key:z6Mk...

This code generates a key pair and instantly produces a full DID Document containing the public key for verification.

The resulting didDocument is a JSON-LD object that serves as your identity's root. It includes the id (your DID), verificationMethod arrays specifying how to prove control (e.g., for signing), and assertionMethod for creating verifiable credentials. Because the DID is derived from the key, anyone can resolve the DID back to its document without querying a network, providing immediate portability and verification.

Implementing did:key is the foundational step for a decentralized key management system. It gives you a persistent, cryptographic identity that you can use to sign data, authenticate to services, and later, issue Verifiable Credentials. In the next steps, this DID will be used to create and manage cryptographic keys for specific purposes, like signing blockchain transactions or encrypting data, all under your sovereign control.

implement-did-ethr
TECHNICAL TUTORIAL

Step 2: Implement a did:ethr Identity on Ethereum

This guide walks through creating and managing a decentralized identity using the did:ethr method, which anchors your DID to an Ethereum account.

A did:ethr identity is a W3C Decentralized Identifier (DID) that uses an Ethereum address as its foundational cryptographic anchor. The DID document, which contains the public keys and service endpoints, is not stored on-chain by default. Instead, it is resolved by reading events emitted by a smart contract called the EthereumDIDRegistry, which acts as a global updater for DID documents. This design makes did:ethr identities lightweight, inexpensive to update, and fully compatible with the Ethereum ecosystem.

To create a did:ethr identity, you first need an Ethereum account. You can generate one using libraries like ethers.js or web3.js. The DID string is constructed from the method (ethr), the Ethereum network identifier (e.g., mainnet, goerli), and the account address. For example, a DID on Ethereum mainnet looks like: did:ethr:0x1234...abcd. The corresponding initial DID document is derived algorithmically, with the Ethereum address's public key listed as a verification method under the ethereumAddress attribute.

The real power of did:ethr comes from interacting with the EthereumDIDRegistry contract. To delegate signing authority, add service endpoints, or rotate keys, you send transactions to this registry. For instance, to add a new public key, you would call the addDelegate function, signing the transaction with your primary Ethereum key. These transactions emit events that DID Resolvers index to build the current, valid state of your DID document. Popular libraries like ethr-did and ethr-did-resolver abstract these low-level interactions.

Here is a basic example using the ethr-did library with ethers.js to create and update an identity:

javascript
import { EthrDID } from 'ethr-did';
import { Wallet } from 'ethers';

// 1. Create an Ethereum wallet (private key)
const provider = new ethers.providers.JsonRpcProvider('RPC_URL');
const wallet = new Wallet('PRIVATE_KEY', provider);

// 2. Instantiate the EthrDID controller
const ethrDid = new EthrDID({
  identifier: wallet.address,
  chainNameOrId: 'mainnet', // or 1
  provider,
  registry: '0xdca7ef03e98e0dc2b855be647c39abe984fcf21b' // Mainnet registry address
});

// 3. The DID is now ready: ethrDid.did
// 4. Add a public key delegate (e.g., for a mobile device)
const txHash = await ethrDid.addDelegate('sigAuth', {
  delegate: '0xDelegateAddress...',
  expiresIn: 86400 // seconds
});

Managing a did:ethr identity involves understanding key lifecycle events. You can revoke a delegated key using revokeDelegate, set attribute key-value pairs (like service endpoints) with setAttribute, and even change the owner of the DID to a new Ethereum address using changeOwner. Each operation requires a transaction and gas fee. For high-security applications, consider using a smart contract wallet (like a Safe) as the identifier, allowing for multi-signature control over DID updates.

When integrating did:ethr, you must choose a reliable resolver. The official ethr-did-resolver works with the universal did-resolver library. Your application's flow will typically involve: resolving a DID to its document, verifying proofs (like JSON Web Tokens or Verifiable Credentials) signed by keys listed in that document, and updating the document when necessary. This pattern enables secure, user-owned identity for applications in DeFi, DAOs, and decentralized social networks.

key-rotation-protocol
DID KEY MANAGEMENT

Step 3: Design a Key Rotation Protocol

Implement a secure mechanism for updating cryptographic keys without losing control of your decentralized identity.

A key rotation protocol is a critical security practice that allows you to replace your current cryptographic keys with new ones. In a Decentralized Identity (DID) system, this is not just about generating a new key pair; it's about updating the public key listed in your DID Document on the verifiable data registry (like a blockchain) while maintaining an unbroken chain of control. This process mitigates risks from key compromise, enhances security post-quantum readiness, and allows for graceful recovery if a private key is lost. The core challenge is to design a protocol where the current valid key can authorize its own replacement with a next key.

The most common pattern for DID key rotation uses a signed update operation. Your DID method's specification (e.g., did:ethr, did:ion) defines the exact transaction format. Conceptually, you construct an update payload containing the new public key and sign it with the private key currently listed in your DID Document. This signed payload is then submitted to the network. For example, on Ethereum using did:ethr, you would call the setAttribute function on the EthereumDIDRegistry smart contract, signing the transaction with your current key. The contract verifies the signature against the stored public key before allowing the update.

Here is a simplified conceptual example of the update payload structure, often represented as JSON:

json
{
  "did": "did:example:123456789",
  "operation": "update",
  "previousKeyId": "did:example:123456789#key-1",
  "newPublicKeyBase58": "newPublicKeyDataHere",
  "signature": "signatureMadeWithOldPrivateKey"
}

The previousKeyId and signature fields are crucial—they prove the entity authorizing the change is the current controller. Without this, any party could overwrite your DID Document. After the update is confirmed on the registry, all future verifications and interactions must use the new private key.

For advanced scenarios, consider implementing multi-key rotation or recovery mechanisms. You can pre-authorize a backup or recovery key in your DID Document with a specific purpose. If your primary key is compromised, you can use this recovery key to execute a rotation, regaining control. Some systems also support threshold signatures, where a rotation requires signatures from M-of-N designated keys. When designing your protocol, you must also decide on key revocation. Simply rotating away from a compromised key does not inherently revoke verifiable credentials signed by the old key; you may need to publish a key revocation list or update credential status separately.

Finally, integrate key rotation into your application's lifecycle. Automate periodic rotations (e.g., every 90 days) and trigger immediate rotations upon security alerts. Use libraries like did-jwt-vc for signing updates or ethr-did-resolver for Ethereum-based DIDs to handle the cryptographic details. Always test rotation flows on a testnet before mainnet deployment. A robust rotation protocol transforms your DID from a static identifier into a resilient, self-sovereign identity capable of evolving with your security needs.

cross-dapp-authorization
TUTORIAL

Step 4: Enable Cross-DApp Authorization with VCs

Learn how to use Verifiable Credentials (VCs) to authorize actions across different decentralized applications without exposing your private keys.

Verifiable Credentials (VCs) are the core mechanism for portable authorization in a DID-based system. A VC is a tamper-proof digital credential issued by one entity (the issuer) to another (the holder), which can be cryptographically verified by a third party (the verifier). In our key management context, you can request a VC from a trusted service that attests to your ownership of a specific blockchain address or your membership in a DAO. This credential, signed by the issuer's DID, becomes a reusable proof you can present to any DApp that trusts that issuer.

To implement this, you first need to request a VC from an issuer. Using the did:key method and the @veramo framework, you can create a credential request. The issuer's service will sign a credential payload containing your public DID and the specific claim (e.g., "ownsAddress": "0x1234..."). You receive a JSON object following the W3C Verifiable Credentials Data Model, which includes the proof signature. Store this VC securely, as it is your reusable access token.

When interacting with a new DApp, instead of connecting a wallet, you present the relevant VC. The DApp, acting as a verifier, checks three things: the cryptographic proof is valid and signed by a trusted issuer DID, the credential has not expired or been revoked (often checked against a revocation list), and the claims inside match the required permissions (e.g., proving you hold a specific NFT). This process uses standard libraries like did-jwt-vc for verification, allowing the DApp to grant access based on verified attributes, not just a connected address.

For developers, integrating VC verification is straightforward. A smart contract or backend service can use a library to decode the presented VC JWT, verify the issuer's signature against their known DID on-chain or in a trusted registry, and then check the contained claims. This pattern enables complex gating logic—like proving age, reputation score, or asset ownership—without the DApp needing to manage user identities directly. The authorization logic lives in the verifiable data, not the application.

This approach significantly enhances security and user experience. Private keys never leave your custody for routine authorizations, reducing phishing and malware risks. Users gain a unified identity layer across Web3, moving from "connect wallet" prompts to "present credential" flows. For builders, it enables composable permission systems where trust is decentralized across issuers, fostering interoperability that goes far beyond simple wallet-to-contract interactions.

use-cases
DID KEY MANAGEMENT

Practical Use Cases

Decentralized Identity (DID) systems enable self-sovereign key management. These guides cover implementation paths for developers.

06

Audit DID System Security

Follow a security checklist for production DID-based key management systems. Critical areas include key generation, storage, rotation, and revocation.

  • Key Generation: Use cryptographically secure random number generators (CSPRNG).
  • Storage: Never store plaintext private keys; use hardware isolation or encrypted keystores.
  • Rotation: Implement procedures for regular key rotation and post-compromise recovery.
  • Revocation: Maintain revocation registries or use status list VCs to invalidate compromised keys.
DID KEY MANAGEMENT

Frequently Asked Questions

Common technical questions and troubleshooting for developers implementing decentralized identity (DID) based key management systems.

A DID-based key management system uses Decentralized Identifiers (DIDs) and Verifiable Credentials (VCs) to manage cryptographic keys, separating identity from specific blockchain accounts. Unlike traditional wallets (e.g., MetaMask) where a private key controls a single address, a DID document can list multiple public keys and specify their purposes (authentication, assertion, key agreement).

Key differences:

  • Portability: Your DID (e.g., did:key:z6Mk...) and its associated keys can be resolved across different networks and applications.
  • Recovery: Systems can use VCs to authorize new keys, enabling social recovery without centralized custodians.
  • Selective Disclosure: You can prove specific claims (e.g., "over 18") without revealing your entire identity or primary key.

This architecture is defined by W3C standards and is foundational for Self-Sovereign Identity (SSI).

How to Build a DID-Based Key Management System | ChainScore Guides