CryptoUtility
Overview
The CryptoUtility is a singleton that provides application-level cryptographic operations. It wraps the IGNIS Framework's AES helper (AES-256-GCM mode) and hash function to offer two capabilities:
- Symmetric encryption/decryption -- using
APP_ENV_APPLICATION_SECRETas the key, for storing sensitive data like payment credentials in the database. - HMAC-SHA256 signing -- for webhook payload integrity verification.
Source: packages/core/src/utilities/crypto.utility.ts (53 lines)
Class Definition
import { AES, applicationEnvironment, EnvironmentKeys, hash } from '@venizia/ignis';
export interface ISignOptions {
timestamp: number;
eventType: string;
parts: Array<string>;
secret: string;
}
export class CryptoUtility {
private static _instance: CryptoUtility;
private readonly _aes: AES;
private readonly _encryptionKey: string;
private constructor() {
this._aes = AES.withAlgorithm('aes-256-gcm');
this._encryptionKey = applicationEnvironment.get<string>(
EnvironmentKeys.APP_ENV_APPLICATION_SECRET,
);
}
static getInstance(): CryptoUtility;
encrypt(text: string): string;
decrypt(encryptedText: string): string;
sign(opts: ISignOptions): string;
}Configuration
| Variable | Required | Description |
|---|---|---|
APP_ENV_APPLICATION_SECRET | Yes | The secret key used for AES-256-GCM encryption. Must be set before CryptoUtility.getInstance() is called. |
# .env.development
APP_ENV_APPLICATION_SECRET=my-application-secret-key-at-least-32-charsWARNING
The APP_ENV_APPLICATION_SECRET is used as the AES-256-GCM key. If you change it, all previously encrypted data becomes unrecoverable. Treat this value as critical infrastructure.
Import
import { CryptoUtility } from '@nx/core';
// or
import { CryptoUtility } from '@nx/core/utilities';API
getInstance()
Returns the singleton instance. On first call, initializes the AES cipher with APP_ENV_APPLICATION_SECRET.
const crypto = CryptoUtility.getInstance();encrypt(text)
Encrypts a plaintext string using AES-256-GCM with the application secret. Returns a base64-encoded ciphertext string containing the IV, ciphertext, and GCM authentication tag.
const crypto = CryptoUtility.getInstance();
const encrypted = crypto.encrypt('my-api-key-12345');
// "base64-encoded-iv+ciphertext+authtag"decrypt(encryptedText)
Decrypts a previously encrypted string back to plaintext. Throws if the ciphertext was tampered with (GCM authentication failure) or if the wrong key is used.
const crypto = CryptoUtility.getInstance();
const original = crypto.decrypt(encrypted);
// "my-api-key-12345"sign(opts)
Creates an HMAC-SHA256 signature for webhook payload verification. Joins all parts with | (pipe) and hashes with the provided secret.
Signature format: timestamp|eventType|part1|part2|... hashed with SHA256, output as base64.
const crypto = CryptoUtility.getInstance();
const signature = crypto.sign({
timestamp: 1705708800000,
eventType: 'payment.success',
parts: ['txn_abc123', '50000', 'VND'],
secret: 'webhook-shared-secret',
});
// Base64-encoded HMAC-SHA256 of "1705708800000|payment.success|txn_abc123|50000|VND"ISignOptions
| Property | Type | Description |
|---|---|---|
timestamp | number | Unix timestamp (milliseconds) for replay protection |
eventType | string | Event type identifier (e.g., payment.success) |
parts | string[] | Additional payload parts to include in the signature |
secret | string | HMAC secret key (typically the webhook's shared secret) |
API Summary
| Method | Signature | Description |
|---|---|---|
getInstance | (): CryptoUtility | Get singleton instance |
encrypt | (text: string): string | AES-256-GCM encrypt with application secret |
decrypt | (encryptedText: string): string | AES-256-GCM decrypt with application secret |
sign | (opts: ISignOptions): string | HMAC-SHA256 sign a structured payload |
Usage Examples
Encrypt Payment Credentials
The @nx/payment package uses CryptoUtility to encrypt payment provider credentials before storing them in the database, and decrypt them when loading configurations:
import { CryptoUtility } from '@nx/core';
// Encrypt credentials before storing
const crypto = CryptoUtility.getInstance();
const encryptedApiKey = crypto.encrypt(vnpayApiKey);
const encryptedSecretKey = crypto.encrypt(vnpaySecretKey);
await configurationRepository.create({
data: {
key: 'VNPAY_QR_MMS',
value: JSON.stringify({
apiKey: encryptedApiKey,
secretKey: encryptedSecretKey,
}),
},
});// Decrypt credentials when loading
const config = await configurationRepository.findByKey('VNPAY_QR_MMS');
const parsed = JSON.parse(config.value);
const apiKey = crypto.decrypt(parsed.apiKey);
const secretKey = crypto.decrypt(parsed.secretKey);Sign Webhook Payloads
The @nx/payment webhook dispatcher uses sign() to generate signatures that webhook consumers can verify:
import { CryptoUtility } from '@nx/core';
const crypto = CryptoUtility.getInstance();
// Generate signature for outgoing webhook
const timestamp = Date.now();
const signature = crypto.sign({
timestamp,
eventType: 'mq-pay:attempt.success',
parts: [transactionId, attemptId, amount.toString()],
secret: webhookConfig.secret,
});
// Include in webhook headers
const headers = {
'X-Webhook-Signature': signature,
'X-Webhook-Timestamp': timestamp.toString(),
};Verify Incoming Webhook Signatures
On the receiving end, recompute the signature and compare:
import { CryptoUtility } from '@nx/core';
function verifyWebhookSignature(req: Request, secret: string): boolean {
const crypto = CryptoUtility.getInstance();
const receivedSignature = req.headers.get('X-Webhook-Signature');
const timestamp = Number(req.headers.get('X-Webhook-Timestamp'));
const body = req.body;
const expectedSignature = crypto.sign({
timestamp,
eventType: body.eventType,
parts: [body.transactionId, body.attemptId, body.amount.toString()],
secret,
});
return receivedSignature === expectedSignature;
}Architecture
IGNIS Framework Reference
CryptoUtility uses two primitives from the IGNIS Framework:
AESclass -- Provides AES-256-GCM authenticated encryption. See IGNIS Crypto Helper for the full AES API, including file encryption, custom IV, and encoding options.hash()function -- Provides HMAC-SHA256 and MD5 hashing. See IGNIS Crypto Utility for usage details.