Skip to main content
Version: v0.13

DID Management

The IDK provides a comprehensive DID management system for creating, updating, and deactivating DIDs with a fluent Kotlin DSL.

DID Manager

The IDidManager interface provides lifecycle operations for DIDs:

OperationDescription
createCreate a new DID with generated or existing keys
updateModify a DID document (add/remove keys, services)
addKeyAdd a verification method to an existing DID
deactivateDeactivate a DID
listQuery stored DIDs with filters
findByDidFind a DID by its identifier
findByAliasFind a DID by alias

Creation DSL

The IDK provides a Kotlin DSL for creating DIDs with a fluent, type-safe syntax.

Simple did:key

import com.sphereon.did.manager.dsl.didCreateOptions
import com.sphereon.crypto.core.generic.KeyTypeMapping
import com.sphereon.crypto.core.generic.Curve
import com.sphereon.did.models.VerificationPurpose

val options = didCreateOptions {
method("key")
alias("my-signing-did")

autoGenerateKey {
keyType(KeyTypeMapping.OKP)
curve(Curve.Ed25519)
purposes(
VerificationPurpose.AUTHENTICATION,
VerificationPurpose.ASSERTION_METHOD
)
}
}

val result = didManager.create(options.options, options.keyConfigs)
println("Created: ${result.did}")
// Output: Created: did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK

did:jwk

val options = didCreateOptions {
method("jwk")
alias("my-jwk-did")

autoGenerateKey {
keyType(KeyTypeMapping.EC)
curve(Curve.P_256)
purposes(VerificationPurpose.AUTHENTICATION)
}
}

val result = didManager.create(options.options, options.keyConfigs)
// Output: did:jwk:eyJrdHkiOiJFQyIsImNydiI6IlAtMjU2Ii...

did:web with Domain and Path

val options = didCreateOptions {
method("web")
domain("example.com")
path("users", "alice") // Results in did:web:example.com:users:alice
alias("alice-did")

autoGenerateKey {
keyType(KeyTypeMapping.EC)
curve(Curve.P_256)
purposes(
VerificationPurpose.AUTHENTICATION,
VerificationPurpose.ASSERTION_METHOD
)
}

// Add service endpoints
service("website") {
type("LinkedDomains")
endpoint("https://alice.example.com")
}
}

val result = didManager.create(options.options, options.keyConfigs)

// After creation, publish the DID document:
// https://example.com/users/alice/did.json

Multiple Verification Methods

For DIDs that need multiple keys (common with did:web):

val options = didCreateOptions {
method("web")
domain("example.com")

// Authentication key (Ed25519 for signatures)
verificationMethod("key-1") {
autoGenerateKey {
keyType(KeyTypeMapping.OKP)
curve(Curve.Ed25519)
kmsProvider("software") // Optional: specify KMS provider
}
purposes(VerificationPurpose.AUTHENTICATION)
}

// Assertion key (P-256 for credentials)
verificationMethod("key-2") {
autoGenerateKey {
keyType(KeyTypeMapping.EC)
curve(Curve.P_256)
}
purposes(VerificationPurpose.ASSERTION_METHOD)
}

// Key agreement (X25519 for encryption)
verificationMethod("key-3") {
autoGenerateKey {
keyType(KeyTypeMapping.OKP)
curve(Curve.X25519)
}
purposes(VerificationPurpose.KEY_AGREEMENT)
}

// Multiple services
service("hub") {
type("LinkedDomains")
endpoint("https://hub.example.com")
}

service("messaging") {
type("DIDCommMessaging")
endpoint("https://messaging.example.com")
}
}

Using Existing Keys

Reference keys already stored in KMS:

val options = didCreateOptions {
method("web")
domain("example.com")

// Use existing key by alias
verificationMethod("key-1") {
useExistingKey {
alias("my-existing-signing-key")
providerId("aws-kms") // Optional: specify provider
}
purposes(VerificationPurpose.AUTHENTICATION)
}
}

Using an Existing JWK

Create a DID from an existing public key:

val existingJwk = Jwk(
kty = JwaKeyType.EC,
crv = "P-256",
x = "WKn-ZIGevcwGFOMJ0GeEei2HpXXU6H0z...",
y = "y77t-RvAHRKTsSGd5KhPq8dJ7VhxqPxM..."
)

val options = didCreateOptions {
method("jwk")
useExistingKey(existingJwk)
alias("imported-key")
}

val result = didManager.create(options.options, options.keyConfigs)

DSL Reference

DidCreateBuilder

MethodDescription
method(name)DID method: "key", "jwk", "web"
alias(name)Human-readable alias
domain(domain)Domain for did:web
path(segments...)Path segments for did:web
controller(did)Controller DID (defaults to self)
autoGenerateKey { }Generate a new key via KMS
useExistingKey { }Use existing key by alias
useExistingKey(jwk)Use existing JWK directly
verificationMethod(id) { }Add named verification method
service(id) { }Add service endpoint

AutoGenerateKeyBuilder

MethodDescription
keyType(type)KeyTypeMapping.OKP, EC, or RSA
curve(curve)Curve.Ed25519, P_256, P_384, etc.
algorithm(alg)Optional signature algorithm
kmsProvider(id)KMS provider ID
purposes(...)Verification purposes
verificationMethodId(id)Custom key ID

ExistingKeyByAliasBuilder

MethodDescription
alias(name)Key alias in KMS
providerId(id)KMS provider ID
purposes(...)Verification purposes

ServiceBuilder

MethodDescription
type(type)Service type (e.g., "LinkedDomains")
endpoint(url)Service endpoint URL
endpoint(map)Complex endpoint as map

Update DID

Modify an existing DID document:

