1 | const { deprecate } = require('util')
|
2 |
|
3 | const { createPublicKey, createPrivateKey, createSecretKey, KeyObject } = require('../help/key_object')
|
4 | const base64url = require('../help/base64url')
|
5 | const isObject = require('../help/is_object')
|
6 | const { jwkToPem } = require('../help/key_utils')
|
7 | const errors = require('../errors')
|
8 |
|
9 | const RSAKey = require('./key/rsa')
|
10 | const ECKey = require('./key/ec')
|
11 | const OKPKey = require('./key/okp')
|
12 | const OctKey = require('./key/oct')
|
13 |
|
14 | const importable = new Set(['string', 'buffer', 'object'])
|
15 |
|
16 | const mergedParameters = (target = {}, source = {}) => {
|
17 | return {
|
18 | alg: source.alg,
|
19 | key_ops: source.key_ops,
|
20 | kid: source.kid,
|
21 | use: source.use,
|
22 | x5c: source.x5c,
|
23 | x5t: source.x5t,
|
24 | 'x5t#S256': source['x5t#S256'],
|
25 | ...target
|
26 | }
|
27 | }
|
28 |
|
29 | const openSSHpublicKey = /^[a-zA-Z0-9-]+ AAAA(?:[0-9A-Za-z+/])+(?:==|=)?(?: .*)?$/
|
30 |
|
31 | const asKey = (key, parameters, { calculateMissingRSAPrimes = false } = {}) => {
|
32 | let privateKey, publicKey, secret
|
33 |
|
34 | if (!importable.has(typeof key)) {
|
35 | throw new TypeError('key argument must be a string, buffer or an object')
|
36 | }
|
37 |
|
38 | if (parameters !== undefined && !isObject(parameters)) {
|
39 | throw new TypeError('parameters argument must be a plain object when provided')
|
40 | }
|
41 |
|
42 | if (key instanceof KeyObject) {
|
43 | switch (key.type) {
|
44 | case 'private':
|
45 | privateKey = key
|
46 | break
|
47 | case 'public':
|
48 | publicKey = key
|
49 | break
|
50 | case 'secret':
|
51 | secret = key
|
52 | break
|
53 | }
|
54 | } else if (typeof key === 'object' && key && 'kty' in key && key.kty === 'oct') {
|
55 | try {
|
56 | secret = createSecretKey(base64url.decodeToBuffer(key.k))
|
57 | } catch (err) {
|
58 | if (!('k' in key)) {
|
59 | secret = { type: 'secret' }
|
60 | }
|
61 | }
|
62 | parameters = mergedParameters(parameters, key)
|
63 | } else if (typeof key === 'object' && key && 'kty' in key) {
|
64 | ({ calculateMissingRSAPrimes = false } = parameters || { calculateMissingRSAPrimes })
|
65 | let pem
|
66 |
|
67 | try {
|
68 | pem = jwkToPem(key, { calculateMissingRSAPrimes })
|
69 | } catch (err) {
|
70 | if (err instanceof errors.JOSEError) {
|
71 | throw err
|
72 | }
|
73 | }
|
74 |
|
75 | if (pem && key.d) {
|
76 | privateKey = createPrivateKey(pem)
|
77 | } else if (pem) {
|
78 | publicKey = createPublicKey(pem)
|
79 | }
|
80 |
|
81 | parameters = mergedParameters({}, key)
|
82 | } else if (key && (typeof key === 'object' || typeof key === 'string')) {
|
83 | try {
|
84 | privateKey = createPrivateKey(key)
|
85 | } catch (err) {
|
86 | if (err instanceof errors.JOSEError) {
|
87 | throw err
|
88 | }
|
89 | }
|
90 |
|
91 | try {
|
92 | publicKey = createPublicKey(key)
|
93 | if (key.startsWith('-----BEGIN CERTIFICATE-----') && (!parameters || !('x5c' in parameters))) {
|
94 | parameters = mergedParameters(parameters, {
|
95 | x5c: [key.replace(/(?:-----(?:BEGIN|END) CERTIFICATE-----|\s)/g, '')]
|
96 | })
|
97 | }
|
98 | } catch (err) {
|
99 | if (err instanceof errors.JOSEError) {
|
100 | throw err
|
101 | }
|
102 | }
|
103 |
|
104 | try {
|
105 |
|
106 |
|
107 | if (!key.includes('-----BEGIN') && !openSSHpublicKey.test(key.toString('ascii').replace(/[\r\n]/g, ''))) {
|
108 | secret = createSecretKey(Buffer.isBuffer(key) ? key : Buffer.from(key))
|
109 | }
|
110 | } catch (err) {}
|
111 | }
|
112 |
|
113 | const keyObject = privateKey || publicKey || secret
|
114 |
|
115 | if (privateKey || publicKey) {
|
116 | switch (keyObject.asymmetricKeyType) {
|
117 | case 'rsa':
|
118 | return new RSAKey(keyObject, parameters)
|
119 | case 'ec':
|
120 | return new ECKey(keyObject, parameters)
|
121 | case 'ed25519':
|
122 | case 'ed448':
|
123 | case 'x25519':
|
124 | case 'x448':
|
125 | return new OKPKey(keyObject, parameters)
|
126 | default:
|
127 | throw new errors.JOSENotSupported('only RSA, EC and OKP asymmetric keys are supported')
|
128 | }
|
129 | } else if (secret) {
|
130 | return new OctKey(keyObject, parameters)
|
131 | }
|
132 |
|
133 | throw new errors.JWKImportFailed('key import failed')
|
134 | }
|
135 |
|
136 | module.exports = asKey
|
137 | Object.defineProperty(asKey, 'deprecated', {
|
138 | value: deprecate((key, parameters) => { return asKey(key, parameters, { calculateMissingRSAPrimes: true }) }, 'JWK.importKey() is deprecated, use JWK.asKey() instead'),
|
139 | enumerable: false
|
140 | })
|