All checks were successful
Deploy eCourt Note Web to Server / Validate Database Schema (Drizzle) (push) Successful in 4s
Deploy eCourt Note Web to Server / Build Web Docker Image (push) Successful in 1m18s
Deploy eCourt Note Web to Server / Deploy eCourt Note Web to Server (push) Successful in 6s
Deploy eCourt Note Web to Server / Notify Deployment Status (push) Successful in 0s
112 lines
No EOL
4.7 KiB
TypeScript
112 lines
No EOL
4.7 KiB
TypeScript
|
|
import { createHmac, createPrivateKey, createPublicKey, timingSafeEqual, verify, sign } from 'node:crypto';
|
|
/**
|
|
* คำนวณ HMAC-SHA256 จาก string หรือ JSON Object
|
|
*/
|
|
export function generateHmacSha256(data: string | object, secretKey: string): string {
|
|
const payload = typeof data === 'string' ? data : JSON.stringify(data);
|
|
return createHmac('sha256', secretKey)
|
|
.update(payload, 'utf8')
|
|
.digest('hex');
|
|
}
|
|
|
|
/**
|
|
* ตรวจสอบความถูกต้องของ HMAC (ใช้ timingSafeEqual เพื่อป้องกัน Timing Attacks)
|
|
*/
|
|
export function verifyHmacSha256(data: string | object, secretKey: string, expectedHmac: string): boolean {
|
|
const calculatedHmac = generateHmacSha256(data, secretKey);
|
|
const a = Buffer.from(calculatedHmac, 'hex');
|
|
const b = Buffer.from(expectedHmac, 'hex');
|
|
|
|
if (a.length !== b.length) {
|
|
return false;
|
|
}
|
|
return timingSafeEqual(a, b);
|
|
}
|
|
// ==========================================
|
|
// ตัวอย่างการใช้งาน
|
|
// ==========================================
|
|
/*
|
|
const secret = 'my-super-secret-key';
|
|
const jsonData = {
|
|
userId: 12345,
|
|
action: 'transfer',
|
|
amount: 500
|
|
};
|
|
|
|
// 1. สร้าง HMAC
|
|
const hmacSignature = generateHmacSha256(jsonData, secret);
|
|
console.log('HMAC Signature (Hex):', hmacSignature);
|
|
|
|
// 2. ตรวจสอบ HMAC
|
|
const isValid = verifyHmacSha256(jsonData, secret, hmacSignature);
|
|
console.log('Is Signature Valid?:', isValid); // true
|
|
*/
|
|
|
|
|
|
/**
|
|
* ฟังก์ชันสำหรับลงนามดิจิทัล (Sign) ข้อมูลด้วย Ed25519 Private Key (Hex)
|
|
* @param message ข้อความดิบที่ต้องการเซ็น (string หรือ Buffer)
|
|
* @param privateKeyHex คีย์ลับในรูปแบบ Hex String (ความยาว 64 ตัวอักษร)
|
|
* @returns ลายเซ็น (Signature) ในรูปแบบ Hex String (ความยาว 128 ตัวอักษร)
|
|
*/
|
|
export function signDataEd25519(message: string | Buffer, privateKeyHex: string): string {
|
|
const rawKeyBuffer = Buffer.from(privateKeyHex, 'hex');
|
|
// ตัดเอาเฉพาะ 32 bytes (Seed) กรณีที่ Hex ยาว 64 bytes (ที่ต่อกับ Public Key มา)
|
|
const seed = rawKeyBuffer.length === 64 ? rawKeyBuffer.subarray(0, 32) : rawKeyBuffer;
|
|
// PKCS#8 Header สำหรับ Ed25519 Private Key
|
|
const pkcs8Header = Buffer.from('302e020100300506032b657004220420', 'hex');
|
|
const pkcs8Buffer = Buffer.concat([pkcs8Header, seed]);
|
|
|
|
const privateKey = createPrivateKey({
|
|
key: pkcs8Buffer,
|
|
format: 'der',
|
|
type: 'pkcs8',
|
|
});
|
|
|
|
const dataBuffer = typeof message === 'string' ? Buffer.from(message, 'utf-8') : message;
|
|
const signatureBuffer = sign(null, dataBuffer, privateKey);
|
|
return signatureBuffer.toString('hex');
|
|
}
|
|
|
|
/**
|
|
* ฟังก์ชันสำหรับตรวจสอบลายเซ็น (Verify) ด้วย Ed25519 Public Key (Hex)
|
|
* @param message ข้อความดิบที่ใช้ในการเซ็น (string หรือ Buffer)
|
|
* @param signatureHex ลายเซ็นในรูปแบบ Hex String
|
|
* @param publicKeyHex คีย์สาธารณะในรูปแบบ Hex String (ความยาว 64 ตัวอักษร)
|
|
* @returns ผลลัพธ์ความถูกต้อง (true = ผ่าน, false = ไม่ผ่าน)
|
|
*/
|
|
export function verifyDataEd25519(message: string | Buffer, signatureHex: string, publicKeyHex: string): boolean {
|
|
try {
|
|
const rawPublicKey = Buffer.from(publicKeyHex, 'hex');
|
|
|
|
// SPKI Header สำหรับ Ed25519 Public Key (32 bytes)
|
|
const spkiHeader = Buffer.from('302a300506032b6570032100', 'hex');
|
|
const spkiBuffer = Buffer.concat([spkiHeader, rawPublicKey]);
|
|
|
|
const publicKey = createPublicKey({
|
|
key: spkiBuffer,
|
|
format: 'der',
|
|
type: 'spki',
|
|
});
|
|
|
|
const dataBuffer = typeof message === 'string' ? Buffer.from(message, 'utf-8') : message;
|
|
const signatureBuffer = Buffer.from(signatureHex, 'hex');
|
|
|
|
return verify(null, dataBuffer, publicKey, signatureBuffer);
|
|
} catch (error) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* ฟังก์ชันสำหรับเรียงคีย์ของ Object ตามตัวอักษรเพื่อไม่ให้โครงสร้าง JSON เพี้ยน
|
|
*/
|
|
export function sortObjectKeys(obj: Record<string, unknown>): Record<string, unknown> {
|
|
return Object.keys(obj)
|
|
.sort()
|
|
.reduce((result: Record<string, unknown>, key: string) => {
|
|
result[key] = obj[key];
|
|
return result;
|
|
}, {});
|
|
} |