import { exportJWK, exportPKCS8, exportSPKI, generateKeyPair, importPKCS8, importSPKI, JWK, KeyLike } from 'jose'; import { Storage, StorageAdapter } from './storage/storage.js'; const signingAlg = 'ES256'; const encryptionAlg = 'RSA-OAEP-512'; interface SerializedKeyPair { id: string; publicKey: string; privateKey: string; created: number; alg: string; expired?: number; } export interface KeyPair { id: string; alg: string; public: KeyLike; private: KeyLike; created: Date; expired?: Date; jwk: JWK; } /** * @deprecated use `signingKeys` instead */ export async function legacySigningKeys(storage: StorageAdapter): Promise { const alg = 'RS512'; const results = [] as KeyPair[]; const scanner = Storage.scan(storage, ['oauth:key']); for await (const [_key, value] of scanner) { const publicKey = await importSPKI(value.publicKey, alg, { extractable: true }); const privateKey = await importPKCS8(value.privateKey, alg); const jwk = await exportJWK(publicKey); jwk.kid = value.id; results.push({ id: value.id, alg, created: new Date(value.created), public: publicKey, private: privateKey, expired: new Date(1735858114000), jwk }); } return results; } export async function signingKeys(storage: StorageAdapter): Promise { const results = [] as KeyPair[]; const scanner = Storage.scan(storage, ['signing:key']); for await (const [_key, value] of scanner) { const publicKey = await importSPKI(value.publicKey, value.alg, { extractable: true }); const privateKey = await importPKCS8(value.privateKey, value.alg); const jwk = await exportJWK(publicKey); jwk.kid = value.id; jwk.use = 'sig'; results.push({ id: value.id, alg: signingAlg, created: new Date(value.created), expired: value.expired ? new Date(value.expired) : undefined, public: publicKey, private: privateKey, jwk }); } results.sort((a, b) => b.created.getTime() - a.created.getTime()); if (results.filter((item) => !item.expired).length) return results; const key = await generateKeyPair(signingAlg, { extractable: true }); const serialized: SerializedKeyPair = { id: crypto.randomUUID(), publicKey: await exportSPKI(key.publicKey), privateKey: await exportPKCS8(key.privateKey), created: Date.now(), alg: signingAlg }; await Storage.set(storage, ['signing:key', serialized.id], serialized); return signingKeys(storage); } export async function encryptionKeys(storage: StorageAdapter): Promise { const results = [] as KeyPair[]; const scanner = Storage.scan(storage, ['encryption:key']); for await (const [_key, value] of scanner) { const publicKey = await importSPKI(value.publicKey, value.alg, { extractable: true }); const privateKey = await importPKCS8(value.privateKey, value.alg); const jwk = await exportJWK(publicKey); jwk.kid = value.id; results.push({ id: value.id, alg: encryptionAlg, created: new Date(value.created), expired: value.expired ? new Date(value.expired) : undefined, public: publicKey, private: privateKey, jwk }); } results.sort((a, b) => b.created.getTime() - a.created.getTime()); if (results.filter((item) => !item.expired).length) return results; const key = await generateKeyPair(encryptionAlg, { extractable: true }); const serialized: SerializedKeyPair = { id: crypto.randomUUID(), publicKey: await exportSPKI(key.publicKey), privateKey: await exportPKCS8(key.privateKey), created: Date.now(), alg: encryptionAlg }; await Storage.set(storage, ['encryption:key', serialized.id], serialized); return encryptionKeys(storage); }