Encryption
Shade402 provides RSA encryption utilities for resource field privacy.
Overview
Resource encryption prevents payment request replay attacks by ensuring the resource field is encrypted and can only be decrypted by the server.
Generating Keys
Generate an RSA key pair:
import { generateKeyPair } from '@shade402/core';
const { publicKey, privateKey } = generateKeyPair();
// Keys are in PEM format
console.log(publicKey);
// -----BEGIN PUBLIC KEY-----
// ...
// -----END PUBLIC KEY-----
console.log(privateKey);
// -----BEGIN PRIVATE KEY-----
// ...
// -----END PRIVATE KEY-----Store keys securely:
Public key: Can be shared (included in 402 responses)
Private key: Must be kept secret (server-side only)
Encrypting Resources
Encrypt a resource string using the server's public key:
import { encryptResource } from '@shade402/core';
const plaintext = '/api/premium-data';
const encrypted = encryptResource(plaintext, serverPublicKey);
console.log('Encrypted:', encrypted); // Base64-encodedThe encryption uses:
RSA-OAEP padding
SHA-256 hash algorithm
2048-bit keys
Decrypting Resources
Decrypt an encrypted resource using the server's private key:
import { decryptResource } from '@shade402/core';
const decrypted = decryptResource(encrypted, serverPrivateKey);
console.log('Decrypted:', decrypted); // '/api/premium-data'Server-side Setup
Generate and Store Keys
import { generateKeyPair } from '@shade402/core';
import fs from 'fs';
// Generate keys (do this once)
const { publicKey, privateKey } = generateKeyPair();
// Store keys securely
fs.writeFileSync('keys/public.pem', publicKey);
fs.writeFileSync('keys/private.pem', privateKey);Load Keys in Application
import fs from 'fs';
const publicKey = fs.readFileSync('keys/public.pem', 'utf8');
const privateKey = fs.readFileSync('keys/private.pem', 'utf8');
initX402({
// ... other config ...
encryptionPublicKey: publicKey,
encryptionPrivateKey: privateKey,
});Using Environment Variables
// Load from environment variables
initX402({
// ... other config ...
encryptionPublicKey: process.env.ENCRYPTION_PUBLIC_KEY!,
encryptionPrivateKey: process.env.ENCRYPTION_PRIVATE_KEY!,
});Client-side Usage
The client automatically handles encryption:
import { X402AutoClient } from '@shade402/client';
const client = new X402AutoClient(wallet);
// Client automatically:
// 1. Receives public key from 402 response
// 2. Encrypts resource field
// 3. Includes encrypted resource in retry request
const response = await client.get(url);Manual Encryption (Explicit Client)
import { X402Client } from '@shade402/client';
const client = new X402Client(wallet);
// Get 402 response
let response = await client.get(url);
const paymentRequest = client.parsePaymentRequest(response);
// Encrypt resource
const encryptedResource = client.encryptResource(paymentRequest.resource);
// Retry with encrypted resource
response = await client.get(url, {
payment: authorization,
encryptedResource: encryptedResource,
});Complete Example
Server
import { initX402, paymentRequired } from '@shade402/express';
import { generateKeyPair } from '@shade402/core';
// Generate keys (once)
const { publicKey, privateKey } = generateKeyPair();
initX402({
paymentAddress: process.env.PAYMENT_WALLET_ADDRESS!,
tokenMint: process.env.TOKEN_MINT!,
network: 'solana-devnet',
encryptionPublicKey: publicKey,
encryptionPrivateKey: privateKey,
});
app.get('/api/premium-data',
paymentRequired({ amount: '0.01' }),
(req, res) => {
// req.decryptedResource contains decrypted resource
console.log('Decrypted resource:', req.decryptedResource);
res.json({ data: 'Premium data' });
}
);Client
import { X402AutoClient } from '@shade402/client';
const client = new X402AutoClient(wallet);
// Encryption handled automatically
const response = await client.get('https://api.example.com/premium-data');Security Considerations
Keep private key secure: Never expose the private key
Use strong keys: 2048-bit keys are recommended
Rotate keys periodically: Generate new keys and update configuration
Use secure storage: Store keys in environment variables or key management service
Separate keys per environment: Use different keys for dev/staging/production
Key Management
Using Key Management Service
import { SecretsManager } from 'aws-sdk';
async function loadKeys() {
const sm = new SecretsManager();
const publicKey = await sm.getSecretValue({
SecretId: 'x402-encryption-public-key',
}).promise();
const privateKey = await sm.getSecretValue({
SecretId: 'x402-encryption-private-key',
}).promise();
return {
publicKey: publicKey.SecretString!,
privateKey: privateKey.SecretString!,
};
}Using Environment Variables
# .env
ENCRYPTION_PUBLIC_KEY="-----BEGIN PUBLIC KEY-----\n...\n-----END PUBLIC KEY-----"
ENCRYPTION_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----"Error Handling
import { encryptResource, decryptResource } from '@shade402/core';
try {
const encrypted = encryptResource(plaintext, publicKey);
} catch (error) {
console.error('Encryption failed:', error);
}
try {
const decrypted = decryptResource(encrypted, privateKey);
} catch (error) {
console.error('Decryption failed:', error);
}Best Practices
Generate keys once and reuse them
Store keys securely (environment variables, key management service)
Rotate keys periodically
Use separate keys for different environments
Never commit keys to version control
Monitor for encryption/decryption failures
Test encryption/decryption in your application
Next Steps
Learn about Security best practices
Check out Core Package overview
See Examples for usage
Last updated
