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; baseRootHeraldApiException.
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.