Ký gói (signature.sig)
Mọi gói nộp lên phải có chữ ký Ed25519 detached trên chuỗi byte chuẩn hoá của gói, theo ADR-0045 mục 1. Công thức:
Với mỗi entry trong zip (trừ
signature.sig), sắp theo tên (alphabet), nối:tên (UTF-8 bytes)+\n(1 byte) +độ dài nội dung (8 byte big-endian)+nội dung. Nối tất cả entry theo thứ tự đó thành một chuỗi byte, rồi ký Ed25519 detached trên chuỗi đó.
Đổi bất kỳ tệp nào trong gói (manifest, mã, migration) sau khi ký sẽ làm chữ ký sai — ký sau cùng, khi mọi tệp khác đã chốt.
Khoá tin cậy do Platform Team cấu hình, không tự ý dùng khoá khác. Ở môi
trường dev, có sẵn một khoá thử (key-id: dev-local) trong
services/module-registry/src/main/resources/application-local.yml — chỉ
dùng cho dev, không bao giờ ký gói thật bằng khoá đó.
Ví dụ code Node.js để tự ký (trích từ scripts/sinh-goi-module-demo.mjs —
đoạn dưới đây đã chạy thật và tự verify lại bằng public key trước khi đưa vào
tài liệu):
import { createPrivateKey, sign } from 'node:crypto';
// Chuỗi byte chuẩn hoá của gói — xem công thức ở trên.
function chuanHoaGoi(entries) {
const phan = [];
const saTheoTen = [...entries].sort((a, b) => (a.ten < b.ten ? -1 : a.ten > b.ten ? 1 : 0));
for (const { ten, noiDung } of saTheoTen) {
const tenBuf = Buffer.from(ten, 'utf8');
const doDaiBuf = Buffer.alloc(8);
doDaiBuf.writeBigUInt64BE(BigInt(noiDung.length));
phan.push(tenBuf, Buffer.from('\n'), doDaiBuf, noiDung);
}
return Buffer.concat(phan);
}
function kyGoi(entries, privateKeyDerBase64) {
const chuanHoa = chuanHoaGoi(entries);
const privateKey = createPrivateKey({
key: Buffer.from(privateKeyDerBase64, 'base64'),
format: 'der',
type: 'pkcs8',
});
// Ed25519: thuật toán ký đã cố định trong khoá, sign() nhận algorithm = null.
return sign(null, chuanHoa, privateKey);
}
entries là mảng { ten, noiDung } (tên entry trong zip và nội dung dạng
Buffer) của mọi tệp trừ signature.sig. Kết quả kyGoi(...) là
Buffer 64 byte, ghi thẳng làm nội dung nhị phân của entry signature.sig
trong zip.