← Back to Documentation

RSA Public-Private Key Pair Guide

Complete guide to using RSA encryption with KeyClaim for enhanced security

Overview

This guide explains how to use RSA public-private key encryption with KeyClaim to enhance security. The flow works as follows:

  1. Generate a public-private key pair
  2. Store the private key securely on your client (never send it to the server)
  3. Upload the public key to KeyClaim server
  4. Server encrypts challenges with your public key
  5. Client decrypts challenges with your private key
  6. Continue with normal HMAC validation flow

Why Use RSA Encryption?

  • Enhanced Security: Challenges are encrypted, making them useless even if intercepted
  • MITM Protection: Attackers cannot read encrypted challenges without the private key
  • Forward Secrecy: Each challenge is unique and encrypted independently

Step 1: Generate Key Pair

Option A: Using KeyClaim Dashboard (Recommended)

  1. Log in to your KeyClaim dashboard
  2. Navigate to API KeysKey Pairs
  3. Click Generate New Key Pair
  4. Choose key size (2048 or 4096 bits recommended)
  5. Click Generate
  6. IMPORTANT: Save your private key immediately - it's only shown once!

Step 2: Store Your Private Key Securely

⚠️ CRITICAL SECURITY RULES:

  • NEVER send your private key to the server
  • NEVER commit private keys to version control
  • NEVER share private keys publicly
  • ALWAYS store private keys in secure storage (keychain, secure vault, environment variables)

Storage Options by Platform:

Web Applications

  • • Browser: Web Crypto API
  • • Environment Variables
  • • Secure Vault Services

Mobile Applications

  • • iOS: Keychain Services
  • • Android: Keystore System
  • • React Native: react-native-keychain

Backend Applications

  • • Environment Variables
  • • Encrypted Files
  • • Cloud Secrets

Step 3: Upload Public Key to Server

The public key is safe to share and should be stored on the KeyClaim server.

Using Dashboard:

If generated via dashboard, the public key is automatically saved. If generated locally, use the Upload Public Key feature.

Using API:

POST /api/keys/generate-keypair Authorization: Bearer YOUR_TOKEN { "name": "My Key Pair", "keySize": 2048 }

Step 4: Associate Public Key with API Key

  1. Go to API Keys in dashboard
  2. Select your API key
  3. Choose the key pair from dropdown
  4. Save

Step 5: Create Challenge (Automatic Encryption)

When creating a challenge, if your API key has a key pair assigned, the challenge will be automatically encrypted:

POST /api/challenge/create Authorization: Bearer YOUR_API_KEY Content-Type: application/json { "ttl": 30 }

Response:

{ "challenge": "base64_encrypted_challenge_here", "expires_in": 30 }

Note: Encryption is automatic if a key pair is assigned to your API key. No need to specify encrypted: true - it happens automatically.

Step 6: Decrypt Challenge on Client

Decrypt the challenge using your private key. See platform-specific examples below:

JavaScript/TypeScript (Web)

async function decryptChallenge(encryptedChallenge, privateKeyPEM) { // Import private key const privateKey = await crypto.subtle.importKey( 'pkcs8', pemToArrayBuffer(privateKeyPEM), { name: 'RSA-OAEP', hash: 'SHA-256', }, false, ['decrypt'] ) // Decrypt const encryptedBuffer = base64ToArrayBuffer(encryptedChallenge) const decryptedBuffer = await crypto.subtle.decrypt( { name: 'RSA-OAEP' }, privateKey, encryptedBuffer ) return new TextDecoder().decode(decryptedBuffer) }

Node.js

const crypto = require('crypto') function decryptChallenge(encryptedChallenge, privateKeyPEM) { const encryptedBuffer = Buffer.from(encryptedChallenge, 'base64') const decrypted = crypto.privateDecrypt( { key: privateKeyPEM, padding: crypto.constants.RSA_PKCS1_OAEP_PADDING, oaepHash: 'sha256', }, encryptedBuffer ) return decrypted.toString('utf8') }

Python

from cryptography.hazmat.primitives import serialization from cryptography.hazmat.primitives.asymmetric import padding from cryptography.hazmat.primitives import hashes import base64 def decrypt_challenge(encrypted_challenge, private_key_pem): private_key = serialization.load_pem_private_key( private_key_pem.encode('utf-8'), password=None ) encrypted_bytes = base64.b64decode(encrypted_challenge) decrypted = private_key.decrypt( encrypted_bytes, padding.OAEP( mgf=padding.MGF1(algorithm=hashes.SHA256()), algorithm=hashes.SHA256(), label=None ) ) return decrypted.decode('utf-8')

Note: For more platform-specific examples (PHP, Java, Kotlin, C#, Go, Android, iOS, React Native), see the complete guide in the repository or contact support.

Step 7: Continue with Normal Flow

After decrypting the challenge, continue with the standard HMAC validation:

// 1. Decrypt challenge const decryptedChallenge = await decryptChallenge(encryptedChallenge, privateKey) // 2. Generate HMAC response const response = await generateHMACResponse(decryptedChallenge, apiKey) // 3. Validate const validation = await fetch('/api/challenge/validate', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${apiKey}` }, body: JSON.stringify({ challenge: encryptedChallenge, // Send encrypted challenge response: response, decryptedChallenge: decryptedChallenge // Server verifies this matches }) })

Complete Example Flow

1. Generate Key Pair (One Time)

// Via dashboard or API const keyPair = await generateKeyPair() // Save privateKey securely // Public key is stored on server

2. Create Challenge (Automatic Encryption)

const response = await fetch('/api/challenge/create', { method: 'POST', headers: { 'Authorization': `Bearer ${API_KEY}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ ttl: 30 }) }) // If API key has a key pair assigned, challenge will be automatically encrypted const { challenge: encryptedChallenge } = await response.json()

3. Decrypt Challenge

const decryptedChallenge = await decryptChallenge( encryptedChallenge, PRIVATE_KEY )

4. Generate HMAC Response

const hmacResponse = await generateHMACResponse( decryptedChallenge, API_KEY )

5. Validate

const validation = await fetch('/api/challenge/validate', { method: 'POST', headers: { 'Authorization': `Bearer ${API_KEY}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ challenge: encryptedChallenge, response: hmacResponse, decryptedChallenge: decryptedChallenge }) }) const result = await validation.json() // { valid: true, ... }

Security Best Practices

✅ DO:

  • • Store private keys in secure keychain/keystore
  • • Use environment variables for private keys
  • • Rotate keys periodically
  • • Use 2048-bit or larger keys
  • • Never log private keys
  • • Use HTTPS for all API calls

❌ DON'T:

  • • Commit private keys to version control
  • • Send private keys to the server
  • • Share private keys between applications
  • • Use weak key sizes (< 2048 bits)
  • • Store private keys in plain text files
  • • Log or print private keys

FAQ

Q: Can I use the same key pair for multiple API keys?

A: Yes, but it's recommended to use separate key pairs for better security isolation.

Q: What happens if I lose my private key?

A: You'll need to generate a new key pair and update your API key association. The old public key will be removed.

Q: Can I regenerate the private key from the public key?

A: No, that's cryptographically impossible. Always backup your private key securely.

Q: How often should I rotate keys?

A: Recommended: Every 6-12 months, or immediately if compromised.