214 lines
		
	
	
		
			6.0 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
		
		
			
		
	
	
			214 lines
		
	
	
		
			6.0 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
|  | // 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 */ | ||
|  | } | ||
|  | 
 | ||
|  | var Rasha = require('./rasha'); | ||
|  | 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) { | ||
|  |       options.privateKeyJwk = Rasha.importSync({ pem: options.privateKeyPem }); | ||
|  |     } | ||
|  |   } | ||
|  |   if (options.privateKeyJwk) { | ||
|  |     if (!options.privateKeyPem) { | ||
|  |       options.privateKeyPem = Rasha.exportSync({ | ||
|  |         jwk: options.privateKeyJwk | ||
|  |       , format: options.format || 'pkcs1' | ||
|  |       , encoding: options.encoding || 'pem' | ||
|  |       }); | ||
|  |     } | ||
|  |   } | ||
|  | 
 | ||
|  |   // Public Keys
 | ||
|  |   if (options.publicKeyPem || options.privateKeyPem) { | ||
|  |     if (!options.publicKeyJwk) { | ||
|  |       options.publicKeyJwk = Rasha.importSync({ | ||
|  |         pem: options.publicKeyPem || options.privateKeyPem | ||
|  |       , public: true | ||
|  |       }); | ||
|  |     } | ||
|  |   } | ||
|  |   if (options.publicKeyJwk || options.privateKeyJwk) { | ||
|  |     if (!options.publicKeyPem) { | ||
|  |       options.publicKeyPem = Rasha.exportSync({ | ||
|  |         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; |