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.

  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.

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

  1. Always verify credentials before trusting them
  2. Check expiration dates to ensure credentials are still valid
  3. Verify revocation status to ensure credentials haven’t been revoked
  4. Use selective disclosure to minimize data exposure
  5. Store credentials securely in a wallet

See also

Next Steps

Ready to use credentials?

Want to learn more?