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;
							 |