Skip to main content

Java Keymaster SDK

Overview

The Java Keymaster SDK lets JVM applications create and manage DIDs, issue credentials, and handle challenge/response flows by calling a Gatekeeper REST service.

Who this is for

  • Java and Kotlin backend services (including Spring Boot).
  • Android/JVM clients that need Keymaster functionality.
  • Teams that want a Java-native alternative to the JS SDK.

What this page covers

  • Install and setup (Gradle/Maven).
  • Configuration and runtime notes.
  • Core workflows for IDs, credentials, challenges, assets, and groups.
  • Crypto helpers and low-level APIs.
  • Tests.

Install

Choose your build tool and add the Keymaster SDK dependencies.

dependencies {
implementation("org.keychain:keymaster:<version>")
implementation("org.keychain:gatekeeper:<version>") // Gatekeeper REST client
// Optional direct modules
implementation("org.keychain:crypto:<version>")
implementation("org.keychain:cid:<version>")
}

Configuration

Gatekeeper URL

Keymaster talks to a Gatekeeper REST service. By default, the client connects to:

http://localhost:4224

To override, set KC_GATEKEEPER_URL in the environment before creating a client.

Registry

  • local is recommended for development and live tests.
  • hyperswarm is commonly used for production networks.

Wallet storage and passphrase

The SDK stores an encrypted wallet file on disk. You must provide:

  • A wallet file path (or directory + filename)
  • A passphrase to encrypt the mnemonic

Hello Keymaster

Minimal setup that creates an ID and resolves it back:

import java.nio.file.Path;
import java.nio.file.Paths;
import org.keychain.gatekeeper.GatekeeperClient;
import org.keychain.gatekeeper.GatekeeperClientOptions;
import org.keychain.keymaster.Keymaster;
import org.keychain.keymaster.model.WalletEncFile;
import org.keychain.keymaster.store.WalletJson;
import org.keychain.keymaster.store.WalletStore;

Path dataDir = Paths.get(System.getProperty("user.home"), ".keymaster");
WalletStore<WalletEncFile> store = new WalletJson<>(WalletEncFile.class, dataDir, "wallet.json");

GatekeeperClientOptions options = new GatekeeperClientOptions();
options.baseUrl = "http://localhost:4224";
GatekeeperClient gatekeeper = new GatekeeperClient(options);

Keymaster keymaster = new Keymaster(store, gatekeeper, "passphrase", "local");

String did = keymaster.createId("Alice");
System.out.println("DID: " + did);

System.out.println(keymaster.resolveDID(did).didDocument.id);

IDs

Create an ID and resolve it:

String did = keymaster.createId("Alice");
System.out.println("DID: " + did);

System.out.println(keymaster.resolveDID(did).didDocument.id);

Set the current ID and list IDs in the wallet:

keymaster.setCurrentId("Alice");
System.out.println(keymaster.listIds());

Wallet and ID management

Wallet lifecycle

Create a new wallet, then load it:

String mnemonic = "red orange yellow green blue indigo violet pink white black brown grey";
keymaster.newWallet(mnemonic, true);

WalletFile wallet = keymaster.loadWallet();
System.out.println(wallet.current);

Update and save the wallet using mutateWallet to avoid race conditions when multiple threads read and write the wallet at the same time:

keymaster.mutateWallet(w -> {
w.extras = Map.of("owner", "team-keymaster");
});

Names and aliases

Validate and manage friendly names:

keymaster.validateName("Alice");
keymaster.addName("alice-main", "did:test:123");
System.out.println(keymaster.getName("alice-main"));
keymaster.removeName("alice-main");

ID maintenance

Rename or remove IDs in the wallet:

keymaster.renameId("Alice", "Alice-1");
keymaster.removeId("Alice-1");

Backup and recovery

Back up the current ID and wallet:

keymaster.backupId();
String backupDid = keymaster.backupWallet("local");
System.out.println(backupDid);

Recover a wallet or ID from a backup DID:

WalletFile recoveredWallet = keymaster.recoverWallet(backupDid);
String recoveredId = keymaster.recoverId(backupDid);

Export an encrypted wallet payload for storage:

WalletEncFile encrypted = keymaster.exportEncryptedWallet();

Assets and schemas

Create and update assets:

String assetDid = keymaster.createAsset(Map.of("name", "mockData"));
Map<String, Object> data = (Map<String, Object>) keymaster.resolveAsset(assetDid);
data.put("status", "active");
keymaster.updateAsset(assetDid, data);

Transfer an asset to a different controller:

String alice = keymaster.createId("Alice");
String bob = keymaster.createId("Bob");
String assetDid = keymaster.createAsset(Map.of("name", "asset-1"));
keymaster.transferAsset(assetDid, bob);

Clone an asset into another registry or controller:

String cloned = keymaster.cloneAsset(assetDid, "local", alice);

Schema helpers:

String schemaDid = keymaster.createSchema(emailSchema);
Object schema = keymaster.getSchema(schemaDid);
boolean ok = keymaster.testSchema(schemaDid);
Map<String, Object> template = keymaster.createTemplate(schemaDid);

List assets and schemas:

System.out.println(keymaster.listAssets());
System.out.println(keymaster.listSchemas());

Credentials

Create a schema, bind a credential, issue it, and publish with reveal:

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.keychain.keymaster.PublishCredentialOptions;

