@sphereon/ssi-sdk-ext.x509-utils 0.24.1-unstable.9

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.
@@ -0,0 +1,196 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ var __importDefault = (this && this.__importDefault) || function (mod) {
26
+ return (mod && mod.__esModule) ? mod : { "default": mod };
27
+ };
28
+ Object.defineProperty(exports, "__esModule", { value: true });
29
+ exports.derToPEM = exports.PEMToDer = exports.hexToPEM = exports.hexToBase64 = exports.base64ToHex = exports.PEMToHex = exports.publicKeyHexFromPEM = exports.hexKeyFromPEMBasedJwk = exports.privateKeyHexFromPEM = exports.PEMToJwk = exports.jwkToPEM = exports.toKeyObject = exports.areCertificatesEqual = exports.pemOrDerToX509Certificate = exports.x5cToPemCertChain = exports.pemCertChainTox5c = void 0;
30
+ const pkijs_1 = require("pkijs");
31
+ const u8a = __importStar(require("uint8arrays"));
32
+ // @ts-ignore
33
+ const keyto_1 = __importDefault(require("@trust/keyto"));
34
+ // Based on (MIT licensed):
35
+ // https://github.com/hildjj/node-posh/blob/master/lib/index.js
36
+ function pemCertChainTox5c(cert, maxDepth) {
37
+ if (!maxDepth) {
38
+ maxDepth = 0;
39
+ }
40
+ /*
41
+ * Convert a PEM-encoded certificate to the version used in the x5c element
42
+ * of a [JSON Web Key](http://tools.ietf.org/html/draft-ietf-jose-json-web-key).
43
+ *
44
+ * `cert` PEM-encoded certificate chain
45
+ * `maxdepth` The maximum number of certificates to use from the chain.
46
+ */
47
+ const intermediate = cert
48
+ .replace(/-----[^\n]+\n?/gm, ',')
49
+ .replace(/\n/g, '')
50
+ .replace(/\r/g, '');
51
+ let x5c = intermediate.split(',').filter(function (c) {
52
+ return c.length > 0;
53
+ });
54
+ if (maxDepth > 0) {
55
+ x5c = x5c.splice(0, maxDepth);
56
+ }
57
+ return x5c;
58
+ }
59
+ exports.pemCertChainTox5c = pemCertChainTox5c;
60
+ function x5cToPemCertChain(x5c, maxDepth) {
61
+ if (!maxDepth) {
62
+ maxDepth = 0;
63
+ }
64
+ const length = maxDepth === 0 ? x5c.length : Math.min(maxDepth, x5c.length);
65
+ let pem = '';
66
+ for (let i = 0; i < length; i++) {
67
+ pem += derToPEM(x5c[i], 'CERTIFICATE');
68
+ }
69
+ return pem;
70
+ }
71
+ exports.x5cToPemCertChain = x5cToPemCertChain;
72
+ const pemOrDerToX509Certificate = (cert) => {
73
+ if (typeof cert !== 'string') {
74
+ return pkijs_1.Certificate.fromBER(cert);
75
+ }
76
+ let DER = cert;
77
+ if (cert.includes('CERTIFICATE')) {
78
+ DER = PEMToDer(cert);
79
+ }
80
+ return pkijs_1.Certificate.fromBER(u8a.fromString(DER, 'base64pad'));
81
+ };
82
+ exports.pemOrDerToX509Certificate = pemOrDerToX509Certificate;
83
+ const areCertificatesEqual = (cert1, cert2) => {
84
+ return cert1.signatureValue.isEqual(cert2.signatureValue);
85
+ };
86
+ exports.areCertificatesEqual = areCertificatesEqual;
87
+ const toKeyObject = (PEM, visibility = 'public') => {
88
+ const jwk = (0, exports.PEMToJwk)(PEM, visibility);
89
+ const keyVisibility = jwk.d ? 'private' : 'public';
90
+ const keyHex = keyVisibility === 'private' ? (0, exports.privateKeyHexFromPEM)(PEM) : (0, exports.publicKeyHexFromPEM)(PEM);
91
+ return {
92
+ pem: (0, exports.hexToPEM)(keyHex, visibility),
93
+ jwk,
94
+ keyHex,
95
+ keyType: keyVisibility,
96
+ };
97
+ };
98
+ exports.toKeyObject = toKeyObject;
99
+ const jwkToPEM = (jwk, visibility = 'public') => {
100
+ return keyto_1.default.from(jwk, 'jwk').toString('pem', visibility === 'public' ? 'public_pkcs8' : 'private_pkcs8');
101
+ };
102
+ exports.jwkToPEM = jwkToPEM;
103
+ const PEMToJwk = (pem, visibility = 'public') => {
104
+ return keyto_1.default.from(pem, 'pem').toJwk(visibility);
105
+ };
106
+ exports.PEMToJwk = PEMToJwk;
107
+ const privateKeyHexFromPEM = (PEM) => {
108
+ return (0, exports.PEMToHex)(PEM);
109
+ };
110
+ exports.privateKeyHexFromPEM = privateKeyHexFromPEM;
111
+ const hexKeyFromPEMBasedJwk = (jwk, visibility = 'public') => {
112
+ if (visibility === 'private') {
113
+ return (0, exports.privateKeyHexFromPEM)((0, exports.jwkToPEM)(jwk, 'private'));
114
+ }
115
+ else {
116
+ return (0, exports.publicKeyHexFromPEM)((0, exports.jwkToPEM)(jwk, 'public'));
117
+ }
118
+ };
119
+ exports.hexKeyFromPEMBasedJwk = hexKeyFromPEMBasedJwk;
120
+ const publicKeyHexFromPEM = (PEM) => {
121
+ const hex = (0, exports.PEMToHex)(PEM);
122
+ if (PEM.includes('CERTIFICATE')) {
123
+ throw Error('Cannot directly deduce public Key from PEM Certificate yet');
124
+ }
125
+ else if (!PEM.includes('PRIVATE')) {
126
+ return hex;
127
+ }
128
+ const publicJwk = (0, exports.PEMToJwk)(PEM, 'public');
129
+ const publicPEM = (0, exports.jwkToPEM)(publicJwk, 'public');
130
+ return (0, exports.PEMToHex)(publicPEM);
131
+ };
132
+ exports.publicKeyHexFromPEM = publicKeyHexFromPEM;
133
+ const PEMToHex = (PEM, headerKey) => {
134
+ if (PEM.indexOf('-----BEGIN ') == -1) {
135
+ throw Error(`PEM header not found: ${headerKey}`);
136
+ }
137
+ let strippedPem;
138
+ if (headerKey) {
139
+ strippedPem = PEM.replace(new RegExp('^[^]*-----BEGIN ' + headerKey + '-----'), '');
140
+ strippedPem = strippedPem.replace(new RegExp('-----END ' + headerKey + '-----[^]*$'), '');
141
+ }
142
+ else {
143
+ strippedPem = PEM.replace(/^[^]*-----BEGIN [^-]+-----/, '');
144
+ strippedPem = strippedPem.replace(/-----END [^-]+-----[^]*$/, '');
145
+ }
146
+ return (0, exports.base64ToHex)(strippedPem, 'base64pad');
147
+ };
148
+ exports.PEMToHex = PEMToHex;
149
+ /**
150
+ * Converts a base64 encoded string to hex string, removing any non-base64 characters, including newlines
151
+ * @param input The input in base64, with optional newlines
152
+ * @param inputEncoding
153
+ */
154
+ const base64ToHex = (input, inputEncoding) => {
155
+ const base64NoNewlines = input.replace(/[^0-9A-Za-z_\-~\/+=]*/g, '');
156
+ return u8a.toString(u8a.fromString(base64NoNewlines, inputEncoding ? inputEncoding : 'base64pad'), 'base16');
157
+ };
158
+ exports.base64ToHex = base64ToHex;
159
+ const hexToBase64 = (input, targetEncoding) => {
160
+ let hex = typeof input === 'string' ? input : input.toString(16);
161
+ if (hex.length % 2 === 1) {
162
+ hex = `0${hex}`;
163
+ }
164
+ return u8a.toString(u8a.fromString(hex, 'base16'), targetEncoding ? targetEncoding : 'base64pad');
165
+ };
166
+ exports.hexToBase64 = hexToBase64;
167
+ const hexToPEM = (hex, type) => {
168
+ const base64 = (0, exports.hexToBase64)(hex, 'base64pad');
169
+ const headerKey = type === 'private' ? 'RSA PRIVATE KEY' : 'PUBLIC KEY';
170
+ if (type === 'private') {
171
+ const pem = derToPEM(base64, headerKey);
172
+ try {
173
+ (0, exports.PEMToJwk)(pem); // We only use it to test the private key
174
+ return pem;
175
+ }
176
+ catch (error) {
177
+ return derToPEM(base64, 'PRIVATE KEY');
178
+ }
179
+ }
180
+ return derToPEM(base64, headerKey);
181
+ };
182
+ exports.hexToPEM = hexToPEM;
183
+ function PEMToDer(pem) {
184
+ return pem.replace(/(-----(BEGIN|END) CERTIFICATE-----|[\n\r])/g, '');
185
+ }
186
+ exports.PEMToDer = PEMToDer;
187
+ function derToPEM(cert, headerKey) {
188
+ const key = headerKey !== null && headerKey !== void 0 ? headerKey : 'CERTIFICATE';
189
+ const matches = cert.match(/.{1,64}/g);
190
+ if (!matches) {
191
+ throw Error('Invalid cert input value supplied');
192
+ }
193
+ return `-----BEGIN ${key}-----\n${matches.join('\n')}\n-----END ${key}-----\n`;
194
+ }
195
+ exports.derToPEM = derToPEM;
196
+ //# sourceMappingURL=x509-utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"x509-utils.js","sourceRoot":"","sources":["../../src/x509/x509-utils.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,42 @@
1
+ import { Certificate } from 'pkijs';
2
+ export type DNInfo = {
3
+ DN: string;
4
+ attributes: Record<string, string>;
5
+ };
6
+ export type CertInfo = {
7
+ certificate?: Certificate;
8
+ notBefore: Date;
9
+ notAfter: Date;
10
+ publicKeyJWK?: any;
11
+ issuer: {
12
+ dn: DNInfo;
13
+ };
14
+ subject: {
15
+ dn: DNInfo;
16
+ };
17
+ };
18
+ export type X509ValidationResult = {
19
+ error: boolean;
20
+ critical: boolean;
21
+ message: string;
22
+ verificationTime: Date;
23
+ certificateChain?: Array<CertInfo>;
24
+ };
25
+ /**
26
+ *
27
+ * @param pemOrDerChain The order must be that the Certs signing another cert must come one after another. So first the signing cert, then any cert signing that cert and so on
28
+ * @param trustedPEMs
29
+ * @param verificationTime
30
+ * @param opts
31
+ */
32
+ export declare const validateX509CertificateChain: ({ chain: pemOrDerChain, trustAnchors, verificationTime, opts, }: {
33
+ chain: (Uint8Array | string)[];
34
+ trustAnchors?: string[];
35
+ verificationTime?: Date;
36
+ opts?: {
37
+ trustRootWhenNoAnchors: boolean;
38
+ };
39
+ }) => Promise<X509ValidationResult>;
40
+ export declare const getIssuerDN: (cert: Certificate) => DNInfo;
41
+ export declare const getSubjectDN: (cert: Certificate) => DNInfo;
42
+ //# sourceMappingURL=x509-validator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"x509-validator.d.ts","sourceRoot":"","sources":["../../src/x509/x509-validator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAyB,WAAW,EAAwE,MAAM,OAAO,CAAA;AAGhI,MAAM,MAAM,MAAM,GAAG;IACnB,EAAE,EAAE,MAAM,CAAA;IACV,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CACnC,CAAA;AAED,MAAM,MAAM,QAAQ,GAAG;IACrB,WAAW,CAAC,EAAE,WAAW,CAAA;IACzB,SAAS,EAAE,IAAI,CAAA;IACf,QAAQ,EAAE,IAAI,CAAA;IACd,YAAY,CAAC,EAAE,GAAG,CAAA;IAClB,MAAM,EAAE;QACN,EAAE,EAAE,MAAM,CAAA;KACX,CAAA;IACD,OAAO,EAAE;QACP,EAAE,EAAE,MAAM,CAAA;KACX,CAAA;CACF,CAAA;AAED,MAAM,MAAM,oBAAoB,GAAG;IACjC,KAAK,EAAE,OAAO,CAAA;IACd,QAAQ,EAAE,OAAO,CAAA;IACjB,OAAO,EAAE,MAAM,CAAA;IACf,gBAAgB,EAAE,IAAI,CAAA;IACtB,gBAAgB,CAAC,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAA;CACnC,CAAA;AAqBD;;;;;;GAMG;AACH,eAAO,MAAM,4BAA4B,oEAKtC;IACD,KAAK,EAAE,CAAC,UAAU,GAAG,MAAM,CAAC,EAAE,CAAA;IAC9B,YAAY,CAAC,EAAE,MAAM,EAAE,CAAA;IACvB,gBAAgB,CAAC,EAAE,IAAI,CAAA;IACvB,IAAI,CAAC,EAAE;QAAE,sBAAsB,EAAE,OAAO,CAAA;KAAE,CAAA;CAC3C,KAAG,QAAQ,oBAAoB,CAsD/B,CAAA;AAgBD,eAAO,MAAM,WAAW,SAAU,WAAW,KAAG,MAK/C,CAAA;AAED,eAAO,MAAM,YAAY,SAAU,WAAW,KAAG,MAKhD,CAAA"}
@@ -0,0 +1,134 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.getSubjectDN = exports.getIssuerDN = exports.validateX509CertificateChain = void 0;
13
+ const pkijs_1 = require("pkijs");
14
+ const x509_utils_1 = require("./x509-utils");
15
+ const defaultCryptoEngine = () => {
16
+ if (typeof self !== 'undefined') {
17
+ if ('crypto' in self) {
18
+ let engineName = 'webcrypto';
19
+ if ('webkitSubtle' in self.crypto) {
20
+ engineName = 'safari';
21
+ }
22
+ (0, pkijs_1.setEngine)(engineName, new pkijs_1.CryptoEngine({ name: engineName, crypto: crypto }));
23
+ }
24
+ }
25
+ else if (typeof crypto !== 'undefined' && 'webcrypto' in crypto) {
26
+ const name = 'NodeJS ^15';
27
+ const nodeCrypto = crypto.webcrypto;
28
+ // @ts-ignore
29
+ (0, pkijs_1.setEngine)(name, new pkijs_1.CryptoEngine({ name, crypto: nodeCrypto }));
30
+ }
31
+ else if (typeof crypto !== 'undefined' && typeof crypto.subtle !== 'undefined') {
32
+ const name = 'crypto';
33
+ (0, pkijs_1.setEngine)(name, new pkijs_1.CryptoEngine({ name, crypto: crypto }));
34
+ }
35
+ };
36
+ /**
37
+ *
38
+ * @param pemOrDerChain The order must be that the Certs signing another cert must come one after another. So first the signing cert, then any cert signing that cert and so on
39
+ * @param trustedPEMs
40
+ * @param verificationTime
41
+ * @param opts
42
+ */
43
+ const validateX509CertificateChain = (_a) => __awaiter(void 0, [_a], void 0, function* ({ chain: pemOrDerChain, trustAnchors, verificationTime = new Date(), opts = { trustRootWhenNoAnchors: false }, }) {
44
+ const { trustRootWhenNoAnchors = false } = opts;
45
+ const trustedPEMs = trustRootWhenNoAnchors && !trustAnchors ? [pemOrDerChain[pemOrDerChain.length - 1]] : trustAnchors;
46
+ if (pemOrDerChain.length === 0) {
47
+ return {
48
+ error: true,
49
+ critical: true,
50
+ message: 'Certificate chain in DER or PEM format must not be empty',
51
+ verificationTime,
52
+ };
53
+ }
54
+ const certs = pemOrDerChain.map(x509_utils_1.pemOrDerToX509Certificate);
55
+ const trustedCerts = trustedPEMs ? trustedPEMs.map(x509_utils_1.pemOrDerToX509Certificate) : undefined;
56
+ defaultCryptoEngine();
57
+ const validationEngine = new pkijs_1.CertificateChainValidationEngine({
58
+ certs /*crls: [crl1], ocsps: [ocsp1], */,
59
+ checkDate: verificationTime,
60
+ trustedCerts,
61
+ });
62
+ const verification = yield validationEngine.verify();
63
+ if (!verification.result || !verification.certificatePath) {
64
+ return {
65
+ error: true,
66
+ critical: true,
67
+ message: verification.resultMessage !== '' ? verification.resultMessage : `Certificate chain validation failed.`,
68
+ verificationTime,
69
+ };
70
+ }
71
+ const subtle = (0, pkijs_1.getCrypto)(true).subtle;
72
+ const certPath = verification.certificatePath;
73
+ const certInfos = yield Promise.all(certPath.map((certificate) => __awaiter(void 0, void 0, void 0, function* () {
74
+ const pk = yield certificate.getPublicKey();
75
+ return {
76
+ issuer: { dn: (0, exports.getIssuerDN)(certificate) },
77
+ subject: { dn: (0, exports.getSubjectDN)(certificate) },
78
+ publicKeyJWK: yield subtle.exportKey('jwk', pk),
79
+ notBefore: certificate.notBefore.value,
80
+ notAfter: certificate.notAfter.value,
81
+ // certificate
82
+ };
83
+ })));
84
+ return {
85
+ error: false,
86
+ critical: false,
87
+ message: `Certificate chain was valid`,
88
+ verificationTime,
89
+ certificateChain: certInfos,
90
+ };
91
+ });
92
+ exports.validateX509CertificateChain = validateX509CertificateChain;
93
+ const rdnmap = {
94
+ '2.5.4.6': 'C',
95
+ '2.5.4.10': 'O',
96
+ '2.5.4.11': 'OU',
97
+ '2.5.4.3': 'CN',
98
+ '2.5.4.7': 'L',
99
+ '2.5.4.8': 'ST',
100
+ '2.5.4.12': 'T',
101
+ '2.5.4.42': 'GN',
102
+ '2.5.4.43': 'I',
103
+ '2.5.4.4': 'SN',
104
+ '1.2.840.113549.1.9.1': 'E-mail',
105
+ };
106
+ const getIssuerDN = (cert) => {
107
+ return {
108
+ DN: getDNString(cert.issuer.typesAndValues),
109
+ attributes: getDNObject(cert.issuer.typesAndValues),
110
+ };
111
+ };
112
+ exports.getIssuerDN = getIssuerDN;
113
+ const getSubjectDN = (cert) => {
114
+ return {
115
+ DN: getDNString(cert.subject.typesAndValues),
116
+ attributes: getDNObject(cert.subject.typesAndValues),
117
+ };
118
+ };
119
+ exports.getSubjectDN = getSubjectDN;
120
+ const getDNObject = (typesAndValues) => {
121
+ var _a;
122
+ const DN = {};
123
+ for (const typeAndValue of typesAndValues) {
124
+ const type = (_a = rdnmap[typeAndValue.type]) !== null && _a !== void 0 ? _a : typeAndValue.type;
125
+ DN[type] = typeAndValue.value.getValue();
126
+ }
127
+ return DN;
128
+ };
129
+ const getDNString = (typesAndValues) => {
130
+ return Object.entries(getDNObject(typesAndValues))
131
+ .map(([key, value]) => `${key}=${value}`)
132
+ .join(',');
133
+ };
134
+ //# sourceMappingURL=x509-validator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"x509-validator.js","sourceRoot":"","sources":["../../src/x509/x509-validator.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,40 @@
1
+ {
2
+ "name": "@sphereon/ssi-sdk-ext.x509-utils",
3
+ "description": "Sphereon SSI-SDK plugin functions for X.509 Certificate handling.",
4
+ "version": "0.24.1-unstable.9+9d7f5c6",
5
+ "source": "src/index.ts",
6
+ "main": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "scripts": {
9
+ "build": "tsc --build",
10
+ "build:clean": "tsc --build --clean && tsc --build"
11
+ },
12
+ "dependencies": {
13
+ "@trust/keyto": "^1.0.1",
14
+ "debug": "^4.3.4",
15
+ "pkijs": "^3.2.4",
16
+ "uint8arrays": "^3.1.1"
17
+ },
18
+ "devDependencies": {
19
+ "typescript": "^5.5.4"
20
+ },
21
+ "files": [
22
+ "dist/**/*",
23
+ "src/**/*",
24
+ "README.md",
25
+ "LICENSE"
26
+ ],
27
+ "private": false,
28
+ "publishConfig": {
29
+ "access": "public"
30
+ },
31
+ "repository": "git@github.com:Sphereon-OpenSource/ssi-sdk.git",
32
+ "author": "Sphereon <dev@sphereon.com>",
33
+ "license": "Apache-2.0",
34
+ "keywords": [
35
+ "JWK",
36
+ "DID",
37
+ "Veramo"
38
+ ],
39
+ "gitHead": "9d7f5c6eb96a1b39e7b98649a31139da604730bf"
40
+ }
package/src/index.ts ADDED
@@ -0,0 +1,6 @@
1
+ /**
2
+ *
3
+ * @packageDocumentation
4
+ */
5
+ export * from './types'
6
+ export * from './x509'
@@ -0,0 +1,16 @@
1
+ export enum JwkKeyUse {
2
+ Encryption = 'enc',
3
+ Signature = 'sig',
4
+ }
5
+
6
+ export type HashAlgorithm = 'SHA-256' | 'SHA-512'
7
+
8
+ export type KeyVisibility = 'public' | 'private'
9
+
10
+ export interface X509Opts {
11
+ cn?: string // The certificate Common Name. Will be used as the KID for the private key. Uses alias if not provided.
12
+ privateKeyPEM?: string // Optional as you also need to provide it in hex format, but advisable to use it
13
+ certificatePEM?: string // Optional, as long as the certificate then is part of the certificateChainPEM
14
+ certificateChainURL?: string // Certificate chain URL. If used this is where the certificateChainPEM will be hosted/found.
15
+ certificateChainPEM?: string // Base64 (not url!) encoded DER certificate chain. Please provide even if certificateChainURL is used!
16
+ }
@@ -0,0 +1,4 @@
1
+ export * from './rsa-key'
2
+ export * from './rsa-signer'
3
+ export * from './x509-utils'
4
+ export * from './x509-validator'
@@ -0,0 +1,81 @@
1
+ import * as u8a from 'uint8arrays'
2
+ import { HashAlgorithm } from '../types'
3
+
4
+ import { derToPEM } from './x509-utils'
5
+
6
+ export type RSASignatureSchemes = 'RSASSA-PKCS1-V1_5' | 'RSA-PSS'
7
+
8
+ export type RSAEncryptionSchemes = 'RSAES-PKCS-v1_5 ' | 'RSAES-OAEP'
9
+
10
+ const usage = (jwk: JsonWebKey): KeyUsage[] => {
11
+ if (jwk.key_ops && jwk.key_ops.length > 0) {
12
+ return jwk.key_ops as KeyUsage[]
13
+ }
14
+ if (jwk.use) {
15
+ const usages: KeyUsage[] = []
16
+ if (jwk.use.includes('sig')) {
17
+ usages.push('sign', 'verify')
18
+ } else if (jwk.use.includes('enc')) {
19
+ usages.push('encrypt', 'decrypt')
20
+ }
21
+ if (usages.length > 0) {
22
+ return usages
23
+ }
24
+ }
25
+ if (jwk.kty === 'RSA') {
26
+ if (jwk.d) {
27
+ return jwk.alg?.toUpperCase()?.includes('QAEP') ? ['encrypt'] : ['sign']
28
+ }
29
+ return jwk.alg?.toUpperCase()?.includes('QAEP') ? ['decrypt'] : ['verify']
30
+ }
31
+ // "decrypt" | "deriveBits" | "deriveKey" | "encrypt" | "sign" | "unwrapKey" | "verify" | "wrapKey";
32
+ return jwk.d && jwk.kty !== 'RSA' ? ['sign', 'decrypt', 'verify', 'encrypt'] : ['verify']
33
+ }
34
+
35
+ export const signAlgorithmToSchemeAndHashAlg = (signingAlg: string) => {
36
+ const alg = signingAlg.toUpperCase()
37
+ let scheme: RSAEncryptionSchemes | RSASignatureSchemes
38
+ if (alg.startsWith('RS')) {
39
+ scheme = 'RSASSA-PKCS1-V1_5'
40
+ } else if (alg.startsWith('PS')) {
41
+ scheme = 'RSA-PSS'
42
+ } else {
43
+ throw Error(`Invalid signing algorithm supplied ${signingAlg}`)
44
+ }
45
+
46
+ const hashAlgorithm = `SHA-${alg.substring(2)}` as HashAlgorithm
47
+ return { scheme, hashAlgorithm }
48
+ }
49
+
50
+ export const cryptoSubtleImportRSAKey = async (
51
+ jwk: JsonWebKey,
52
+ scheme: RSAEncryptionSchemes | RSASignatureSchemes,
53
+ hashAlgorithm?: HashAlgorithm
54
+ ): Promise<CryptoKey> => {
55
+ const hashName = hashAlgorithm ? hashAlgorithm : jwk.alg ? `SHA-${jwk.alg.substring(2)}` : 'SHA-256'
56
+
57
+ const importParams: RsaHashedImportParams = { name: scheme, hash: hashName }
58
+ return await crypto.subtle.importKey('jwk', jwk as JsonWebKey, importParams, false, usage(jwk))
59
+ }
60
+
61
+ export const generateRSAKeyAsPEM = async (
62
+ scheme: RSAEncryptionSchemes | RSASignatureSchemes,
63
+ hashAlgorithm?: HashAlgorithm,
64
+ modulusLength?: number
65
+ ): Promise<string> => {
66
+ const hashName = hashAlgorithm ? hashAlgorithm : 'SHA-256'
67
+
68
+ const params: RsaHashedKeyGenParams = {
69
+ name: scheme,
70
+ hash: hashName,
71
+ modulusLength: modulusLength ? modulusLength : 2048,
72
+ publicExponent: new Uint8Array([1, 0, 1]),
73
+ }
74
+ const keyUsage: KeyUsage[] = scheme === 'RSA-PSS' || scheme === 'RSASSA-PKCS1-V1_5' ? ['sign', 'verify'] : ['encrypt', 'decrypt']
75
+
76
+ const keypair = await crypto.subtle.generateKey(params, true, keyUsage)
77
+ const pkcs8 = await crypto.subtle.exportKey('pkcs8', keypair.privateKey)
78
+
79
+ const uint8Array = new Uint8Array(pkcs8)
80
+ return derToPEM(u8a.toString(uint8Array, 'base64pad'), 'RSA PRIVATE KEY')
81
+ }
@@ -0,0 +1,80 @@
1
+ import * as u8a from 'uint8arrays'
2
+ import { HashAlgorithm, KeyVisibility } from '../types'
3
+ import { cryptoSubtleImportRSAKey, RSAEncryptionSchemes, RSASignatureSchemes } from './rsa-key'
4
+ import { PEMToJwk } from './x509-utils'
5
+
6
+ export class RSASigner {
7
+ private readonly hashAlgorithm: HashAlgorithm
8
+ private readonly jwk: JsonWebKey
9
+
10
+ private key: CryptoKey | undefined
11
+ private readonly scheme: RSAEncryptionSchemes | RSASignatureSchemes
12
+
13
+ /**
14
+ *
15
+ * @param key Either in PEM or JWK format (no raw hex keys here!)
16
+ * @param opts The algorithm and signature/encryption schemes
17
+ */
18
+ constructor(
19
+ key: string | JsonWebKey,
20
+ opts?: { hashAlgorithm?: HashAlgorithm; scheme?: RSAEncryptionSchemes | RSASignatureSchemes; visibility?: KeyVisibility }
21
+ ) {
22
+ if (typeof key === 'string') {
23
+ this.jwk = PEMToJwk(key, opts?.visibility)
24
+ } else {
25
+ this.jwk = key
26
+ }
27
+
28
+ this.hashAlgorithm = opts?.hashAlgorithm ?? 'SHA-256'
29
+ this.scheme = opts?.scheme ?? 'RSA-PSS'
30
+ }
31
+
32
+ private getImportParams(): AlgorithmIdentifier | RsaPssParams {
33
+ if (this.scheme === 'RSA-PSS') {
34
+ return { name: this.scheme, saltLength: 32 }
35
+ }
36
+ // console.log({ name: this.scheme /*, hash: this.hashAlgorithm*/ })
37
+ return { name: this.scheme /*, hash: this.hashAlgorithm*/ }
38
+ }
39
+
40
+ private async getKey(): Promise<CryptoKey> {
41
+ if (!this.key) {
42
+ this.key = await cryptoSubtleImportRSAKey(this.jwk, this.scheme, this.hashAlgorithm)
43
+ }
44
+ return this.key
45
+ }
46
+
47
+ private bufferToString(buf: ArrayBuffer) {
48
+ const uint8Array = new Uint8Array(buf)
49
+ return u8a.toString(uint8Array, 'base64url') // Needs to be base64url for JsonWebSignature2020. Don't change!
50
+ }
51
+
52
+ public async sign(data: Uint8Array): Promise<string> {
53
+ const input = data
54
+ const key = await this.getKey()
55
+ const signature = this.bufferToString(await crypto.subtle.sign(this.getImportParams(), key, input))
56
+ if (!signature) {
57
+ throw Error('Could not sign input data')
58
+ }
59
+
60
+ // base64url signature
61
+ return signature
62
+ }
63
+
64
+ public async verify(data: string | Uint8Array, signature: string): Promise<boolean> {
65
+ const jws = signature.includes('.') ? signature.split('.')[2] : signature
66
+
67
+ const input = typeof data == 'string' ? u8a.fromString(data, 'utf-8') : data
68
+
69
+ let key = await this.getKey()
70
+ if (!key.usages.includes('verify')) {
71
+ const verifyJwk = { ...this.jwk }
72
+ delete verifyJwk.d
73
+ delete verifyJwk.use
74
+ delete verifyJwk.key_ops
75
+ key = await cryptoSubtleImportRSAKey(verifyJwk, this.scheme, this.hashAlgorithm)
76
+ }
77
+ const verificationResult = await crypto.subtle.verify(this.getImportParams(), key, u8a.fromString(jws, 'base64url'), input)
78
+ return verificationResult
79
+ }
80
+ }