Back to Documentation
PHP Documentation

PHP Documentation

PHP 7.4+ integration guide

Official GuideProduction Ready

PHP SDK Guide

Installation

bash
composer require guzzlehttp/guzzle
# PHP 8.0+ with OpenSSL extension (for RSA encryption)

Basic Usage

php
<?php
require 'vendor/autoload.php';

use GuzzleHttp\Client;

$apiBaseUrl = 'https://keyclaim.org/api';
$apiKey = 'kc_your_api_key_here';

// Step 1: Create a challenge
function createChallenge($apiKey, $apiBaseUrl) {
    $client = new Client();
    $response = $client->post("$apiBaseUrl/challenge/create", [
        'json' => [
            'key' => $apiKey,
            'ttl' => 30
        ]
    ]);
    
    $data = json_decode($response->getBody(), true);
    return $data['challenge'];
}

// Step 2: Generate response from challenge
// Option A: HMAC-based response (recommended for backend)
function generateResponseHMAC($challenge, $secret) {
    return hash_hmac('sha256', $challenge, $secret);
}

// Option B: RSA Encryption-based (for frontend with private key)
function decryptChallenge($encryptedChallenge, $privateKeyPem) {
    $encrypted = base64_decode($encryptedChallenge);
    $decrypted = '';
    
    if (!openssl_private_decrypt(
        $encrypted,
        $decrypted,
        $privateKeyPem,
        OPENSSL_PKCS1_OAEP_PADDING
    )) {
        throw new Exception('Failed to decrypt challenge');
    }
    
    return $decrypted;
}

// Step 3: Validate the response
function validateChallenge($apiKey, $apiBaseUrl, $challenge, $response, $decryptedChallenge = null) {
    $client = new Client();
    
    $payload = [
        'key' => $apiKey,
        'challenge' => $challenge,
        'response' => $response
    ];
    
    if ($decryptedChallenge !== null) {
        $payload['decryptedChallenge'] = $decryptedChallenge;
    }
    
    $response = $client->post("$apiBaseUrl/challenge/validate", [
        'json' => $payload
    ]);
    
    return json_decode($response->getBody(), true);
}

// Complete example (HMAC)
try {
    // Create challenge
    $challenge = createChallenge($apiKey, $apiBaseUrl);
    echo "Challenge: $challenge\n";
    
    // Generate response
    $secret = $apiKey;
    $response = generateResponseHMAC($challenge, $secret);
    echo "Response: $response\n";
    
    // Validate
    $result = validateChallenge($apiKey, $apiBaseUrl, $challenge, $response);
    print_r($result);
    
    if ($result['valid']) {
        echo "✓ Challenge validated successfully!\n";
        if (isset($result['signature'])) {
            echo "Signature: {$result['signature']}\n";
        }
    } else {
        echo "✗ Validation failed\n";
    }
} catch (Exception $e) {
    echo "Error: " . $e->getMessage() . "\n";
}

Complete SDK Class

php
<?php
require 'vendor/autoload.php';

use GuzzleHttp\Client;
use GuzzleHttp\Exception\RequestException;

class KeyClaimClient {
    private $apiKey;
    private $baseUrl;
    private $client;
    private $privateKeyPem;
    
    public function __construct($apiKey, $baseUrl = 'https://keyclaim.org/api', $privateKeyPem = null) {
        $this->apiKey = $apiKey;
        $this->baseUrl = $baseUrl;
        $this->client = new Client();
        $this->privateKeyPem = $privateKeyPem;
    }
    
    public function createChallenge($ttl = 30) {
        try {
            $response = $this->client->post("$this->baseUrl/challenge/create", [
                'headers' => [
                    'Authorization' => 'Bearer ' . $this->apiKey,
                    'Content-Type' => 'application/json',
                ],
                'json' => [
                    'ttl' => $ttl
                ]
            ]);
            
            return json_decode($response->getBody(), true);
        } catch (RequestException $e) {
            throw new Exception('Failed to create challenge: ' . $e->getMessage());
        }
    }
    
    public function generateResponseHMAC($challenge) {
        return hash_hmac('sha256', $challenge, $this->apiKey);
    }
    
    public function decryptChallenge($encryptedChallenge) {
        if (!$this->privateKeyPem) {
            throw new Exception('Private key not provided');
        }
        
        $encrypted = base64_decode($encryptedChallenge);
        $decrypted = '';
        
        if (!openssl_private_decrypt(
            $encrypted,
            $decrypted,
            $this->privateKeyPem,
            OPENSSL_PKCS1_OAEP_PADDING
        )) {
            throw new Exception('Failed to decrypt challenge');
        }
        
        return $decrypted;
    }
    
