80 lines
2.4 KiB
JavaScript
80 lines
2.4 KiB
JavaScript
'use strict';
|
|
|
|
const fs = require('fs');
|
|
const crypto = require('crypto');
|
|
|
|
let _privateKey = null;
|
|
let _publicKey = null;
|
|
let _kid = null;
|
|
|
|
/**
|
|
* Lazy-loads or generates the EC P-256 key pair used for signing VHL CWTs.
|
|
*
|
|
* Key resolution order:
|
|
* 1. PEM file at VHL_PRIVATE_KEY_FILE (if set and exists).
|
|
* 2. Ephemeral key pair generated at startup (logged as warning).
|
|
*
|
|
* The kid (Key ID) is derived as the first 8 bytes of SHA-256 over the
|
|
* DER-encoded public key, encoded as base64url. This matches the GDHCN
|
|
* convention for referencing public keys in the trust registry.
|
|
*/
|
|
function loadKeys() {
|
|
if (_privateKey) return;
|
|
|
|
const keyFile = process.env.VHL_PRIVATE_KEY_FILE;
|
|
if (keyFile) {
|
|
if (fs.existsSync(keyFile)) {
|
|
const pem = fs.readFileSync(keyFile);
|
|
_privateKey = crypto.createPrivateKey({ key: pem, format: 'pem' });
|
|
_publicKey = crypto.createPublicKey(_privateKey);
|
|
} else {
|
|
console.warn(`[VHL] VHL_PRIVATE_KEY_FILE not found: ${keyFile}. Generating ephemeral key pair.`);
|
|
}
|
|
} else {
|
|
console.warn('[VHL] VHL_PRIVATE_KEY_FILE not set. Generating ephemeral key pair (lost on restart).');
|
|
}
|
|
|
|
if (!_privateKey) {
|
|
const { privateKey, publicKey } = crypto.generateKeyPairSync('ec', { namedCurve: 'P-256' });
|
|
_privateKey = privateKey;
|
|
_publicKey = publicKey;
|
|
}
|
|
|
|
// kid = base64url(SHA-256(DER public key)[0..7])
|
|
const pubDer = _publicKey.export({ type: 'spki', format: 'der' });
|
|
_kid = crypto.createHash('sha256').update(pubDer).digest().slice(0, 8).toString('base64url');
|
|
}
|
|
|
|
/** Returns the EC P-256 private KeyObject for signing. */
|
|
function getPrivateKey() {
|
|
loadKeys();
|
|
return _privateKey;
|
|
}
|
|
|
|
/** Returns the EC P-256 public KeyObject for verification. */
|
|
function getPublicKey() {
|
|
loadKeys();
|
|
return _publicKey;
|
|
}
|
|
|
|
/**
|
|
* Returns the Key ID (kid) for use in CWT protected headers.
|
|
* Derived from the public key fingerprint so verifiers can look up the key
|
|
* in the GDHCN trust registry.
|
|
*/
|
|
function getKid() {
|
|
loadKeys();
|
|
return _kid;
|
|
}
|
|
|
|
/**
|
|
* Returns the public key in JWK format for publishing to a trust registry.
|
|
* @returns {object} JWK representation of the public key.
|
|
*/
|
|
function getPublicJWK() {
|
|
loadKeys();
|
|
return _publicKey.export({ format: 'jwk' });
|
|
}
|
|
|
|
module.exports = { getPrivateKey, getPublicKey, getKid, getPublicJWK };
|