| 
									
										
										
										
											2018-12-16 02:54:57 -07:00
										 |  |  | // Copyright 2016-2018 AJ ONeal. All rights reserved
 | 
					
						
							|  |  |  | /* This Source Code Form is subject to the terms of the Mozilla Public | 
					
						
							|  |  |  |  * License, v. 2.0. If a copy of the MPL was not distributed with this | 
					
						
							|  |  |  |  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 | 
					
						
							|  |  |  | 'use strict'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | var RSA = module.exports; | 
					
						
							|  |  |  | RSA.utils = {}; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | try { | 
					
						
							|  |  |  |   require('buffer-v6-polyfill'); | 
					
						
							|  |  |  | } catch(e) { | 
					
						
							|  |  |  |   /* ignore */ | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-03-15 09:12:43 -06:00
										 |  |  | var Keypairs = require('keypairs'); | 
					
						
							| 
									
										
										
										
											2018-12-16 02:54:57 -07:00
										 |  |  | var RSACSR = require('./rsa-csr'); | 
					
						
							|  |  |  | var NOBJ = {}; | 
					
						
							|  |  |  | var DEFAULT_BITLEN = 2048; | 
					
						
							|  |  |  | var DEFAULT_EXPONENT = 0x10001; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | RSA.generateKeypair = function (options, cb, extra1, extra2) { | 
					
						
							|  |  |  |   var length; | 
					
						
							|  |  |  |   var exponent; | 
					
						
							|  |  |  |   if ('function' === typeof extra2) { | 
					
						
							|  |  |  |     length = options || DEFAULT_BITLEN; | 
					
						
							|  |  |  |     exponent = cb || DEFAULT_EXPONENT; | 
					
						
							|  |  |  |     options = extra1 || NOBJ; | 
					
						
							|  |  |  |     cb = extra2; | 
					
						
							|  |  |  |   } else { | 
					
						
							|  |  |  |     if (!options) { options = NOBJ; } | 
					
						
							|  |  |  |     length = options.bitlen || DEFAULT_BITLEN; | 
					
						
							|  |  |  |     exponent = options.exp || DEFAULT_EXPONENT; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   try { | 
					
						
							|  |  |  |     var keypair = require('./generate-privkey.js')(length, exponent); | 
					
						
							|  |  |  |     keypair.thumbprint = RSA.thumbprint(keypair); | 
					
						
							|  |  |  |     cb(null, keypair); | 
					
						
							|  |  |  |   } catch(e) { | 
					
						
							|  |  |  |     cb(e); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | RSA.import = function (options) { | 
					
						
							|  |  |  |   options = JSON.parse(JSON.stringify(options)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // Private Keys
 | 
					
						
							|  |  |  |   if (options.privateKeyPem) { | 
					
						
							|  |  |  |     if (!options.privateKeyJwk) { | 
					
						
							| 
									
										
										
										
											2019-03-14 12:02:04 -06:00
										 |  |  |       options.privateKeyJwk = Keypairs._importSync({ pem: options.privateKeyPem }); | 
					
						
							| 
									
										
										
										
											2018-12-16 02:54:57 -07:00
										 |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   if (options.privateKeyJwk) { | 
					
						
							|  |  |  |     if (!options.privateKeyPem) { | 
					
						
							| 
									
										
										
										
											2019-03-14 12:02:04 -06:00
										 |  |  |       options.privateKeyPem = Keypairs._exportSync({ | 
					
						
							| 
									
										
										
										
											2018-12-16 02:54:57 -07:00
										 |  |  |         jwk: options.privateKeyJwk | 
					
						
							|  |  |  |       , format: options.format || 'pkcs1' | 
					
						
							|  |  |  |       , encoding: options.encoding || 'pem' | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // Public Keys
 | 
					
						
							|  |  |  |   if (options.publicKeyPem || options.privateKeyPem) { | 
					
						
							|  |  |  |     if (!options.publicKeyJwk) { | 
					
						
							| 
									
										
										
										
											2019-03-14 12:02:04 -06:00
										 |  |  |       options.publicKeyJwk = Keypairs._importSync({ | 
					
						
							| 
									
										
										
										
											2018-12-16 02:54:57 -07:00
										 |  |  |         pem: options.publicKeyPem || options.privateKeyPem | 
					
						
							|  |  |  |       , public: true | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   if (options.publicKeyJwk || options.privateKeyJwk) { | 
					
						
							|  |  |  |     if (!options.publicKeyPem) { | 
					
						
							| 
									
										
										
										
											2019-03-14 12:02:04 -06:00
										 |  |  |       options.publicKeyPem = Keypairs._exportSync({ | 
					
						
							| 
									
										
										
										
											2018-12-16 02:54:57 -07:00
										 |  |  |         jwk: options.publicKeyJwk || options.privateKeyJwk | 
					
						
							|  |  |  |       , format: options.format || 'pkcs1' | 
					
						
							|  |  |  |       , encoding: options.encoding || 'pem' | 
					
						
							|  |  |  |       , public: true | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   if (!options.publicKeyPem) { | 
					
						
							|  |  |  |     throw new Error("Error: no keys were present to import"); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // Consistent CRLF
 | 
					
						
							|  |  |  |   if (options.privateKeyPem) { | 
					
						
							|  |  |  |     options.privateKeyPem = options.privateKeyPem | 
					
						
							|  |  |  |       .trim().replace(/[\r\n]+/g, '\r\n') + '\r\n'; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   options.publicKeyPem = options.publicKeyPem | 
					
						
							|  |  |  |     .trim().replace(/[\r\n]+/g, '\r\n') + '\r\n'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // Thumbprint
 | 
					
						
							|  |  |  |   if (!options.thumbprint) { | 
					
						
							|  |  |  |     options.thumbprint = RSA._thumbprint(options); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return options; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | RSA.exportPrivatePem = function (keypair) { | 
					
						
							|  |  |  |   keypair = RSA.import(keypair); | 
					
						
							|  |  |  |   return keypair.privateKeyPem; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | RSA.exportPublicPem = function (keypair) { | 
					
						
							|  |  |  |   keypair = RSA.import(keypair); | 
					
						
							|  |  |  |   return keypair.publicKeyPem; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | RSA.exportPrivateJwk = function (keypair) { | 
					
						
							|  |  |  |   keypair = RSA.import(keypair); | 
					
						
							|  |  |  |   return keypair.privateKeyJwk; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | RSA.exportPublicJwk = function (keypair) { | 
					
						
							|  |  |  |   if (!keypair.publicKeyJwk) { | 
					
						
							|  |  |  |     keypair = RSA.import(keypair); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   return keypair.publicKeyJwk; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | RSA.signJws = RSA.generateJws = RSA.generateSignatureJws = RSA.generateSignatureJwk = | 
					
						
							|  |  |  | function (keypair, header, protect, payload) { | 
					
						
							|  |  |  | // old   (keypair, payload, nonce)
 | 
					
						
							|  |  |  |   var nonce; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   keypair = RSA.import(keypair); | 
					
						
							|  |  |  |   keypair.publicKeyJwk = RSA.exportPublicJwk(keypair); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if ('string' === typeof protect || ('undefined' === typeof protect && 'undefined' === typeof payload)) { | 
					
						
							|  |  |  |     console.warn("deprecation notice: new signature for signJws(keypair, header, protect, payload)"); | 
					
						
							|  |  |  |     // old API
 | 
					
						
							|  |  |  |     payload = header; | 
					
						
							|  |  |  |     nonce = protect; | 
					
						
							|  |  |  |     protect = undefined; | 
					
						
							|  |  |  |     header = { | 
					
						
							|  |  |  |       alg: "RS256" | 
					
						
							|  |  |  |     , jwk: keypair.publicKeyJwk | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  |     protect = { nonce: nonce }; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // Compute JWS signature
 | 
					
						
							|  |  |  |   var protectedHeader = ""; | 
					
						
							|  |  |  |   if (protect) { | 
					
						
							|  |  |  |     protectedHeader = JSON.stringify(protect); // { alg: prot.alg, nonce: prot.nonce, url: prot.url });
 | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   var protected64 = RSA.utils.toWebsafeBase64(Buffer.from(protectedHeader).toString('base64')); | 
					
						
							|  |  |  |   var payload64 = RSA.utils.toWebsafeBase64(payload.toString('base64')); | 
					
						
							|  |  |  |   var raw = protected64 + "." + payload64; | 
					
						
							|  |  |  |   var pem = RSA.exportPrivatePem(keypair); | 
					
						
							|  |  |  |   var signer = require('crypto').createSign("RSA-SHA256"); | 
					
						
							|  |  |  |   signer.update(raw); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return { | 
					
						
							|  |  |  |     header: header | 
					
						
							|  |  |  |   , protected: protected64 | 
					
						
							|  |  |  |   , payload: payload64 | 
					
						
							|  |  |  |   , signature: signer.sign(pem, 'base64') | 
					
						
							|  |  |  |     .replace(/\+/g, '-') | 
					
						
							|  |  |  |     .replace(/\//g, '_') | 
					
						
							|  |  |  |     .replace(/=/g, '') | 
					
						
							|  |  |  |   }; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | RSA.generateCsrPem = function (keypair, domains) { | 
					
						
							|  |  |  |   keypair = RSA.import(keypair); | 
					
						
							|  |  |  |   return RSACSR.sync({ jwk: keypair.privateKeyJwk, domains: domains }); | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | RSA.generateCsrDer = function (keypair, domains) { | 
					
						
							|  |  |  |   keypair = RSA.import(keypair); | 
					
						
							|  |  |  |   return RSACSR.sync({ | 
					
						
							|  |  |  |     jwk: keypair.privateKeyJwk | 
					
						
							|  |  |  |   , domains: domains | 
					
						
							|  |  |  |   , encoding: 'der' | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | RSA.generateCsrDerWeb64 =RSA.generateCsrWeb64 = function (keypair, names) { | 
					
						
							|  |  |  |   var buf = RSA.generateCsrDer(keypair, names); | 
					
						
							|  |  |  |   var b64 = buf.toString('base64'); | 
					
						
							|  |  |  |   return RSA.utils.toWebsafeBase64(b64); | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | RSA._thumbprintInput = function (n, e) { | 
					
						
							|  |  |  |   // #L147 const rsaThumbprintTemplate = `{"e":"%s","kty":"RSA","n":"%s"}`
 | 
					
						
							|  |  |  |   return Buffer.from('{"e":"'+ e + '","kty":"RSA","n":"'+ n +'"}', 'ascii'); | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | RSA._thumbprint = function (keypair) { | 
					
						
							|  |  |  |   var publicKeyJwk = keypair.publicKeyJwk; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (!publicKeyJwk.e || !publicKeyJwk.n) { | 
					
						
							|  |  |  |     throw new Error("You must provide an RSA jwk with 'e' and 'n' (the public components)"); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   var input = RSA._thumbprintInput(publicKeyJwk.n, publicKeyJwk.e); | 
					
						
							|  |  |  |   var base64Digest = require('crypto').createHash('sha256').update(input).digest('base64'); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return RSA.utils.toWebsafeBase64(base64Digest); | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | RSA.thumbprint = function (keypair) { | 
					
						
							|  |  |  |   if (!keypair.publicKeyJwk) { | 
					
						
							|  |  |  |     keypair.publicKeyJwk = RSA.exportPublicJwk(keypair); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   return RSA._thumbprint(keypair); | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | RSA.utils.toWebsafeBase64 = function (b64) { | 
					
						
							|  |  |  |   return b64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g,""); | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | RSA.exportPrivateKey = RSA.exportPrivatePem; | 
					
						
							|  |  |  | RSA.exportPublicKey = RSA.exportPublicPem; |