UNPKG

4.44 kBJavaScriptView Raw
1const { deprecate } = require('util')
2
3const { createPublicKey, createPrivateKey, createSecretKey, KeyObject } = require('../help/key_object')
4const base64url = require('../help/base64url')
5const isObject = require('../help/is_object')
6const { jwkToPem } = require('../help/key_utils')
7const errors = require('../errors')
8
9const RSAKey = require('./key/rsa')
10const ECKey = require('./key/ec')
11const OKPKey = require('./key/okp')
12const OctKey = require('./key/oct')
13
14const importable = new Set(['string', 'buffer', 'object'])
15
16const 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
29const openSSHpublicKey = /^[a-zA-Z0-9-]+ AAAA(?:[0-9A-Za-z+/])+(?:==|=)?(?: .*)?$/
30
31const 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') { // symmetric key <Object>
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) { // assume JWK formatted asymmetric key <Object>
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')) { // <Object> | <string> | <Buffer> passed to crypto.createPrivateKey or crypto.createPublicKey or <Buffer> passed to crypto.createSecretKey
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 // this is to filter out invalid PEM keys and certs, i'll rather have them fail import then
106 // have them imported as symmetric "oct" keys
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
136module.exports = asKey
137Object.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})