val updateResult = didManager.update(
did = "did:web:example.com",
options = DidUpdateOptions(
// Add a new service
addServices = listOf(
DidService(
id = "#messaging",
type = "DIDCommMessaging",
serviceEndpoint = "https://messaging.example.com"
)
),

// Remove services
removeServiceIds = listOf("#old-service"),

// Add verification methods
addVerificationMethods = listOf(
VerificationMethod(
id = "did:web:example.com#key-4",
type = "JsonWebKey2020",
controller = "did:web:example.com",
publicKeyJwk = newPublicKey
)
),

// Remove verification methods
removeVerificationMethodIds = listOf("#deprecated-key")
)
)

updateResult.fold(
success = { result ->
println("Updated: ${result.did}")
// For did:web, publish the updated document
},
failure = { error ->
println("Update failed: ${error.message}")
}
)

Add Key

Add a verification method to an existing DID:

val addKeyResult = didManager.addKey(
did = "did:web:example.com",
options = AddKeyOptions(
publicKeyJwk = newPublicKey,
verificationMethodId = "key-5",
verificationMethodType = VerificationMethodType.JSON_WEB_KEY_2020,
purposes = listOf(VerificationPurpose.ASSERTION_METHOD)
)
)

Deactivate DID

Mark a DID as deactivated:

val deactivateResult = didManager.deactivate(
did = "did:web:example.com",
options = DidDeactivateOptions(
reason = "Key compromise"
)
)

deactivateResult.fold(
success = { result ->
println("Deactivated: ${result.did}")

// For did:web, publish the deactivated document
val deactivatedDoc = result.deactivatedDocument
// Publish to https://example.com/.well-known/did.json
},
failure = { error ->
println("Deactivation failed: ${error.message}")
}
)

Persistence

Storage Backends

BackendUse CaseModule
MemoryTesting, ephemerallib-did-persistence-memory
SQLiteMobile, embeddedlib-did-persistence-sqlite
PostgreSQLEnterprise (EDK)EDK module

DidRepository API

Direct access to DID storage:

val didRepository: DidRepository = session.component.didRepository

// Save a DID record
didRepository.save(didRecord).getOrThrow()

// Find by DID
val record = didRepository.findByDid("did:web:example.com").getOrNull()

// Find by alias
val record = didRepository.findByAlias("my-did").getOrNull()

// Query with filter
val records = didRepository.findAll(
DidRecordFilter(
method = "web",
role = DidRole.MANAGED
)
).getOrThrow()

// Update
didRepository.update(updatedRecord).getOrThrow()

// Delete
didRepository.delete("did:web:example.com").getOrThrow()

Key Mappings

Track the relationship between verification methods and KMS keys:

// Get key mappings for a DID
val mappings = didRepository.getKeyMappings(didRecordId).getOrThrow()

mappings.forEach { mapping ->
println("Verification method: ${mapping.verificationMethodId}")
println("KMS key alias: ${mapping.kmsKeyAlias}")
println("KMS provider: ${mapping.kmsProviderId}")
println("Purposes: ${mapping.purposesJson}")
}

// Save a key mapping
didRepository.saveKeyMapping(
didRecordId = recordId,
mapping = DidKeyMappingRecord(
id = uuid(),
verificationMethodId = "#key-1",
kmsKeyAlias = "my-signing-key",
kmsProviderId = "software",
purposesJson = """["authentication", "assertionMethod"]"""
)
).getOrThrow()

// Delete key mappings
didRepository.deleteKeyMapping(mappingId)
didRepository.deleteKeyMappingsForDid(didRecordId)

Publishing did:web

For did:web, you must publish the DID document to your web server:

val result = didManager.create(options.options, options.keyConfigs)
val didDocument = result.didDocument

// Serialize to JSON
val json = Json.encodeToString(didDocument)

// Publish to the correct URL based on the DID:
// did:web:example.com → https://example.com/.well-known/did.json
// did:web:example.com:users:alice → https://example.com/users/alice/did.json

// Example with Ktor client
httpClient.put("https://example.com/.well-known/did.json") {
contentType(ContentType.Application.Json)
setBody(json)
}

did:web URL Mapping

DIDPublished URL
did:web:example.comhttps://example.com/.well-known/did.json
did:web:example.com:pathhttps://example.com/path/did.json
did:web:example.com:users:alicehttps://example.com/users/alice/did.json
did:web:example.com%3A8080https://example.com:8080/.well-known/did.json

REST API (Universal Registrar)

The EDK provides REST APIs for DID management. See EDK Decentralized Identifiers.

EndpointDescription
POST /1.0/createCreate a new DID
POST /1.0/updateUpdate an existing DID
POST /1.0/deactivateDeactivate a DID
GET /1.0/methodsList supported methods
GET /1.0/propertiesGet capabilities

Platform Examples

// Complete example: Create and use a DID
val didManager: IDidManager = session.component.didManager

// 1. Create DID
val createOptions = didCreateOptions {
method("key")
alias("signing-key")
autoGenerateKey {
keyType(KeyTypeMapping.OKP)
curve(Curve.Ed25519)
purposes(VerificationPurpose.ASSERTION_METHOD)
}
}

val createResult = didManager.create(
createOptions.options,
createOptions.keyConfigs
)
val did = createResult.did

// 2. Use DID for signing
val signCommand: SignJwsCommand = session.component.signJwsCommand
val signResult = signCommand.execute(
SignJwsArgs(
payload = "Hello, World!".encodeToByteArray(),
identifierOpts = ManagedIdentifierOpts(
method = IdentifierMethodDefaults.DID,
identifier = did
)
),
sessionContext
)

println("Signed JWS: ${signResult.getOrThrow().jws}")

Next Steps