Map<String, Object> emailSchema = new HashMap<>();
emailSchema.put("$schema", "http://json-schema.org/draft-07/schema#");
Map<String, Object> properties = new HashMap<>();
Map<String, Object> email = new HashMap<>();
email.put("format", "email");
email.put("type", "string");
properties.put("email", email);
emailSchema.put("properties", properties);
emailSchema.put("required", List.of("email"));
emailSchema.put("type", "object");

String aliceDid = keymaster.createId("Alice");
String schemaDid = keymaster.createSchema(emailSchema);

Map<String, Object> bound = keymaster.bindCredential(schemaDid, aliceDid);
@SuppressWarnings("unchecked")
Map<String, Object> credential = (Map<String, Object>) bound.get("credential");
credential.put("email", "alice@example.com");
String credentialDid = keymaster.issueCredential(bound);

PublishCredentialOptions publish = new PublishCredentialOptions();
publish.reveal = true;
keymaster.publishCredential(credentialDid, publish);

Manage issued and held credentials:

System.out.println(keymaster.listIssued("Alice"));
System.out.println(keymaster.listCredentials("Alice"));

Accept, update, and revoke credentials:

keymaster.acceptCredential(credentialDid);

@SuppressWarnings("unchecked")
Map<String, Object> vc = (Map<String, Object>) keymaster.getCredential(credentialDid);
Map<String, Object> cred = (Map<String, Object>) vc.get("credential");
cred.put("email", "alice+updated@example.com");
keymaster.updateCredential(credentialDid, vc);

keymaster.revokeCredential(credentialDid);

Unpublish or remove a credential from the wallet:

keymaster.unpublishCredential(credentialDid);
keymaster.removeCredential(credentialDid);

Groups

Create a group and add members:

String groupDid = keymaster.createGroup("engineering");
String memberDid = keymaster.createId("Bob");
keymaster.addGroupMember(groupDid, memberDid);
keymaster.removeGroupMember(groupDid, memberDid);

Check membership and list groups:

boolean isMember = keymaster.testGroup(groupDid, memberDid);
System.out.println(isMember);

System.out.println(keymaster.listGroups());

Challenge and response

Create a challenge, create a response from a different subject, then verify it:

import java.util.List;
import java.util.Map;

String alice = keymaster.createId("Alice");
String bob = keymaster.createId("Bob");
keymaster.createId("Victor");

keymaster.setCurrentId("Alice");
String credentialDid = keymaster.createSchema(emailSchema);
Map<String, Object> boundCredential = keymaster.bindCredential(credentialDid, bob);
String vcDid = keymaster.issueCredential(boundCredential);

keymaster.setCurrentId("Bob");
keymaster.acceptCredential(vcDid);

keymaster.setCurrentId("Victor");
Map<String, Object> challenge = Map.of(
"credentials",
List.of(
Map.of(
"schema",
credentialDid,
"issuers",
List.of(alice)
)
)
);
String challengeDid = keymaster.createChallenge(challenge);

keymaster.setCurrentId("Bob");
String responseDid = keymaster.createResponse(challengeDid);
Map<String, Object> verify = keymaster.verifyResponse(responseDid);
System.out.println(verify.get("match"));

Crypto helpers

Encrypt and decrypt JSON payloads:

Map<String, Object> payload = Map.of("message", "hello");
String encryptedDid = keymaster.encryptJSON(payload, alice);
@SuppressWarnings("unchecked")
Map<String, Object> decrypted = (Map<String, Object>) keymaster.decryptJSON(encryptedDid);
System.out.println(decrypted.get("message"));

Encrypt and decrypt a plain text message:

String cipherDid = keymaster.encryptMessage("secret", alice);
String message = keymaster.decryptMessage(cipherDid);
System.out.println(message);

Sign and verify a map:

Map<String, Object> unsigned = Map.of("action", "approve");
Map<String, Object> signed = keymaster.addSignature(unsigned);
System.out.println(keymaster.verifySignature(signed));

Wallet storage

File-backed wallet storage using WalletJson:

import java.nio.file.Path;
import java.nio.file.Paths;
import org.keychain.keymaster.model.WalletEncFile;
import org.keychain.keymaster.store.WalletJson;
import org.keychain.keymaster.store.WalletStore;

Path dataDir = Paths.get(System.getProperty("user.home"), ".keymaster");
WalletStore<WalletEncFile> store = new WalletJson<>(WalletEncFile.class, dataDir, "wallet.json");

For short-lived services or tests, use a temp directory and a short passphrase. The wallet is encrypted on disk. The decrypted mnemonic and derived keys are only kept in memory.

Compatibility and runtime notes

Java version

The SDK targets JDK 11. Use a JDK 11 runtime for builds and production deployments.

Crypto compatibility

The Java crypto implementation is designed to be compatible with the JS Keymaster.

  • BIP39 mnemonic and BIP32 derivation (m/44'/0'/{account}'/0/{index})
  • secp256k1 JWK conversion and ECDSA signatures
  • Canonical JSON (RFC8785) with SHA-256 hashes
  • XChaCha20-Poly1305 encryption

Gatekeeper and registry

Keymaster requires a Gatekeeper REST service. Use the local registry for development and live testing, and hyperswarm when you need to publish to a public network.

If you see gatekeeper not configured, ensure you passed a GatekeeperClient to Keymaster.

Tests

The repository includes live integration tests that call a running Gatekeeper service. Run them explicitly when you want end-to-end validation:

./gradlew :keymaster:liveTest

Crypto and CID modules have non-live tests that run with the default test task:

./gradlew :crypto:test :cid:test