Back to Documentation
Java Documentation

Java Documentation

Java 8+ integration guide

Official GuideProduction Ready

Java SDK Guide

Dependencies

Add to your pom.xml (Maven):

xml
<dependencies>
    <dependency>
        <groupId>com.squareup.okhttp3</groupId>
        <artifactId>okhttp</artifactId>
        <version>4.12.0</version>
    </dependency>
    <dependency>
        <groupId>com.google.code.gson</groupId>
        <artifactId>gson</artifactId>
        <version>2.10.1</version>
    </dependency>
</dependencies>

Or build.gradle (Gradle):

gradle
dependencies {
    implementation 'com.squareup.okhttp3:okhttp:4.12.0'
    implementation 'com.google.code.gson:gson:2.10.1'
}

Basic Usage

java
import okhttp3.*;
import com.google.gson.Gson;
import com.google.gson.JsonObject;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;

public class KeyClaimClient {
    private static final String API_BASE_URL = "https://keyclaim.org/api";
    private final String apiKey;
    private final String secret;
    private final OkHttpClient client;
    private final Gson gson;

    public KeyClaimClient(String apiKey, String secret) {
        this.apiKey = apiKey;
        this.secret = secret != null ? secret : apiKey;
        this.client = new OkHttpClient();
        this.gson = new Gson();
    }

    // Step 1: Create a challenge
    public ChallengeResponse createChallenge(int ttl) throws Exception {
        JsonObject requestBody = new JsonObject();
        requestBody.addProperty("key", apiKey);
        requestBody.addProperty("ttl", ttl);

        RequestBody body = RequestBody.create(
            requestBody.toString(),
            MediaType.parse("application/json")
        );

        Request request = new Request.Builder()
            .url(API_BASE_URL + "/challenge/create")
            .post(body)
            .build();

        try (Response response = client.newCall(request).execute()) {
            if (!response.isSuccessful()) {
                throw new Exception("Failed to create challenge: " + response.code());
            }
            String responseBody = response.body().string();
            return gson.fromJson(responseBody, ChallengeResponse.class);
        }
    }

    // Step 2: Generate response from challenge
    // Option A: Simple echo (for testing)
    public String generateResponseSimple(String challenge) {
        return challenge;
    }

    // Option B: HMAC-SHA256 (recommended)
    public String generateResponseHMAC(String challenge) throws Exception {
        Mac mac = Mac.getInstance("HmacSHA256");
        SecretKeySpec secretKeySpec = new SecretKeySpec(
            secret.getBytes(StandardCharsets.UTF_8),
            "HmacSHA256"
        );
        mac.init(secretKeySpec);
        byte[] hash = mac.doFinal(challenge.getBytes(StandardCharsets.UTF_8));
        
        // Convert to hex string
        StringBuilder hexString = new StringBuilder();
        for (byte b : hash) {
            String hex = Integer.toHexString(0xff & b);
            if (hex.length() == 1) {
                hexString.append('0');
            }
            hexString.append(hex);
        }
        return hexString.toString();
    }

    // Option C: SHA256 Hash
    public String generateResponseHash(String challenge) throws Exception {
        MessageDigest digest = MessageDigest.getInstance("SHA-256");
        byte[] hash = digest.digest((challenge + secret).getBytes(StandardCharsets.UTF_8));
        
        StringBuilder hexString = new StringBuilder();
        for (byte b : hash) {
            String hex = Integer.toHexString(0xff & b);
            if (hex.length() == 1) {
                hexString.append('0');
            }
            hexString.append(hex);
        }
        return hexString.toString();
    }

    // Step 3: Validate the response
    public ValidationResponse validate(String challenge, String response) throws Exception {
        JsonObject requestBody = new JsonObject();
        requestBody.addProperty("key", apiKey);
        requestBody.addProperty("challenge", challenge);
        requestBody.addProperty("response", response);

        RequestBody body = RequestBody.create(
            requestBody.toString(),
            MediaType.parse("application/json")
        );

        Request request = new Request.Builder()
            .url(API_BASE_URL + "/challenge/validate")
            .post(body)
            .build();

        try (Response httpResponse = client.newCall(request).execute()) {
            String responseBody = httpResponse.body().string();
            ValidationResponse result = gson.fromJson(responseBody, ValidationResponse.class);
            
            if (!httpResponse.isSuccessful()) {
                result.valid = false;
            }
            
            return result;
        }
    }

    // Complete example
    public ValidationResponse validateChallenge(String challenge, String method) throws Exception {
        String response;
        switch (method) {
            case "echo":
                response = generateResponseSimple(challenge);
                break;
            case "hmac":
                response = generateResponseHMAC(challenge);
                break;
            case "hash":
                response = generateResponseHash(challenge);
                break;
            default:
                throw new IllegalArgumentException("Unknown method: " + method);
        }
        return validate(challenge, response);
    }

    // Response classes
    public static class ChallengeResponse {
        public String challenge;
        public int expires_in;
    }

    public static class ValidationResponse {
        public boolean valid;
        public String error;
        public String signature;
    }
}

Usage Example

java
public class Example {
    public static void main(String[] args) {
        try {
            KeyClaimClient client = new KeyClaimClient(
                "kc_your_api_key",
                "your-secret-key"
            );

            // Create challenge
            KeyClaimClient.ChallengeResponse challengeResp = client.createChallenge(30);
            String challenge = challengeResp.challenge;
            System.out.println("Challenge: " + challenge);

            // Generate response
            String response = client.generateResponseHMAC(challenge);
            System.out.println("Response: " + response);

            // Validate
            KeyClaimClient.ValidationResponse result = client.validate(challenge, response);
            System.out.println("Valid: " + result.valid);
            
            if (result.valid) {
                System.out.println("✓ Challenge validated successfully!");
                if (result.signature != null) {
                    System.out.println("Signature: " + result.signature);
                }
            } else {
                System.out.println("✗ Validation failed: " + result.error);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

Response Generation Methods

Method 1: Echo (Testing)

java
public String generateResponseEcho(String challenge) {
    return challenge;
}

Method 2: HMAC-SHA256 (Recommended)

java
public String generateResponseHMAC(String challenge) throws Exception {
    Mac mac = Mac.getInstance("HmacSHA256");
    SecretKeySpec secretKeySpec = new SecretKeySpec(
        secret.getBytes(StandardCharsets.UTF_8),
        "HmacSHA256"
    );
    mac.init(secretKeySpec);
    byte[] hash = mac.doFinal(challenge.getBytes(StandardCharsets.UTF_8));
    return bytesToHex(hash);
}

private String bytesToHex(byte[] bytes) {
    StringBuilder hexString = new StringBuilder();
    for (byte b : bytes) {
        String hex = Integer.toHexString(0xff & b);
        if (hex.length() == 1) {
            hexString.append('0');
        }
        hexString.append(hex);
    }
    return hexString.toString();
}

Method 3: SHA256 Hash

java
public String generateResponseHash(String challenge) throws Exception {
    MessageDigest digest = MessageDigest.getInstance("SHA-256");
    byte[] hash = digest.digest((challenge + secret).getBytes(StandardCharsets.UTF_8));
    return bytesToHex(hash);
}