إنتقل إلى المحتوى الرئيسي
ينطبق على:
Java 17+Spring BootDuoKey Cockpit

المتطلبات المسبقة

  • JDK 17+ (the reference client uses `java.net.http.HttpClient` and a Java record, which requires JDK 16+).
  • A CAP API key (dke_cap_…) for the calling tenant — see Getting Started.
  • An active tenant policy (bootstrap one from a template).

Install​

The Java client is a single self-contained class, ch.duokey.cap.CapClient, downloaded from your Cockpit. It depends only on the JDK HTTP client plus Jackson for JSON.

The download endpoint returns a JSON envelope (language, filename, content), so extract the content field — for example with jq — rather than saving the response body directly:

DownloadBASH
curl -H "Authorization: Bearer $COCKPIT_SESSION_JWT" \
"https://cockpit.example.com/api/cap/sdk/download/java" \
| jq -r '.content' > src/main/java/ch/duokey/cap/CapClient.java
pom.xml (Jackson)XML
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.18.8</version>
</dependency>

The client surface​

MemberPurpose
new CapClient(String baseUrl, String capKey)Create a client. baseUrl is the Cockpit API root (…/api); capKey is the dke_cap_… key.
new CapClient(baseUrl, capKey, byte[] dataKey)Optional: supply a 32-byte key for the local demo protect/reveal (see caveat below).
CapDecision resolve(String dataClass, String purpose)Resolve an intent to a decision.
void observe(String dataClass, String purpose, String observedAlgorithm, String source)Report the algorithm that actually ran (drift detection).
String protect(String value, String dataClass, String purpose)Convenience: resolve + apply (demo helper — see caveat).
String reveal(String tagged)Reverse a value produced by protect (demo helper).

CapDecision is a record: algorithm(), posture(), quantumResistant(), backend().

CapDecision fieldsJAVA
CapDecision d = cap.resolve("pan", "storage");
d.algorithm();        // e.g. "aes256-gcm"
d.posture();          // "classical" | "hybrid" | "pqc_only"
d.quantumResistant(); // false for classical, true otherwise
d.backend();          // "software" | "fips1403_hsm" | "pqc_capable" | "local_tokenizer"

Configure with Spring​

application.propertiesPROPERTIES
cap.base-url=https://cockpit.example.com/api
cap.key=${CAP_KEY}
CapConfig.javaJAVA
@Configuration
public class CapConfig {
  @Bean
  CapClient capClient(
          @Value("${cap.base-url}") String baseUrl,
          @Value("${cap.key}") String capKey) {
      return new CapClient(baseUrl, capKey);
  }
}
Keep the key out of source

Bind cap.key from an environment variable or your secret manager. Never commit a dke_cap_… key.

Resolve and run a signature​

You never hard-code the algorithm. You resolve the intent, run the operation with the resolved algorithm and the policy-referenced key, then observe.

SignatureService.javaJAVA
@Service
public class SignatureService {

  private final CapClient cap;

  public SignatureService(CapClient cap) { this.cap = cap; }

  public byte[] sign(byte[] payload) {
      // 1) Resolve WHAT (pii + sign) -> HOW (algorithm, posture, key, backend)
      CapDecision d = cap.resolve("pii", "sign");

      // 2) Run the signature with d.algorithm() using the referenced key.
      String algorithm = d.algorithm();   // e.g. "hybrid-ml-dsa65-ecdsa-p256"
      byte[] signature = signer.sign(payload, algorithm);

      // 3) Close the loop: report what actually ran.
      cap.observe("pii", "sign", algorithm, "payments-svc");
      return signature;
  }
}
What CAP does and does not do

CAP resolves the decision (algorithm, posture, key reference, backend) — it never holds your key bytes. The signature/encryption itself runs in your process or on the resolved backend, using the key referenced by d. For PQC and hybrid algorithms, the cryptographic operation executes on the DuoKey backend in production.

The convenience helpers (protect / reveal)​

For symmetric storage and tokenization the client offers one-liners:

protect / revealJAVA
String tagged = cap.protect("4111111111111111", "pan", "tokenize");
String pan    = cap.reveal(tagged);
The reference helpers are illustrative

In the downloaded reference client, protect/reveal run aes256-gcm with a local demo key and a stand-in tokenizer — they are for wiring and testing, not production. Production tokenization and vault-backed encryption run server-side against the policy's key_ref. Do not ship the demo protect() as real tokenization.

Observe and drift​

observe(...) reports the algorithm that actually executed. If it is weaker than the resolved decision, CAP records a drift finding (warning if still quantum-resistant, critical if it dropped to classical). Security teams review findings via the Cockpit (GET /api/cap/drift).

Reporting drift-honest telemetryJAVA
// Suppose a legacy path signed with plain ECDSA while policy resolved to hybrid:
cap.observe("pii", "sign", "ecdsa-p256", "legacy-batch");
// -> CAP flags a drift finding for pii.sign (declared hybrid, observed classical)

The posture flip — zero code change​

1

Today

resolve("pii","sign") returns hybrid-ml-dsa65-ecdsa-p256 under a hybrid policy.

2

Security raises the floor

The team creates a new policy version with a pqc_only posture (or bootstraps cnsa_2_0).

3

Next resolve

The same code now receives ml-dsa65 — no recompile, no redeploy of application logic.

Error handling​

SituationWhat you getWhat to do
No active policy for the tenantresolved: false, reason "no active CAP policy…"Bootstrap a policy from a template.
Posture floor cannot be met for the intentresolved: false with a reasonFix the policy rule or the requested residency.
Unknown data class / purposeClient/validation errorUse a valid data class and one of storage / transport / tokenize / sign.
Invalid or revoked keyHTTP 401/403 from /cap/sdk/*Mint a new dke_cap_ key.
cap.enabled feature offAccess deniedThe tenant needs the Enterprise or Free Trial edition.