Chuyển tới nội dung chính

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.

cảnh báo

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):

ky-goi.mjs
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.