DIDComm V2 Integration Guide
Overview
This guide explains how to integrate the production-ready didcomm-java library to replace the placeholder cryptographic implementation.
Current Implementation Status
The DIDComm plugin has two crypto implementations:
- Placeholder Implementation (
DidCommCrypto.kt)- ✅ Structure is correct
- ❌ Returns dummy data (not real encryption)
- ⚠️ Suitable for development/testing only
- Production Implementation (
DidCommCryptoProduction.kt)- ✅ Uses
didcomm-javalibrary - ✅ Full ECDH-1PU implementation
- ⚠️ Requires library dependency
- ✅ Uses
Step 1: Add didcomm-java Dependency
Option A: Add to libs.versions.toml (Recommended)
Add to gradle/libs.versions.toml:
1
2
3
4
5
[versions]
didcomm-java = "0.3.2"
[libraries]
didcomm-java = { module = "org.didcommx:didcomm", version.ref = "didcomm-java" }
Then in credentials/plugins/didcomm/build.gradle.kts:
1
2
3
4
5
6
dependencies {
// ... existing dependencies ...
// DIDComm library (production crypto)
implementation(libs.didcomm.java)
}
Option B: Direct Dependency
Add directly to credentials/plugins/didcomm/build.gradle.kts:
1
2
3
4
5
6
dependencies {
// ... existing dependencies ...
// DIDComm library (production crypto)
implementation("org.didcommx:didcomm:0.3.2")
}
Step 2: Update DidCommCryptoProduction
Uncomment the code in DidCommCryptoProduction.kt:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
class DidCommCryptoProduction(...) {
private val didComm = org.didcommx.didcomm.DIDComm()
suspend fun encrypt(...): String {
// Uncomment the implementation code
val didCommMessage = org.didcommx.didcomm.message.Message.builder()
.id(message["id"]?.jsonPrimitive?.content ?: UUID.randomUUID().toString())
.type(message["type"]?.jsonPrimitive?.content ?: "")
.from(fromDid)
.to(listOf(toDid))
.body(message["body"]?.jsonObject ?: buildJsonObject { })
.build()
val packed = didComm.pack(
message = didCommMessage,
from = fromDid,
to = listOf(toDid),
signFrom = fromKeyId
)
return packed.value
}
suspend fun decrypt(...): JsonObject {
// Uncomment the implementation code
val packed = org.didcommx.didcomm.pack.EncryptedPackedMessage(packedMessage)
val unpacked = didComm.unpack(
packed = packed,
to = recipientDid,
from = senderDid
)
// Convert back to JsonObject
// ...
}
}
Step 3: Update Factory to Use Production Crypto
Update DidCommFactory.kt:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
object DidCommFactory {
fun createInMemoryService(
kms: KeyManagementService,
resolveDid: suspend (String) -> DidDocument?,
useProductionCrypto: Boolean = true // Enable production crypto
): DidCommService {
val crypto = if (useProductionCrypto) {
DidCommCryptoAdapter(kms, resolveDid, useProduction = true)
} else {
DidCommCryptoAdapter(kms, resolveDid, useProduction = false)
}
val packer = DidCommPacker(crypto, resolveDid)
return InMemoryDidCommService(packer, resolveDid)
}
}
Step 4: Update DidCommPacker
Update DidCommPacker.kt to handle both envelope and packed string formats:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class DidCommPacker(
private val crypto: DidCommCryptoAdapter, // Use adapter
private val resolveDid: suspend (String) -> DidDocument?
) {
suspend fun pack(...): String {
val messageJson = message.toJsonObject()
if (encrypt) {
// Use adapter which handles both implementations
if (crypto is DidCommCryptoAdapter && crypto.useProduction) {
// Production crypto returns packed string directly
return crypto.encryptAsPacked(
messageJson, fromDid, fromKeyId, toDid, toKeyId
)
} else {
// Placeholder crypto returns envelope
val envelope = crypto.encrypt(...)
return envelopeToJson(envelope)
}
}
// ...
}
}
Step 5: Key Management Integration
The didcomm-java library needs access to private keys. You’ll need to:
- Extend KMS Interface (if needed):
1 2 3 4 5
interface KeyManagementService { // ... existing methods ... suspend fun getPrivateKey(keyId: String): PrivateKey }
- Create Key Adapter:
1 2 3 4 5 6 7
class DidCommKeyResolver( private val kms: KeyManagementService ) : org.didcommx.didcomm.secrets.SecretsResolver { override suspend fun getKey(keyId: String): PrivateKey { return kms.getPrivateKey(keyId) } }
- Pass to DIDComm:
1 2
val keyResolver = DidCommKeyResolver(kms) val didComm = org.didcommx.didcomm.DIDComm(secretsResolver = keyResolver)
Testing
Test with Placeholder (Development)
1
2
3
4
5
val didcomm = DidCommFactory.createInMemoryService(
kms = kms,
resolveDid = resolveDid,
useProductionCrypto = false // Use placeholder
)
Test with Production Crypto
1
2
3
4
5
val didcomm = DidCommFactory.createInMemoryService(
kms = kms,
resolveDid = resolveDid,
useProductionCrypto = true // Use production
)
Verification
After integration, verify:
- ✅ Messages encrypt correctly
- ✅ Messages decrypt correctly
- ✅ Interoperability with other DIDComm implementations
- ✅ All tests pass
Troubleshooting
“ClassNotFoundException: org.didcommx.didcomm.DIDComm”
Solution: The didcomm-java dependency is not on the classpath. Add it to build.gradle.kts.
“UnsupportedOperationException: Production crypto not available”
Solution: Uncomment the implementation code in DidCommCryptoProduction.kt.
“Cannot resolve getPrivateKey”
Solution: Extend your KMS implementation to provide private key access, or create an adapter.
Migration Path
- Phase 1 (Current): Use placeholder crypto for development
- Phase 2: Add
didcomm-javadependency - Phase 3: Uncomment production crypto code
- Phase 4: Test with production crypto
- Phase 5: Switch to production crypto by default