Skip to content

CryptoUtility

Tổng quan

CryptoUtility là một singleton cung cấp các thao tác mật mã ở cấp ứng dụng. Nó bọc helper AES của IGNIS Framework (chế độ AES-256-GCM) và hàm hash để cung cấp hai khả năng:

  1. Mã hoá/giải mã đối xứng -- dùng APP_ENV_APPLICATION_SECRET làm khoá, để lưu trữ dữ liệu nhạy cảm như thông tin xác thực thanh toán trong cơ sở dữ liệu.
  2. Ký HMAC-SHA256 -- để xác minh tính toàn vẹn của payload trong webhook.

Nguồn: packages/core/src/utilities/crypto.utility.ts (53 dòng)

Định nghĩa lớp

typescript
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;
}

Cấu hình

BiếnBắt buộcMô tả
APP_ENV_APPLICATION_SECRETKhoá bí mật dùng cho mã hoá AES-256-GCM. Phải được thiết lập trước khi gọi CryptoUtility.getInstance().
bash
# .env.development
APP_ENV_APPLICATION_SECRET=my-application-secret-key-at-least-32-chars

WARNING

APP_ENV_APPLICATION_SECRET được dùng làm khoá AES-256-GCM. Nếu bạn thay đổi nó, toàn bộ dữ liệu đã mã hoá trước đó sẽ không thể khôi phục. Hãy coi giá trị này như hạ tầng trọng yếu.

Import

typescript
import { CryptoUtility } from '@nx/core';
// or
import { CryptoUtility } from '@nx/core/utilities';

API

getInstance()

Trả về instance singleton. Ở lần gọi đầu tiên, nó khởi tạo cipher AES với APP_ENV_APPLICATION_SECRET.

typescript
const crypto = CryptoUtility.getInstance();

encrypt(text)

Mã hoá một chuỗi văn bản thuần bằng AES-256-GCM với application secret. Trả về chuỗi ciphertext mã hoá base64 chứa IV, ciphertext và GCM authentication tag.

typescript
const crypto = CryptoUtility.getInstance();

const encrypted = crypto.encrypt('my-api-key-12345');
// "base64-encoded-iv+ciphertext+authtag"

decrypt(encryptedText)

Giải mã một chuỗi đã mã hoá trước đó về văn bản thuần. Ném lỗi nếu ciphertext bị can thiệp (GCM authentication thất bại) hoặc nếu dùng sai khoá.

typescript
const crypto = CryptoUtility.getInstance();

const original = crypto.decrypt(encrypted);
// "my-api-key-12345"

sign(opts)

Tạo chữ ký HMAC-SHA256 để xác minh payload của webhook. Nối tất cả các phần với | (dấu pipe) rồi băm với secret được cung cấp.

Định dạng chữ ký: timestamp|eventType|part1|part2|... được băm bằng SHA256, đầu ra ở dạng base64.

typescript
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

Thuộc tínhKiểuMô tả
timestampnumberUnix timestamp (mili giây) để chống tấn công phát lại
eventTypestringĐịnh danh loại sự kiện (ví dụ: payment.success)
partsstring[]Các phần payload bổ sung để đưa vào chữ ký
secretstringKhoá bí mật HMAC (thường là shared secret của webhook)

Tóm tắt API

Phương thứcChữ kýMô tả
getInstance(): CryptoUtilityLấy instance singleton
encrypt(text: string): stringMã hoá AES-256-GCM với application secret
decrypt(encryptedText: string): stringGiải mã AES-256-GCM với application secret
sign(opts: ISignOptions): stringKý HMAC-SHA256 cho một payload có cấu trúc

Ví dụ sử dụng

Mã hoá thông tin xác thực thanh toán

Package @nx/payment dùng CryptoUtility để mã hoá thông tin xác thực của nhà cung cấp thanh toán trước khi lưu vào cơ sở dữ liệu, và giải mã chúng khi tải cấu hình:

typescript
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,
    }),
  },
});
typescript
// 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);

Ký payload của webhook

Bộ phát webhook của @nx/payment dùng sign() để tạo chữ ký mà bên tiêu thụ webhook có thể xác minh:

typescript
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(),
};

Xác minh chữ ký webhook đến

Ở phía nhận, tính lại chữ ký rồi so sánh:

typescript
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;
}

Kiến trúc

Tham chiếu IGNIS Framework

CryptoUtility dùng hai primitive từ IGNIS Framework:

  • Lớp AES -- Cung cấp mã hoá xác thực AES-256-GCM. Xem IGNIS Crypto Helper để biết API AES đầy đủ, bao gồm mã hoá file, IV tuỳ chỉnh và các tuỳ chọn encoding.
  • Hàm hash() -- Cung cấp băm HMAC-SHA256 và MD5. Xem IGNIS Crypto Utility để biết chi tiết sử dụng.

Tài liệu liên quan

Proprietary and Confidential. Unauthorized copying, distribution, or use of this software is strictly prohibited.