Skip to content
SDKs · ServerIn developmentView source

Java

Java 17+, on the JDK HttpClient. No third-party HTTP dependency.

In development

This SDK is implemented but not yet published to its package registry. Until it ships, collect an opaque evidence blob on the device and appraise it server-side with @rootherald/node (or any available server SDK). The API shown below is the planned surface and may change before release.

pom.xmlxml
<dependency>
  <groupId>io.rootherald</groupId>
  <artifactId>rootherald-client</artifactId>
  <version>x.y.z</version> <!-- assigned at first release -->
</dependency>

The calls

  • RootHeraldClient.builder().secretKey(…).baseUrl(…).build()
  • issueChallenge(ChallengeOptions.defaults().ask(ASK_IDENTITY, ASK_POSTURE).policy(…))Challenge(challengeId, challenge, nonce, expiresAt).
  • verify(evidenceJson, AttestOptions.of(challengeId).policy(…))AttestResult(verdict, verdictNode, assuranceClaimsMet, enrollmentRequired, key); isAllowed().
  • relayEnroll(blob, challengeId) / relayActivate(activation).
  • KeySignatures.verifyKeySignature(jwk, message, signature)java.security, raw or DER.
  • Exceptions in io.rootherald: InvalidSecretKeyException, ChallengeException, InvalidEvidenceException, UnknownPolicyException, PolicyDowngradeException, AdmissionRefusedException, QuotaExceededException; base RootHeraldApiException.

Example

AttestController.javajava
import io.rootherald.QuotaExceededException;
import io.rootherald.client.*;

var rh = RootHeraldClient.builder().secretKey(System.getenv("RH_SECRET_KEY")).build();

@PostMapping("/api/challenge")
Challenge challenge() {
    return rh.issueChallenge(ChallengeOptions.defaults()
        .ask(ChallengeOptions.ASK_IDENTITY, ChallengeOptions.ASK_POSTURE)
        .policy("rootherald:builtin:strict-hardware"));      // relay challenge() to the client verbatim
}

@PostMapping("/api/verify")
ResponseEntity<?> verify(@RequestBody VerifyBody body) {   // { challengeId, evidence } — evidence relayed as a JSON string
    AttestResult result;
    try {
        result = rh.verify(body.evidence(), AttestOptions.of(body.challengeId()));
    } catch (QuotaExceededException e) {
        return ResponseEntity.status(429).build();
    }
    if (result.enrollmentRequired()) return ResponseEntity.status(409).body("enrollment_required");
    if (!result.isAllowed())         return ResponseEntity.status(403).body("device_rejected");

    String deviceId = result.verdictNode().path("device").path("ueid").asText();
    result.key().ifPresent(k -> keys.put(deviceId, k.jwk()));    // only for a key ask, only on pass
    return ResponseEntity.ok(Map.of("deviceId", deviceId));
}

A Spring Boot sample is at sdk-java/samples/spring-boot-demo.