UNPKG

@fin.cx/skr

Version:

SKR03 and SKR04 German accounting standards for double-entry bookkeeping

319 lines 24.9 kB
import * as plugins from './plugins.js'; import * as path from 'path'; import * as crypto from 'crypto'; import * as https from 'https'; export class SecurityManager { constructor(options = {}) { this.options = { timestampServerUrl: options.timestampServerUrl || 'http://timestamp.digicert.com', includeTimestamp: options.includeTimestamp !== false, ...options }; this.logger = new plugins.smartlog.ConsoleLog(); } /** * Creates a CAdES-B (Basic) signature for data */ async createCadesSignature(data, certificatePem, privateKeyPem) { const cert = certificatePem || this.options.certificatePem; const key = privateKeyPem || this.options.privateKeyPem; if (!cert || !key) { throw new Error('Certificate and private key are required for signing'); } try { // Parse certificate and key const certificate = plugins.nodeForge.pki.certificateFromPem(cert); const privateKey = this.options.privateKeyPassphrase ? plugins.nodeForge.pki.decryptRsaPrivateKey(key, this.options.privateKeyPassphrase) : plugins.nodeForge.pki.privateKeyFromPem(key); // Create PKCS#7 signed data (CMS) const p7 = plugins.nodeForge.pkcs7.createSignedData(); // Add content if (typeof data === 'string') { p7.content = plugins.nodeForge.util.createBuffer(data, 'utf8'); } else { p7.content = plugins.nodeForge.util.createBuffer(data.toString('latin1')); } // Add certificate p7.addCertificate(certificate); // Add signer p7.addSigner({ key: privateKey, certificate: certificate, digestAlgorithm: plugins.nodeForge.pki.oids.sha256, authenticatedAttributes: [ { type: plugins.nodeForge.pki.oids.contentType, value: plugins.nodeForge.pki.oids.data }, { type: plugins.nodeForge.pki.oids.messageDigest }, { type: plugins.nodeForge.pki.oids.signingTime, value: new Date().toISOString() } ] }); // Sign the data p7.sign({ detached: true }); // Convert to PEM const pem = plugins.nodeForge.pkcs7.messageToPem(p7); // Extract base64 signature const signature = pem .replace(/-----BEGIN PKCS7-----/, '') .replace(/-----END PKCS7-----/, '') .replace(/\r?\n/g, ''); const result = { signature: signature, signatureFormat: 'CAdES-B', signingTime: new Date().toISOString(), certificateChain: [cert] }; // Add timestamp if requested if (this.options.includeTimestamp && this.options.timestampServerUrl) { try { const timestampResponse = await this.requestTimestamp(signature); result.timestampToken = timestampResponse.token; result.timestampTime = timestampResponse.time; result.signatureFormat = 'CAdES-T'; } catch (error) { this.logger.log('warn', `Failed to obtain timestamp: ${error}`); } } return result; } catch (error) { throw new Error(`Failed to create CAdES signature: ${error}`); } } /** * Requests an RFC 3161 timestamp from a TSA */ async requestTimestamp(dataHash) { try { // Create hash of the data let hash; if (typeof dataHash === 'string') { hash = crypto.createHash('sha256').update(dataHash).digest(); } else { hash = crypto.createHash('sha256').update(dataHash).digest(); } // Create timestamp request (simplified - in production use proper ASN.1 encoding) const tsRequest = this.createTimestampRequest(hash); // Send request to TSA const response = await this.sendTimestampRequest(tsRequest); return { token: response.toString('base64'), time: new Date().toISOString(), serverUrl: this.options.timestampServerUrl, hashAlgorithm: 'sha256' }; } catch (error) { throw new Error(`Failed to obtain timestamp: ${error}`); } } /** * Creates a timestamp request (simplified version) */ createTimestampRequest(hash) { // In production, use proper ASN.1 encoding library // This is a simplified placeholder const request = { version: 1, messageImprint: { hashAlgorithm: { algorithm: '2.16.840.1.101.3.4.2.1' }, // SHA-256 OID hashedMessage: hash }, reqPolicy: null, nonce: crypto.randomBytes(8), certReq: true }; // Convert to DER-encoded ASN.1 (simplified) return Buffer.from(JSON.stringify(request)); } /** * Sends timestamp request to TSA server */ async sendTimestampRequest(request) { return new Promise((resolve, reject) => { const url = new URL(this.options.timestampServerUrl); const options = { hostname: url.hostname, port: url.port || 443, path: url.pathname, method: 'POST', headers: { 'Content-Type': 'application/timestamp-query', 'Content-Length': request.length } }; const req = https.request(options, (res) => { const chunks = []; res.on('data', (chunk) => chunks.push(chunk)); res.on('end', () => { const response = Buffer.concat(chunks); if (res.statusCode === 200) { resolve(response); } else { reject(new Error(`TSA server returned status ${res.statusCode}`)); } }); }); req.on('error', reject); req.write(request); req.end(); }); } /** * Verifies a CAdES signature */ async verifyCadesSignature(data, signature, certificatePem) { try { // Add PEM headers if not present let pemSignature = signature; if (!signature.includes('BEGIN PKCS7')) { pemSignature = `-----BEGIN PKCS7-----\n${signature}\n-----END PKCS7-----`; } // Parse the PKCS#7 message const p7 = plugins.nodeForge.pkcs7.messageFromPem(pemSignature); // Prepare content for verification let content; if (typeof data === 'string') { content = plugins.nodeForge.util.createBuffer(data, 'utf8'); } else { content = plugins.nodeForge.util.createBuffer(data.toString('latin1')); } // Verify the signature const verified = p7.verify({ content: content, detached: true }); return verified; } catch (error) { this.logger.log('error', `Signature verification failed: ${error}`); return false; } } /** * Generates a self-signed certificate for testing */ async generateSelfSignedCertificate(commonName = 'SKR Export System', validDays = 365) { const keys = plugins.nodeForge.pki.rsa.generateKeyPair(2048); const cert = plugins.nodeForge.pki.createCertificate(); cert.publicKey = keys.publicKey; cert.serialNumber = '01'; cert.validity.notBefore = new Date(); cert.validity.notAfter = new Date(); cert.validity.notAfter.setDate(cert.validity.notAfter.getDate() + validDays); const attrs = [ { name: 'commonName', value: commonName }, { name: 'countryName', value: 'DE' }, { name: 'organizationName', value: 'SKR Export System' }, { shortName: 'OU', value: 'Accounting' } ]; cert.setSubject(attrs); cert.setIssuer(attrs); cert.setExtensions([ { name: 'basicConstraints', cA: true }, { name: 'keyUsage', keyCertSign: true, digitalSignature: true, nonRepudiation: true, keyEncipherment: true, dataEncipherment: true }, { name: 'extKeyUsage', serverAuth: true, clientAuth: true, codeSigning: true, emailProtection: true, timeStamping: true }, { name: 'nsCertType', client: true, server: true, email: true, objsign: true, sslCA: true, emailCA: true, objCA: true }, { name: 'subjectAltName', altNames: [ { type: 2, value: commonName } ] } ]); // Self-sign certificate cert.sign(keys.privateKey, plugins.nodeForge.md.sha256.create()); // Convert to PEM const certificatePem = plugins.nodeForge.pki.certificateToPem(cert); const privateKeyPem = plugins.nodeForge.pki.privateKeyToPem(keys.privateKey); return { certificate: certificatePem, privateKey: privateKeyPem }; } /** * Creates a detached signature file */ async createDetachedSignature(dataPath, outputPath) { const data = await plugins.smartfile.fs.toBuffer(dataPath); const signature = await this.createCadesSignature(data); const signatureData = { signature: signature.signature, format: signature.signatureFormat, signingTime: signature.signingTime, timestamp: signature.timestampToken, timestampTime: signature.timestampTime, algorithm: 'SHA256withRSA', signedFile: path.basename(dataPath) }; await plugins.smartfile.memory.toFs(JSON.stringify(signatureData, null, 2), outputPath); } /** * Verifies a detached signature file */ async verifyDetachedSignature(dataPath, signaturePath) { try { const data = await plugins.smartfile.fs.toBuffer(dataPath); const signatureJson = await plugins.smartfile.fs.toStringSync(signaturePath); const signatureData = JSON.parse(signatureJson); return await this.verifyCadesSignature(data, signatureData.signature); } catch (error) { this.logger.log('error', `Failed to verify detached signature: ${error}`); return false; } } /** * Adds Long-Term Validation (LTV) information */ async addLtvInformation(signature, ocspResponse, crlData) { // Add OCSP response and CRL data for long-term validation const ltv = { ...signature, signatureFormat: 'CAdES-LT', ocsp: ocspResponse?.toString('base64'), crl: crlData?.toString('base64'), ltvTime: new Date().toISOString() }; return ltv; } } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2tyLnNlY3VyaXR5LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvc2tyLnNlY3VyaXR5LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxPQUFPLE1BQU0sY0FBYyxDQUFDO0FBQ3hDLE9BQU8sS0FBSyxJQUFJLE1BQU0sTUFBTSxDQUFDO0FBQzdCLE9BQU8sS0FBSyxNQUFNLE1BQU0sUUFBUSxDQUFDO0FBQ2pDLE9BQU8sS0FBSyxLQUFLLE1BQU0sT0FBTyxDQUFDO0FBMEIvQixNQUFNLE9BQU8sZUFBZTtJQUkxQixZQUFZLFVBQTJCLEVBQUU7UUFDdkMsSUFBSSxDQUFDLE9BQU8sR0FBRztZQUNiLGtCQUFrQixFQUFFLE9BQU8sQ0FBQyxrQkFBa0IsSUFBSSwrQkFBK0I7WUFDakYsZ0JBQWdCLEVBQUUsT0FBTyxDQUFDLGdCQUFnQixLQUFLLEtBQUs7WUFDcEQsR0FBRyxPQUFPO1NBQ1gsQ0FBQztRQUNGLElBQUksQ0FBQyxNQUFNLEdBQUcsSUFBSSxPQUFPLENBQUMsUUFBUSxDQUFDLFVBQVUsRUFBRSxDQUFDO0lBQ2xELENBQUM7SUFFRDs7T0FFRztJQUNJLEtBQUssQ0FBQyxvQkFBb0IsQ0FDL0IsSUFBcUIsRUFDckIsY0FBdUIsRUFDdkIsYUFBc0I7UUFFdEIsTUFBTSxJQUFJLEdBQUcsY0FBYyxJQUFJLElBQUksQ0FBQyxPQUFPLENBQUMsY0FBYyxDQUFDO1FBQzNELE1BQU0sR0FBRyxHQUFHLGFBQWEsSUFBSSxJQUFJLENBQUMsT0FBTyxDQUFDLGFBQWEsQ0FBQztRQUV4RCxJQUFJLENBQUMsSUFBSSxJQUFJLENBQUMsR0FBRyxFQUFFLENBQUM7WUFDbEIsTUFBTSxJQUFJLEtBQUssQ0FBQyxzREFBc0QsQ0FBQyxDQUFDO1FBQzFFLENBQUM7UUFFRCxJQUFJLENBQUM7WUFDSCw0QkFBNEI7WUFDNUIsTUFBTSxXQUFXLEdBQUcsT0FBTyxDQUFDLFNBQVMsQ0FBQyxHQUFHLENBQUMsa0JBQWtCLENBQUMs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