feat: Goa GEL Blockchain e-Licensing Platform - Full Stack Implementation
Complete implementation of the Goa Government e-Licensing platform with: Backend: - NestJS API with JWT authentication - PostgreSQL database with Knex ORM - Redis caching and session management - MinIO document storage - Hyperledger Besu blockchain integration - Multi-department workflow system - Comprehensive API tests (266/282 passing) Frontend: - Angular 21 with standalone components - Angular Material + TailwindCSS UI - Visual workflow builder - Document upload with progress tracking - Blockchain explorer integration - Role-based dashboards (Admin, Department, Citizen) - E2E tests with Playwright (37 tests) Infrastructure: - Docker Compose orchestration - Blockscout blockchain explorer - Development and production configurations
This commit is contained in:
83
backend/src/common/utils/crypto.util.ts
Normal file
83
backend/src/common/utils/crypto.util.ts
Normal file
@@ -0,0 +1,83 @@
|
||||
import { createCipheriv, createDecipheriv, randomBytes, scryptSync } from 'crypto';
|
||||
import * as bcrypt from 'bcrypt';
|
||||
import * as crypto from 'crypto';
|
||||
|
||||
export async function hash(data: string): Promise<string> {
|
||||
return bcrypt.hash(data, 10);
|
||||
}
|
||||
|
||||
export async function generateApiKey(): Promise<{
|
||||
apiKey: string;
|
||||
apiSecret: string;
|
||||
apiKeyHash: string;
|
||||
apiSecretHash: string;
|
||||
}> {
|
||||
const apiKey = `goa_${crypto.randomBytes(16).toString('hex')}`;
|
||||
const apiSecret = crypto.randomBytes(32).toString('hex');
|
||||
|
||||
const [apiKeyHash, apiSecretHash] = await Promise.all([
|
||||
hash(apiKey),
|
||||
hash(apiSecret),
|
||||
]);
|
||||
|
||||
return {
|
||||
apiKey,
|
||||
apiSecret,
|
||||
apiKeyHash,
|
||||
apiSecretHash,
|
||||
};
|
||||
}
|
||||
|
||||
export class CryptoUtil {
|
||||
private static readonly ALGORITHM = 'aes-256-gcm';
|
||||
private static readonly SALT_LENGTH = 16;
|
||||
private static readonly TAG_LENGTH = 16;
|
||||
private static readonly IV_LENGTH = 16;
|
||||
|
||||
static encrypt(data: string, password: string): string {
|
||||
const salt = randomBytes(CryptoUtil.SALT_LENGTH);
|
||||
const key = scryptSync(password, salt, 32);
|
||||
const iv = randomBytes(CryptoUtil.IV_LENGTH);
|
||||
|
||||
const cipher = createCipheriv(CryptoUtil.ALGORITHM, key, iv);
|
||||
const encrypted = Buffer.concat([
|
||||
cipher.update(data, 'utf8'),
|
||||
cipher.final(),
|
||||
]);
|
||||
|
||||
const authTag = cipher.getAuthTag();
|
||||
|
||||
return Buffer.concat([salt, iv, authTag, encrypted]).toString('hex');
|
||||
}
|
||||
|
||||
static decrypt(encryptedData: string, password: string): string {
|
||||
const buffer = Buffer.from(encryptedData, 'hex');
|
||||
|
||||
const salt = buffer.subarray(0, CryptoUtil.SALT_LENGTH);
|
||||
const iv = buffer.subarray(
|
||||
CryptoUtil.SALT_LENGTH,
|
||||
CryptoUtil.SALT_LENGTH + CryptoUtil.IV_LENGTH,
|
||||
);
|
||||
const authTag = buffer.subarray(
|
||||
CryptoUtil.SALT_LENGTH + CryptoUtil.IV_LENGTH,
|
||||
CryptoUtil.SALT_LENGTH + CryptoUtil.IV_LENGTH + CryptoUtil.TAG_LENGTH,
|
||||
);
|
||||
const encrypted = buffer.subarray(
|
||||
CryptoUtil.SALT_LENGTH + CryptoUtil.IV_LENGTH + CryptoUtil.TAG_LENGTH,
|
||||
);
|
||||
|
||||
const key = scryptSync(password, salt, 32);
|
||||
const decipher = createDecipheriv(CryptoUtil.ALGORITHM, key, iv);
|
||||
decipher.setAuthTag(authTag);
|
||||
|
||||
return decipher.update(encrypted) + decipher.final('utf8');
|
||||
}
|
||||
|
||||
static generateKey(length: number = 32): string {
|
||||
return randomBytes(length).toString('hex');
|
||||
}
|
||||
|
||||
static generateIV(length: number = 16): string {
|
||||
return randomBytes(length).toString('hex');
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user