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:
| Operation | Description |
|---|---|
create | Create a new DID with generated or existing keys |
update | Modify a DID document (add/remove keys, services) |
addKey | Add a verification method to an existing DID |
deactivate | Deactivate a DID |
list | Query stored DIDs with filters |
findByDid | Find a DID by its identifier |
findByAlias | Find 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
| Method | Description |
|---|---|
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
| Method | Description |
|---|---|
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
| Method | Description |
|---|---|
alias(name) | Key alias in KMS |
providerId(id) | KMS provider ID |
purposes(...) | Verification purposes |
ServiceBuilder
| Method | Description |
|---|---|
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
| Backend | Use Case | Module |
|---|---|---|
| Memory | Testing, ephemeral | lib-did-persistence-memory |
| SQLite | Mobile, embedded | lib-did-persistence-sqlite |
| PostgreSQL | Enterprise (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
| DID | Published URL |
|---|---|
did:web:example.com | https://example.com/.well-known/did.json |
did:web:example.com:path | https://example.com/path/did.json |
did:web:example.com:users:alice | https://example.com/users/alice/did.json |
did:web:example.com%3A8080 | https://example.com:8080/.well-known/did.json |
REST API (Universal Registrar)
The EDK provides REST APIs for DID management. See EDK Decentralized Identifiers.
| Endpoint | Description |
|---|---|
POST /1.0/create | Create a new DID |
POST /1.0/update | Update an existing DID |
POST /1.0/deactivate | Deactivate a DID |
GET /1.0/methods | List supported methods |
GET /1.0/properties | Get capabilities |
Platform Examples
- Kotlin/JVM
- Swift/iOS
// 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}")
// Create DID on iOS
let didManager = session.component.didManager
let options = DidCreateOptions(
method: "key",
alias: "signing-key"
)
let result = try await didManager.create(options: options)
print("Created DID: \(result.did)")
// Use for signing
let signCommand = session.component.signJwsCommand
let signResult = try await signCommand.execute(
args: SignJwsArgs(
payload: "Hello, World!".data(using: .utf8)!,
identifierOpts: ManagedIdentifierOpts(
method: .DID,
identifier: result.did
)
),
context: sessionContext
)
Next Steps
- DID Resolution - Resolve and query DIDs
- Key Management - Manage cryptographic keys
- Identifier Resolution - Unified identifier resolution