Skip to main content
The Java SDK is stateless. It does not hold wallet state between calls. After createWalletAccount(), the returned KeygenResult carries two distinct pieces of state — each belongs in a different storage tier: Both must be persisted from creation. The SDK provides no compatibility shim — every subsequent operation requires walletProperties explicitly, and operations without cached List<ServerKeyShare> (and without backup fallback) will fail.

Persisting WalletProperties

WalletProperties is non-sensitive identity + backup-pointer metadata. It exposes toJson() / fromJson(...) with snake_case keys (cross-SDK compatible with Python and Rust) and toNodeJson() / fromNodeJson(...) with camelCase (Node v1 interop). Cache it like any other per-user record:
Cache the full record. The externalServerKeySharesBackupInfo field on WalletProperties is not recoverable via SDK-scoped endpoints — fetchWalletMetadata(accountAddress) returns identity only. Operations that need it (password verification, share recovery) will fail if it’s missing. Mutating operations (updatePassword, refresh, reshare) return new backup info — merge it into the cached WalletProperties via WalletProperties.withBackupInfo(...) before writing back to your cache.

Storing List<ServerKeyShare>

When you set .backUpToDynamic(false) during wallet creation, you are responsible for securely storing the List<ServerKeyShare> returned by createWalletAccount. These key shares, combined with your Dynamic developer API key, provide signing authority and must be protected with defense-in-depth strategies. ServerKeyShare is JSON-serializable — use ServerKeyShare.toJson() / fromJson(...) or your vault’s native format before persisting.
Use google-cloud-kms or google-cloud-secretmanager — same envelope pattern as AWS KMS, integrated with Google Cloud IAM and audit logging.
Use azure-security-keyvault-secrets — Azure-managed secrets with Managed Identity auth.
Use vault-java-driver or spring-cloud-vault to read/write secrets to a Vault KV mount. Works well if your stack already runs Vault.

Security Requirements Checklist

Regardless of your storage method, follow these requirements:
  • Never log plaintext key shares — redact externalServerKeyShares from all logs, error messages, and monitoring. ServerKeyShare.toString() is redacted by the SDK; mirror that in your own code.
  • Encrypt at rest — use AES-256-GCM or equivalent; ensure database/storage has encryption enabled
  • Encrypt in transit — all communication must use TLS 1.3
  • Implement access controls — restrict which services and roles can decrypt key shares
  • Enable audit logging — track all access to encrypted materials with timestamps and actor identity
  • Separate encryption keys — don’t reuse keys across environments (dev/staging/prod)
  • Use unique encryption per record — generate new IVs for each encryption operation
  • Implement key rotation — rotate encryption keys periodically (e.g., every 90 days)
  • Plan for key compromise — document incident response for key material exposure
  • Secure deletion — null out decrypted byte arrays after use; prefer char[] for passwords so you can clear them deterministically

Storage Schema Example

What NOT to Do

  • Never store plaintext key shares in databases, files, or environment variables
  • Never commit encryption keys or key shares to version control
  • Never use the same encryption key across all wallets
  • Never rely solely on database encryption without application-level encryption
  • Never expose key shares through APIs or logs
Last modified on May 25, 2026