Verifiable Credentials (VCs)
TrustWeave expansions in this guide are authored by Geoknoesis LLC. They reflect Geoknoesis’ recommended patterns for W3C Verifiable Credentials on the JVM.
What is a Verifiable Credential?
A Verifiable Credential is a tamper-evident attestation following the W3C VC Data Model. It combines:
1
2
3
dependencies {
implementation("com.trustweave:trustweave-common:1.0.0-SNAPSHOT")
}
Result: Grants access to the credential builders and verification helpers referenced throughout this guide.
- Metadata – issuer, issuance/expiration dates, schema references.
- Credential subject – the claims being asserted (
name,degree,license, etc.). - Proof – cryptographic signature binding the issuer to the credential content.
Why VCs matter in TrustWeave
- They are the unit of trust flowing between issuers and verifiers.
- Wallets store VCs, anchor clients notarise them, and verification routines replay the proofs.
- Typed builders and canonicalisation keep the credential lifecycle consistent across DID methods and signature suites.
How TrustWeave issues and verifies VCs
| Component | Purpose |
|---|---|
CredentialServiceRegistry |
Discovers issuer/verifier services (in-memory or SPI). |
trustWeave.issue { } |
High-level DSL performing canonicalisation, signing, and proof attachment. |
trustWeave.verify { } |
Rebuilds canonical form, resolves DIDs, validates proofs, and returns VerificationResult. |
CredentialIssuanceOptions |
Lower-level SPI options (validity window, schema hints) when using CredentialServiceRegistry. |
Detailed API signatures live in the Credential Service API reference.
Example: issuing a credential
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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
import com.trustweave.TrustWeave
import com.trustweave.credential.IssuanceConfig
import com.trustweave.credential.ProofType
import kotlinx.serialization.json.buildJsonObject
import kotlinx.serialization.json.put
suspend fun issueEmployeeBadge(trustWeave: TrustWeave, issuerDid: String, issuerKeyId: String) =
trustWeave.issue {
credential {
type("EmploymentCredential")
issuer(issuerDid)
subject {
id("did:key:holder-123")
"role" to "Site Reliability Engineer"
"level" to "L5"
}
issued(Instant.now())
}
signedBy(issuerDid = issuerDid, keyId = issuerKeyId)
}
**Outcome:** Issues a signed credential using typed issuance options, returning a `VerifiableCredential` that downstream wallets or verifiers can consume.
TrustWeave automatically:
- Canonicalises the JSON payload using JSON Canonicalization Scheme (JCS).
- Signs the digest through the configured `KeyManagementService`.
- Embeds the resulting proof (`Ed25519Signature2020` by default) into the VC.
- Returns a `VerifiableCredential` data class that you can store or present.
### Example: verifying a credential
```kotlin
import com.trustweave.TrustWeave
import com.trustweave.credential.models.VerifiableCredential
suspend fun verifyBadge(trustWeave: TrustWeave, credential: VerifiableCredential) {
val result = trustWeave.verify {
credential(credential)
}
// Note: verify() returns VerificationResult sealed type
when (result) {
is VerificationResult.Valid -> {
println("Credential verified successfully")
}
is VerificationResult.Invalid -> {
println("Verification failed: ${result.reason}")
}
}
}
**Outcome:** Surfaces verification success or failure reasons, letting you guard business logic with `result.valid` and log granular errors.
Verification resolves the issuer DID document, checks the signature suites, and applies optional policies (expiration, schema, revocation when present).
## Practical usage tips
- **SPI-level options** – drop down to `CredentialServiceRegistry` and supply `CredentialIssuanceOptions` when you need custom proof types, schema hints, or audiences.
- **Anchoring** – store the credential digest with a `BlockchainAnchorClient` to prove freshness (see [Blockchain Anchoring](/core-concepts/blockchain-anchoring/)).
- **Revocation** – integrate status endpoints by adding `credentialStatus` claims; custom verification policies can enforce them.
- **Error handling** – credential operations throw `TrustWeaveError` exceptions directly. Use `try-catch` blocks for error handling. See [Error Handling](/advanced/error-handling/).
- **Input validation** – TrustWeave automatically validates credential structure, issuer DID format, and method registration before issuance.
## Related How-To Guides
- **[Issue Credentials](/how-to/issue-credentials/)** - Step-by-step guide for issuing verifiable credentials
- **[Verify Credentials](/how-to/verify-credentials/)** - Step-by-step guide for verifying credentials
## VC Structure
A Verifiable Credential contains:
A Verifiable Credential contains:
1. **Metadata** – issuer, issuance/expiration dates, schema references
2. **Credential Subject** – the claims being asserted (`name`, `degree`, `license`, etc.)
3. **Proof** – cryptographic signature binding the issuer to the credential content
4. **Schema** – optional schema for validation
5. **Status** – optional revocation status
### Example VC
```json
{
"@context": [
"https://www.w3.org/2018/credentials/v1",
"https://www.w3.org/2018/credentials/examples/v1"
],
"id": "https://example.com/credentials/3732",
"type": ["VerifiableCredential", "UniversityDegreeCredential"],
"issuer": "did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK",
"issuanceDate": "2023-01-01T00:00:00Z",
"expirationDate": "2028-01-01T00:00:00Z",
"credentialSubject": {
"id": "did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK",
"degree": {
"type": "BachelorDegree",
"name": "Bachelor of Science in Computer Science",
"university": "Example University"
}
},
"proof": {
"type": "Ed25519Signature2020",
"created": "2023-01-01T00:00:00Z",
"verificationMethod": "did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK#keys-1",
"proofPurpose": "assertionMethod",
"proofValue": "z5J1pJ2..."
}
}
VC Lifecycle
1. Issuance
A credential is issued by an issuer to a subject:
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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
import com.trustweave.credential.models.VerifiableCredential
import com.trustweave.credential.CredentialIssuanceOptions
// Issue credential using TrustWeave DSL API
val trustWeave = TrustWeave.build {
factories(
kmsFactory = TestkitKmsFactory(),
didMethodFactory = TestkitDidMethodFactory()
)
keys { provider("inMemory"); algorithm("Ed25519") }
did { method("key") { algorithm("Ed25519") } }
credentials { defaultProofType(ProofType.Ed25519Signature2020) }
}
import com.trustweave.trust.types.DidCreationResult
import com.trustweave.trust.types.IssuanceResult
val didResult = trustWeave.createDid {
method("key")
algorithm("Ed25519")
}
val issuerDid = when (didResult) {
is DidCreationResult.Success -> didResult.did
else -> throw IllegalStateException("Failed to create DID: ${didResult.reason}")
}
val resolution = trustWeave.resolveDid(issuerDid)
val issuerDoc = when (resolution) {
is DidResolutionResult.Success -> resolution.document
else -> throw IllegalStateException("Failed to resolve issuer DID")
}
val issuerKeyId = issuerDoc.verificationMethod.firstOrNull()?.id?.substringAfter("#")
?: throw IllegalStateException("No verification method found")
val issuanceResult = trustWeave.issue {
credential {
type(CredentialType.Person)
issuer(issuerDid.value)
subject {
id(subjectDid)
"name" to "Alice"
"email" to "alice@example.com"
}
issued(Instant.now())
}
signedBy(issuerDid = issuerDid.value, keyId = issuerKeyId)
}
val issuedCredential = when (issuanceResult) {
is IssuanceResult.Success -> issuanceResult.credential
else -> throw IllegalStateException("Failed to issue credential: ${issuanceResult.reason}")
}
**Outcome:** Produces a signed credential ready for distribution, anchored to the specific proof type and key you configured.
### 2. Storage
Store credentials in a wallet:
```kotlin
import com.trustweave.testkit.credential.BasicWallet
val wallet = BasicWallet()
val credentialId = wallet.store(issuedCredential)
**Outcome:** Persists the credential in a wallet so it can be queried, organised, and presented later.
3. Presentation
Create a Verifiable Presentation to share credentials:
1
2
3
4
5
6
7
8
9
10
11
import com.trustweave.credential.models.VerifiablePresentation
import com.trustweave.credential.PresentationOptions
val presentation = VerifiablePresentation(
type = listOf("VerifiablePresentation"),
verifiableCredential = listOf(issuedCredential),
holder = subjectDid,
proof = // ... proof of presentation
)
**Outcome:** Wraps one or more credentials in a holder-signed presentation, enabling selective disclosure downstream.
4. Verification
Verify a credential or presentation:
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
import com.trustweave.TrustWeave
import com.trustweave.credential.VerificationConfig
val trustWeave = TrustWeave.build {
factories(
kmsFactory = TestkitKmsFactory(),
didMethodFactory = TestkitDidMethodFactory()
)
keys { provider("inMemory"); algorithm("Ed25519") }
did { method("key") { algorithm("Ed25519") } }
}
val result = trustWeave.verify {
credential(issuedCredential)
checkExpiration()
// checkRevocation() requires status list integration
}
if (result.valid) {
println("Credential passed structural checks.")
println("Proof valid: ${result.proofValid}, Issuer valid: ${result.issuerValid}")
} else {
println("Verification errors: ${result.errors.joinToString()}")
}
**Outcome:** Indicates whether the credential satisfied structural checks (expiration, DID resolution, optional revocation) and surfaces diagnostics for debugging.
Important: The built-in verifier performs structural checks today (proof fields, expiration, DID resolution). Integrate a dedicated cryptographic proof validator and revocation resolver for production deployments.
5. Revocation
Revoke a credential if needed:
1
2
// Credential status is checked during verification
// Revocation is handled via credentialStatus field
Types of Claims
Identity Claims
Claims about who you are:
- Name
- Date of birth
- Nationality
- Email address
Achievement Claims
Claims about what you’ve accomplished:
- Educational degrees
- Professional certifications
- Awards
- Skills
Authorization Claims
Claims about what you’re allowed to do:
- Access permissions
- Membership status
- Role assignments
Proof Types
TrustWeave supports multiple proof types:
- Ed25519Signature2020: Ed25519 signatures (recommended)
- JsonWebSignature2020: JWT-based proofs
- BbsBlsSignature2020: BBS+ signatures for selective disclosure
Schema Validation
Credentials can reference schemas for validation:
1
2
3
4
5
6
7
8
val credential = VerifiableCredential(
// ...
credentialSchema = CredentialSchema(
id = "https://example.com/schemas/person.json",
type = "JsonSchemaValidator2018",
schemaFormat = SchemaFormat.JSON_SCHEMA
)
)
Privacy Features
Selective Disclosure
Reveal only specific fields from a credential:
1
2
3
4
5
6
val presentation = wallet.createSelectiveDisclosure(
credentialIds = listOf(credentialId),
disclosedFields = listOf("name", "email"), // Only reveal name and email
holderDid = holderDid,
options = PresentationOptions(...)
)
Zero-Knowledge Proofs
Some proof types (like BBS+) support zero-knowledge proofs, allowing you to prove claims without revealing the actual values.
Common Use Cases
- Education: Diplomas, certificates, transcripts
- Employment: Work history, skills, references
- Healthcare: Medical records, prescriptions, test results
- Identity: Government IDs, passports, driver’s licenses
- Membership: Club memberships, subscriptions, loyalty programs
Best Practices
- Always verify credentials before trusting them
- Check expiration dates to ensure credentials are still valid
- Verify revocation status to ensure credentials haven’t been revoked
- Use selective disclosure to minimize data exposure
- Store credentials securely in a wallet
See also
- Credential Service API for parameter details and SPI guidance
- Quick Start – Step 4 & 5 for a runnable walkthrough
- Wallets for storage and presentation
- Architecture Overview for the credential flow diagram
Next Steps
Ready to use credentials?
- Issue Credentials - Step-by-step guide
- Verify Credentials - Step-by-step guide
- Quick Start – Step 4 & 5 - Create your first credential
Want to learn more?
- Wallets - Managing credentials
- Wallet API Tutorial - Hands-on wallet examples
- Credential Service API Reference - Complete API documentation
- Core API Reference - TrustLayer API