CipherStash vs AWS KMS
A side-by-side comparison of CipherStash Encryption and AWS KMS for application-level encryption
CipherStash vs AWS KMS
Encrypting data shouldn't require managing binary buffers, base64 encoding, key ARNs, or building custom search solutions. CipherStash Encryption eliminates these complexities, giving you encryption that "just works" with a developer-friendly API.
The simple truth: Encrypting a value
Let's start with the most basic operation — encrypting a single value.
AWS KMS: Manual work required
import { KMSClient, EncryptCommand } from '@aws-sdk/client-kms';
const client = new KMSClient({ region: 'us-west-2' });
const keyId = 'arn:aws:kms:us-west-2:123456789012:key/abcd1234-efgh-5678-ijkl-9012mnopqrst';
async function encryptWithKMS(plaintext: string): Promise<string> {
try {
const command = new EncryptCommand({
KeyId: keyId,
Plaintext: Buffer.from(plaintext),
});
const response = await client.send(command);
const ciphertext = response.CiphertextBlob;
const base64Ciphertext = Buffer.from(ciphertext).toString('base64');
return base64Ciphertext;
} catch (error) {
console.error('Error encrypting data:', error);
throw error;
}
}
const encrypted = await encryptWithKMS('secret@squirrel.example');What you're managing: Key ARNs, binary buffer conversions, base64 encoding/decoding, manual error handling, region configuration, AWS credential setup.
CipherStash Encryption: One simple call
import { Encryption } from '@cipherstash/stack';
import { encryptedTable, encryptedColumn } from '@cipherstash/stack/schema';
const users = encryptedTable('users', {
email: encryptedColumn('email'),
});
const client = await Encryption({
schemas: [users],
});
const encryptResult = await client.encrypt(
'secret@squirrel.example',
{ column: users.email, table: users }
);
if (encryptResult.failure) {
throw new Error(encryptResult.failure.message);
}
const ciphertext = encryptResult.data;What you get: No key management (handled by ZeroKMS), no binary conversions, no base64 encoding, type-safe error handling, JSON payload ready for storage, zero-knowledge encryption by default.
Decryption
AWS KMS
async function decryptWithKMS(base64Ciphertext: string): Promise<string> {
try {
const ciphertextBlob = Buffer.from(base64Ciphertext, 'base64');
const command = new DecryptCommand({ CiphertextBlob: ciphertextBlob });
const response = await client.send(command);
return Buffer.from(response.Plaintext).toString('utf-8');
} catch (error) {
console.error('Error decrypting data:', error);
throw error;
}
}CipherStash Encryption
const decryptResult = await client.decrypt(ciphertext);
if (decryptResult.failure) {
throw new Error(decryptResult.failure.message);
}
const plaintext = decryptResult.data;Features that AWS KMS can't do without major custom work
1. Searchable encryption: Built-in vs impossible
AWS KMS requires decrypting everything and searching in memory, storing plaintext indexes, or building a custom searchable encryption solution.
CipherStash Encryption has searchable encryption built-in:
const users = encryptedTable('users', {
email: encryptedColumn('email')
.freeTextSearch()
.equality()
.orderAndRange(),
});
const searchTerms = await client.encryptQuery('secret', {
column: users.email,
table: users,
});2. Identity-aware encryption: Built-in vs custom implementation
AWS KMS has no built-in support. You must implement custom logic with encryption context as a workaround.
CipherStash Encryption has built-in identity-aware encryption:
import { LockContext } from '@cipherstash/stack/identity';
const lc = new LockContext();
const lockContext = await lc.identify(userJwt);
const encryptResult = await client.encrypt(
'secret@squirrel.example',
{ column: users.email, table: users }
).withLockContext(lockContext);
const decryptResult = await client.decrypt(ciphertext)
.withLockContext(lockContext);3. Bulk operations: Native API vs manual batching
AWS KMS has no bulk API — you must manually batch operations and handle rate limits.
CipherStash Encryption has native bulk encryption:
const bulkPlaintexts = [
{ id: '1', plaintext: 'Alice' },
{ id: '2', plaintext: 'Bob' },
{ id: '3', plaintext: 'Charlie' },
];
const bulkResult = await client.bulkEncrypt(bulkPlaintexts, {
column: users.name,
table: users,
});
const encryptedMap = bulkResult.data;Feature comparison
| Feature | AWS KMS | CipherStash Encryption |
|---|---|---|
| Basic Encryption | Requires manual buffer/base64 handling | One-line API, JSON payload |
| Key Management | You manage key ARNs | Zero-knowledge, automatic |
| Searchable Encryption | Not possible | Built-in for PostgreSQL |
| Identity-Aware Encryption | Custom implementation | Built-in LockContext |
| Bulk Operations | Manual batching | Native bulk API |
| Error Handling | Try/catch, manual types | Type-safe Result pattern |
| Type Safety | Manual typing | Full TypeScript inference |
| Storage Format | Binary (needs encoding) | JSON (database-ready) |
| ORM Integration | Manual integration | Built-in Drizzle support |
| Zero-Knowledge | AWS has key access | True zero-knowledge |
| Setup Complexity | Medium (AWS credentials, regions) | Low (just environment variables) |
When to use each
Use AWS KMS when:
- You need encryption for AWS services (S3, EBS, etc.)
- You're encrypting infrastructure-level resources
- You don't need to search encrypted data
- You're comfortable with manual buffer/base64 handling
Use CipherStash Encryption when:
- You're building applications with databases
- You need to search encrypted data
- You want a developer-friendly API
- You need identity-aware encryption
- You want zero-knowledge key management
- You value type safety and developer experience