@super-protocol/sdk-js 4.0.14 → 4.0.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,230 +0,0 @@
1
- import assert from 'assert';
2
- import forge from 'node-forge';
3
- import { X509CertificateGenerator, BasicConstraintsExtension, ExtendedKeyUsageExtension, Extension, SubjectAlternativeNameExtension, ExtendedKeyUsage, KeyUsageFlags, KeyUsagesExtension, Pkcs10CertificateRequestGenerator, Pkcs10CertificateRequest, X509Certificate, AuthorityInfoAccessExtension, AuthorityKeyIdentifierExtension, SubjectKeyIdentifierExtension, } from '@peculiar/x509';
4
- import { cryptoProvider } from './setup-crypto.js';
5
- import { CryptoKeysTransformer } from '../utils/CryptoKeysTransformer.js';
6
- import { isIpAddress } from '../utils/helper.js';
7
- import { CertificatesHelper } from './helper.js';
8
- const ONE_HOUR_MS = 60 * 60 * 1000; // 1 hour in milliseconds
9
- const notAllowedCertificateCustomExtensions = [...Object.values(forge.pki.oids)];
10
- export class CertificateGenerator {
11
- /**
12
- * Generates certificate based on the provided parameters.
13
- * @param params - Parameters for generating the certificate.
14
- * @returns The generated certificate in PEM format.
15
- */
16
- static async generateCert(params) {
17
- const ca = Boolean(params.ca);
18
- const { publicKey: subjectPublicKey, privateKey: signerPrivateKey } = await CertificateGenerator.getCryptoKeys(params);
19
- const signingAlgorithm = subjectPublicKey.algorithm;
20
- const extensions = [new BasicConstraintsExtension(ca, undefined, true)];
21
- const extendedKeyUsageItems = [];
22
- if (signingAlgorithm.namedCurve !== 'K-256' && params.dnsNames?.length) {
23
- const generalNames = params.dnsNames.map((dnsName) => ({
24
- type: (isIpAddress(dnsName) ? 'ip' : 'dns'),
25
- value: dnsName,
26
- }));
27
- extensions.push(new SubjectAlternativeNameExtension(generalNames));
28
- extendedKeyUsageItems.push(...[ExtendedKeyUsage.serverAuth, ExtendedKeyUsage.clientAuth]);
29
- }
30
- if (params.ocspSigning) {
31
- extendedKeyUsageItems.push(ExtendedKeyUsage.ocspSigning);
32
- }
33
- if (params.ocspExtension) {
34
- const { ocspUrl, issuerCertUrl } = params.ocspExtension;
35
- extensions.push(new AuthorityInfoAccessExtension({
36
- ocsp: [ocspUrl],
37
- ...(issuerCertUrl ? { caIssuers: [issuerCertUrl] } : {}),
38
- }));
39
- }
40
- if (extendedKeyUsageItems.length) {
41
- extensions.push(new ExtendedKeyUsageExtension(extendedKeyUsageItems, false));
42
- }
43
- let keyUsageFlags = KeyUsageFlags.digitalSignature | KeyUsageFlags.keyEncipherment;
44
- if (params.ca) {
45
- keyUsageFlags |= KeyUsageFlags.keyCertSign;
46
- }
47
- extensions.push(new KeyUsagesExtension(keyUsageFlags, true));
48
- const signerPublicKey = await CryptoKeysTransformer.cryptoPublicFromCryptoPrivate(signerPrivateKey);
49
- extensions.push(...[
50
- await AuthorityKeyIdentifierExtension.create(signerPublicKey),
51
- await SubjectKeyIdentifierExtension.create(subjectPublicKey),
52
- ]);
53
- if (params.customExtensions?.length) {
54
- const filteredExtensions = params.customExtensions.filter((ext) => !notAllowedCertificateCustomExtensions.includes(ext.oid));
55
- for (const customExtension of filteredExtensions) {
56
- if (!customExtension.oid || !customExtension.value) {
57
- throw new Error('Custom extension OID and value are required');
58
- }
59
- extensions.push(new Extension(customExtension.oid, false, customExtension.value));
60
- }
61
- }
62
- const createCertificateParams = {
63
- serialNumber: CertificatesHelper.generateSerialNumber().toString(16),
64
- issuer: CertificatesHelper.serializePrincipalInfo(params.issuer),
65
- subject: CertificatesHelper.serializePrincipalInfo(params.subject),
66
- notBefore: new Date(Date.now() - ONE_HOUR_MS), //1 hour ago to avoid clock skew issues between servers
67
- notAfter: params.notAfter,
68
- publicKey: subjectPublicKey,
69
- signingKey: signerPrivateKey,
70
- signingAlgorithm,
71
- extensions,
72
- };
73
- const cert = await X509CertificateGenerator.create(createCertificateParams);
74
- return cert.toString('pem');
75
- }
76
- /**
77
- * Generates a pair of cryptographic keys based on the specified signature algorithm.
78
- * @param signatureAlgorithm - The algorithm to use for key generation.
79
- * @returns A promise that resolves to a CryptoKeyPair containing the public and private keys.
80
- */
81
- static generateKeys(signatureAlgorithm) {
82
- const algorithm = CertificateGenerator.getAlgorithm(signatureAlgorithm);
83
- return cryptoProvider.subtle.generateKey(algorithm, true, ['sign', 'verify']);
84
- }
85
- /**
86
- * Generates a Certificate Signing Request (CSR) based on the provided parameters.
87
- * @param params - Parameters for generating the CSR.
88
- * @returns The generated CSR in PEM format.
89
- */
90
- static async generateCsr(params) {
91
- const keys = await CertificateGenerator.getCryptoKeys(params);
92
- const signingAlgorithm = keys.publicKey.algorithm;
93
- signingAlgorithm.hash = { name: 'SHA-256' };
94
- const extensions = [];
95
- if (signingAlgorithm.namedCurve !== 'K-256' && params.dnsNames?.length) {
96
- const generalNames = params.dnsNames.map((dnsName) => ({
97
- type: (isIpAddress(dnsName) ? 'ip' : 'dns'),
98
- value: dnsName,
99
- }));
100
- extensions.push(new SubjectAlternativeNameExtension(generalNames));
101
- }
102
- if (params.customExtensions?.length) {
103
- for (const customExtension of params.customExtensions) {
104
- if (!customExtension.oid || !customExtension.value) {
105
- throw new Error(`Some custom extension missed OID or value`);
106
- }
107
- extensions.push(new Extension(customExtension.oid, false, customExtension.value));
108
- }
109
- }
110
- const createCsrParams = {
111
- name: CertificatesHelper.serializePrincipalInfo(params.subject),
112
- keys,
113
- signingAlgorithm,
114
- extensions,
115
- };
116
- const csr = await Pkcs10CertificateRequestGenerator.create(createCsrParams);
117
- return csr.toString('pem');
118
- }
119
- /**
120
- * Verifies self-signed certificate
121
- * @param rawCert - the certificate
122
- * @returns An object containing the verification result.
123
- */
124
- verifySelfSignedCert(rawCert) {
125
- const cert = new X509Certificate(rawCert);
126
- if (cert.issuer !== cert.subject) {
127
- return Promise.resolve({ isValid: false });
128
- }
129
- return cert.verify().then((isValid) => ({ isValid }));
130
- }
131
- /**
132
- * Parses a certificate
133
- * @param rawCert - the certificate
134
- * @returns An object containing the parsed certificate details.
135
- */
136
- static async parseCert(rawCert) {
137
- const cert = new X509Certificate(rawCert);
138
- const publicKey = await cryptoProvider.subtle.importKey('spki', cert.publicKey.rawData, Object.assign(cert.signatureAlgorithm, cert.publicKey.algorithm), true, ['verify']);
139
- const authorityKeyIdentifierExt = cert.extensions.find((ext) => ext instanceof AuthorityKeyIdentifierExtension);
140
- const authorityKeyIdentifier = authorityKeyIdentifierExt?.keyId;
141
- const subjectKeyIdentifierExt = cert.extensions.find((ext) => ext instanceof SubjectKeyIdentifierExtension);
142
- const subjectKeyIdentifier = subjectKeyIdentifierExt?.keyId;
143
- return {
144
- serialNumberHex: cert.serialNumber,
145
- publicKey,
146
- subject: cert.subject,
147
- issuer: cert.issuer,
148
- notBefore: cert.notBefore,
149
- notAfter: cert.notAfter,
150
- dnsNames: CertificateGenerator.extractDnsNamesFromExtensions(cert.extensions),
151
- authorityKeyIdentifier,
152
- subjectKeyIdentifier,
153
- extensions: cert.extensions
154
- .filter((ext) => ext.type !== forge.pki.oids['subjectAltName'])
155
- .map((ext) => ({
156
- oid: ext.type,
157
- value: Buffer.from(ext.value),
158
- })),
159
- };
160
- }
161
- /**
162
- * Checks and parses a Certificate Signing Request (CSR) in PEM format.
163
- * @param csrPem - The CSR in PEM format.
164
- * @returns An object containing the parsed CSR details.
165
- */
166
- static async checkAndParseCsr(csrPem) {
167
- const csr = new Pkcs10CertificateRequest(csrPem);
168
- const isValid = await csr.verify();
169
- if (!isValid) {
170
- throw new Error('CSR signature verification failed');
171
- }
172
- const publicKey = await cryptoProvider.subtle.importKey('spki', csr.publicKey.rawData, Object.assign(csr.signatureAlgorithm, csr.publicKey.algorithm), true, ['verify']);
173
- const parsedCsr = {
174
- subject: csr.subject,
175
- publicKey,
176
- dnsNames: CertificateGenerator.extractDnsNamesFromExtensions(csr.extensions),
177
- extensions: csr.extensions
178
- .filter((ext) => ext.type !== forge.pki.oids['subjectAltName'])
179
- .map((ext) => ({
180
- oid: ext.type,
181
- value: Buffer.from(ext.value),
182
- })),
183
- };
184
- return parsedCsr;
185
- }
186
- static async getCryptoKeys({ privateKey, publicKey }) {
187
- const [pubKey, privKey] = await Promise.all([
188
- typeof publicKey === 'string'
189
- ? CryptoKeysTransformer.spkiPemToCryptoKey(publicKey)
190
- : publicKey,
191
- typeof privateKey === 'string'
192
- ? CryptoKeysTransformer.pkcs8PemToCryptoKey(privateKey)
193
- : privateKey,
194
- ]);
195
- assert.deepEqual(pubKey.algorithm, privKey.algorithm, 'Both keys must have same algorithm defined');
196
- return { publicKey: pubKey, privateKey: privKey };
197
- }
198
- static getAlgorithm(signatureAlgorithm) {
199
- switch (signatureAlgorithm) {
200
- case 'RSASSA-PKCS1-SHA256':
201
- return {
202
- name: 'RSASSA-PKCS1-v1_5',
203
- hash: 'SHA-256',
204
- publicExponent: new Uint8Array([1, 0, 1]), // 65537
205
- modulusLength: 2048,
206
- };
207
- case 'ECDSA-P-256-SHA256':
208
- return {
209
- name: 'ECDSA',
210
- namedCurve: 'P-256',
211
- };
212
- case 'ECDSA-secp256k1-SHA256':
213
- return {
214
- name: 'ECDSA',
215
- namedCurve: 'K-256',
216
- };
217
- default:
218
- throw new Error(`Unsupported signature algorithm: ${signatureAlgorithm}`);
219
- }
220
- }
221
- static extractDnsNamesFromExtensions(extensions) {
222
- const subjectAltNameExt = extensions.find((ext) => ext.type === forge.pki.oids['subjectAltName']);
223
- if (!subjectAltNameExt) {
224
- return;
225
- }
226
- const dnsNames = subjectAltNameExt.names.items.map((item) => item.value);
227
- return dnsNames;
228
- }
229
- }
230
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZ2VuZXJhdG9yLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL2NlcnRpZmljYXRlcy9nZW5lcmF0b3IudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxNQUFNLE1BQU0sUUFBUSxDQUFDO0FBQzVCLE9BQU8sS0FBSyxNQUFNLFlBQVksQ0FBQztBQUMvQixPQUFPLEVBRUwsd0JBQXdCLEVBQ3hCLHlCQUF5QixFQUN6Qix5QkFBeUIsRUFDekIsU0FBUyxFQUNULCtCQUErQixFQUcvQixnQkFBZ0IsRUFDaEIsYUFBYSxFQUNiLGtCQUFrQixFQUNsQixpQ0FBaUMsRUFFakMsd0JBQXdCLEVBQ3hCLGVBQWUsRUFDZiw0QkFBNEIsRUFDNUIsK0JBQStCLEVBQy9CLDZCQUE2QixHQUU5QixNQUFNLGdCQUFnQixDQUFDO0FBVXhCLE9BQU8sRUFBRSxjQUFjLEVBQUUsTUFBTSxtQkFBbUIsQ0FBQztBQUNuRCxPQUFPLEVBQUUscUJBQXFCLEVBQUUsTUFBTSxtQ0FBbUMsQ0FBQztBQUMxRSxPQUFPLEVBQUUsV0FBVyxFQUFFLE1BQU0sb0JBQW9CLENBQUM7QUFDakQsT0FBTyxFQUFFLGtCQUFrQixFQUFFLE1BQU0sYUFBYSxDQUFDO0FBRWpELE1BQU0sV0FBVyxHQUFHLEVBQUUsR0FBRyxFQUFFLEdBQUcsSUFBSSxDQUFDLENBQUMseUJBQXlCO0FBRTdELE1BQU0scUNBQXFDLEdBQUcsQ0FBQyxHQUFHLE1BQU0sQ0FBQyxNQUFNLENBQUMsS0FBSyxDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDO0FBRWpGLE1BQU0sT0FBTyxvQkFBb0I7SUFDL0I7Ozs7T0FJRztJQUNILE1BQU0sQ0FBQyxLQUFLLENBQUMsWUFBWSxDQUFDLE1BQTBCO1FBQ2xELE1BQU0sRUFBRSxHQUFHLE9BQU8sQ0FBQyxNQUFNLENBQUMsRUFBRSxDQUFDLENBQUM7UUFFOUIsTUFBTSxFQUFFLFNBQVMsRUFBRSxnQkFBZ0IsRUFBRSxVQUFV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