Kotlin SDK Guide
Dependencies
Add to your build.gradle.kts:
kotlin
dependencies {
implementation("com.squareup.okhttp3:okhttp:4.12.0")
implementation("com.google.code.gson:gson:2.10.1")
}Basic Usage
kotlin
import okhttp3.*
import com.google.gson.Gson
import com.google.gson.JsonObject
import java.security.MessageDigest
import javax.crypto.Mac
import javax.crypto.spec.SecretKeySpec
class KeyClaimClient(
private val apiKey: String,
private val secret: String = apiKey,
private val baseUrl: String = "https://keyclaim.org/api"
) {
private val client = OkHttpClient()
private val gson = Gson()
data class ChallengeResponse(
val challenge: String,
val expires_in: Int
)
data class ValidationResponse(
val valid: Boolean,
val error: String? = null,
val signature: String? = null
)
// Step 1: Create a challenge
suspend fun createChallenge(ttl: Int = 30): ChallengeResponse {
val requestBody = JsonObject().apply {
addProperty("key", apiKey)
addProperty("ttl", ttl)
}
val body = RequestBody.create(
requestBody.toString().toByteArray(),
MediaType.parse("application/json")
)
val request = Request.Builder()
.url("$baseUrl/challenge/create")
.post(body)
.build()
client.newCall(request).execute().use { response ->
if (!response.isSuccessful) {
throw Exception("Failed to create challenge: ${response.code}")
}
val responseBody = response.body?.string() ?: throw Exception("Empty response")
return gson.fromJson(responseBody, ChallengeResponse::class.java)
}
}
// Step 2: Generate response from challenge
// Option A: Simple echo (for testing)
fun generateResponseSimple(challenge: String): String {
return challenge
}
// Option B: HMAC-SHA256 (recommended)
fun generateResponseHMAC(challenge: String): String {
val mac = Mac.getInstance("HmacSHA256")
val secretKeySpec = SecretKeySpec(
secret.toByteArray(Charsets.UTF_8),
"HmacSHA256"
)
mac.init(secretKeySpec)
val hash = mac.doFinal(challenge.toByteArray(Charsets.UTF_8))
return hash.joinToString("") { "%02x".format(it) }
}
// Option C: SHA256 Hash
fun generateResponseHash(challenge: String): String {
val digest = MessageDigest.getInstance("SHA-256")
val hash = digest.digest((challenge + secret).toByteArray(Charsets.UTF_8))
return hash.joinToString("") { "%02x".format(it) }
}
// Step 3: Validate the response
suspend fun validate(challenge: String, response: String): ValidationResponse {
val requestBody = JsonObject().apply {
addProperty("key", apiKey)
addProperty("challenge", challenge)
addProperty("response", response)
}
val body = RequestBody.create(
requestBody.toString().toByteArray(),
MediaType.parse("application/json")
)
val request = Request.Builder()
.url("$baseUrl/challenge/validate")
.post(body)
.build()
client.newCall(request).execute().use { httpResponse ->
val responseBody = httpResponse.body?.string() ?: return ValidationResponse(
valid = false,
error = "Empty response"
)
val result = gson.fromJson(responseBody, ValidationResponse::class.java)
return if (httpResponse.isSuccessful) {
result
} else {
result.copy(valid = false)
}
}
}
// Complete example
suspend fun validateChallenge(challenge: String, method: String = "hmac"): ValidationResponse {
val response = when (method) {
"echo" -> generateResponseSimple(challenge)
"hmac" -> generateResponseHMAC(challenge)
"hash" -> generateResponseHash(challenge)
else -> throw IllegalArgumentException("Unknown method: $method")
}
return validate(challenge, response)
}
}Usage Example
kotlin
import kotlinx.coroutines.runBlocking
fun main() = runBlocking {
val client = KeyClaimClient(
apiKey = "kc_your_api_key",
secret = "your-secret-key"
)
try {
// Create challenge
val challengeResp = client.createChallenge(ttl = 30)
val challenge = challengeResp.challenge
println("Challenge: $challenge")
// Generate response
val response = client.generateResponseHMAC(challenge)
println("Response: $response")
// Validate
val result = client.validate(challenge, response)
println("Valid: ${result.valid}")
if (result.valid) {
println("✓ Challenge validated successfully!")
result.signature?.let { println("Signature: $it") }
} else {
println("✗ Validation failed: ${result.error}")
}
} catch (e: Exception) {
e.printStackTrace()
}
}Response Generation Methods
Method 1: Echo (Testing)
kotlin
fun generateResponseEcho(challenge: String): String = challengeMethod 2: HMAC-SHA256 (Recommended)
kotlin
fun generateResponseHMAC(challenge: String): String {
val mac = Mac.getInstance("HmacSHA256")
val secretKeySpec = SecretKeySpec(
secret.toByteArray(Charsets.UTF_8),
"HmacSHA256"
)
mac.init(secretKeySpec)
val hash = mac.doFinal(challenge.toByteArray(Charsets.UTF_8))
return hash.joinToString("") { "%02x".format(it) }
}Method 3: SHA256 Hash
kotlin
fun generateResponseHash(challenge: String): String {
val digest = MessageDigest.getInstance("SHA-256")
val hash = digest.digest((challenge + secret).toByteArray(Charsets.UTF_8))
return hash.joinToString("") { "%02x".format(it) }
}Explore Other Documentation
Choose your preferred programming language

Node.js Documentation
JavaScript/TypeScript integration guide
View Guide→

Python Documentation
Python 3.7+ integration guide
View Guide→

PHP Documentation
PHP 7.4+ integration guide
View Guide→

Java Documentation
Java 8+ integration guide
View Guide→
C# Documentation
.NET/C# integration guide
View Guide→

Go Documentation
Go 1.18+ integration guide
View Guide→