    public function validate($challenge, $response, $decryptedChallenge = null) {
        try {
            $payload = [
                'key' => $this->apiKey,
                'challenge' => $challenge,
                'response' => $response
            ];
            
            if ($decryptedChallenge !== null) {
                $payload['decryptedChallenge'] = $decryptedChallenge;
            }
            
            $response = $this->client->post("$this->baseUrl/challenge/validate", [
                'json' => $payload
            ]);
            
            return json_decode($response->getBody(), true);
        } catch (RequestException $e) {
            throw new Exception('Failed to validate: ' . $e->getMessage());
        }
    }
    
    public function validateChallenge($challenge, $method = 'hmac') {
        if ($method === 'hmac') {
            $response = $this->generateResponseHMAC($challenge);
            return $this->validate($challenge, $response);
        } elseif ($method === 'rsa' && $this->privateKeyPem) {
            $decrypted = $this->decryptChallenge($challenge);
            return $this->validate($challenge, $decrypted, $decrypted);
        } else {
            throw new Exception("Unknown method: $method");
        }
    }
}

// Usage
$client = new KeyClaimClient('kc_your_api_key', 'https://keyclaim.org/api');

// HMAC method
$result = $client->createChallenge();
$challenge = $result['challenge'];
$validation = $client->validateChallenge($challenge, 'hmac');
print_r($validation);

// RSA method (with private key)
// If API key has a key pair assigned, challenge will be automatically encrypted
// $privateKey = file_get_contents('private_key.pem');
// $clientRSA = new KeyClaimClient('kc_your_api_key', 'https://keyclaim.org/api', $privateKey);
// $result = $clientRSA->createChallenge(30);
// $challenge = $result['challenge'];
// $validation = $clientRSA->validateChallenge($challenge, 'rsa');
// print_r($validation);

RSA Key Pair Generation

php
<?php

function generateRSAKeyPair($keySize = 2048) {
    $config = [
        "digest_alg" => "sha256",
        "private_key_bits" => $keySize,
        "private_key_type" => OPENSSL_KEYTYPE_RSA,
    ];
    
    // Generate private key
    $resource = openssl_pkey_new($config);
    if (!$resource) {
        throw new Exception('Failed to generate key pair');
    }
    
    // Export private key
    openssl_pkey_export($resource, $privateKeyPem);
    
    // Get public key
    $publicKeyDetails = openssl_pkey_get_details($resource);
    $publicKeyPem = $publicKeyDetails['key'];
    
    return [
        'private_key' => $privateKeyPem,
        'public_key' => $publicKeyPem
    ];
}

// Generate and save
$keyPair = generateRSAKeyPair(2048);

file_put_contents('private_key.pem', $keyPair['private_key']);
file_put_contents('public_key.pem', $keyPair['public_key']);

echo "Key pair generated!\n";
echo "Private key saved to: private_key.pem\n";
echo "Public key saved to: public_key.pem\n";
echo "\n⚠️  Keep your private key secure!\n";

Laravel Integration

php
<?php
// app/Services/KeyClaimService.php

namespace App\Services;

use GuzzleHttp\Client;

class KeyClaimService {
    private $client;
    private $apiKey;
    private $baseUrl;
    
    public function __construct() {
        $this->client = new Client();
        $this->apiKey = config('services.keyclaim.api_key');
        $this->baseUrl = config('services.keyclaim.base_url', 'https://keyclaim.org/api');
    }
    
    public function createChallenge($ttl = 30) {
        $response = $this->client->post("$this->baseUrl/challenge/create", [
            'json' => [
                'key' => $this->apiKey,
                'ttl' => $ttl
            ]
        ]);
        
        return json_decode($response->getBody(), true);
    }
    
    public function validate($challenge, $response) {
        $response = $this->client->post("$this->baseUrl/challenge/validate", [
            'json' => [
                'key' => $this->apiKey,
                'challenge' => $challenge,
                'response' => $response
            ]
        ]);
        
        return json_decode($response->getBody(), true);
    }
}

// config/services.php
return [
    'keyclaim' => [
        'api_key' => env('KEYCLAIM_API_KEY'),
        'base_url' => env('KEYCLAIM_BASE_URL', 'https://keyclaim.org/api'),
    ],
];

// Usage in Controller
use App\Services\KeyClaimService;

$keyClaim = new KeyClaimService();
$challenge = $keyClaim->createChallenge();