import * as plugins from './bunq.plugins.js'; export class BunqCrypto { private privateKey: string; private publicKey: string; constructor() {} /** * Generate a new RSA key pair for bunq API communication */ public async generateKeyPair(): Promise { const keyPair = plugins.crypto.generateKeyPairSync('rsa', { modulusLength: 2048, publicKeyEncoding: { type: 'spki', format: 'pem' }, privateKeyEncoding: { type: 'pkcs8', format: 'pem' } }); this.privateKey = keyPair.privateKey; this.publicKey = keyPair.publicKey; } /** * Get the public key */ public getPublicKey(): string { if (!this.publicKey) { throw new Error('Public key not generated yet'); } return this.publicKey; } /** * Get the private key */ public getPrivateKey(): string { if (!this.privateKey) { throw new Error('Private key not generated yet'); } return this.privateKey; } /** * Set keys from stored values */ public setKeys(privateKey: string, publicKey: string): void { this.privateKey = privateKey; this.publicKey = publicKey; } /** * Sign data with the private key */ public signData(data: string): string { if (!this.privateKey) { throw new Error('Private key not set'); } const sign = plugins.crypto.createSign('SHA256'); sign.update(data); sign.end(); return sign.sign(this.privateKey, 'base64'); } /** * Verify data with the server's public key */ public verifyData(data: string, signature: string, serverPublicKey: string): boolean { const verify = plugins.crypto.createVerify('SHA256'); verify.update(data); verify.end(); return verify.verify(serverPublicKey, signature, 'base64'); } /** * Create request signature header (signs only body per bunq docs) */ public createSignatureHeader( method: string, endpoint: string, headers: { [key: string]: string }, body: string = '' ): string { // According to bunq docs, only sign the request body return this.signData(body); } /** * Verify response signature (signs only body per bunq API behavior) */ public verifyResponseSignature( statusCode: number, headers: { [key: string]: string }, body: string, serverPublicKey: string ): boolean { const responseSignature = headers['x-bunq-server-signature']; if (!responseSignature) { return false; } // According to bunq API behavior, only the response body is signed return this.verifyData(body, responseSignature, serverPublicKey); } /** * Generate a random request ID */ public generateRequestId(): string { return plugins.crypto.randomUUID(); } }