155 lines
		
	
	
		
			3.6 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			155 lines
		
	
	
		
			3.6 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| 'use strict';
 | |
| 
 | |
| var native = module.exports;
 | |
| 
 | |
| // XXX provided by caller: export
 | |
| var RSA = native;
 | |
| var PEM = require('../pem.js');
 | |
| var x509 = require('../x509.js');
 | |
| var ASN1 = require('../asn1/parser.js');
 | |
| 
 | |
| native.generate = function(opts) {
 | |
| 	opts.kty = 'RSA';
 | |
| 	return native._generate(opts).then(function(pair) {
 | |
| 		var format = opts.format;
 | |
| 		var encoding = opts.encoding;
 | |
| 
 | |
| 		// The easy way
 | |
| 		if ('json' === format && !encoding) {
 | |
| 			format = 'jwk';
 | |
| 			encoding = 'json';
 | |
| 		}
 | |
| 		if (
 | |
| 			('jwk' === format || !format) &&
 | |
| 			('json' === encoding || !encoding)
 | |
| 		) {
 | |
| 			return pair;
 | |
| 		}
 | |
| 		if ('jwk' === format || 'json' === encoding) {
 | |
| 			throw new Error(
 | |
| 				"format '" +
 | |
| 					format +
 | |
| 					"' is incompatible with encoding '" +
 | |
| 					encoding +
 | |
| 					"'"
 | |
| 			);
 | |
| 		}
 | |
| 
 | |
| 		// The... less easy way
 | |
| 		/*
 | |
|     var priv;
 | |
|     var pub;
 | |
| 
 | |
|     if ('spki' === format || 'pkcs8' === format) {
 | |
|       format = 'pkcs8';
 | |
|       pub = 'spki';
 | |
|     }
 | |
| 
 | |
|     if ('pem' === format) {
 | |
|       format = 'pkcs1';
 | |
|       encoding = 'pem';
 | |
|     } else if ('der' === format) {
 | |
|       format = 'pkcs1';
 | |
|       encoding = 'der';
 | |
|     }
 | |
| 
 | |
|     priv = format;
 | |
|     pub = pub || format;
 | |
| 
 | |
|     if (!encoding) {
 | |
|       encoding = 'pem';
 | |
|     }
 | |
| 
 | |
|     if (priv) {
 | |
|       priv = { type: priv, format: encoding };
 | |
|       pub = { type: pub, format: encoding };
 | |
|     } else {
 | |
|       // jwk
 | |
|       priv = { type: 'pkcs1', format: 'pem' };
 | |
|       pub = { type: 'pkcs1', format: 'pem' };
 | |
|     }
 | |
|     */
 | |
| 		if (('pem' === format || 'der' === format) && !encoding) {
 | |
| 			encoding = format;
 | |
| 			format = 'pkcs1';
 | |
| 		}
 | |
| 
 | |
| 		var exOpts = { jwk: pair.private, format: format, encoding: encoding };
 | |
| 		return RSA.export(exOpts).then(function(priv) {
 | |
| 			exOpts.public = true;
 | |
| 			if ('pkcs8' === exOpts.format) {
 | |
| 				exOpts.format = 'spki';
 | |
| 			}
 | |
| 			return RSA.export(exOpts).then(function(pub) {
 | |
| 				return { private: priv, public: pub };
 | |
| 			});
 | |
| 		});
 | |
| 	});
 | |
| };
 | |
| 
 | |
| native._generate = function(opts) {
 | |
| 	if (!opts) {
 | |
| 		opts = {};
 | |
| 	}
 | |
| 	return new Promise(function(resolve, reject) {
 | |
| 		try {
 | |
| 			var modlen = opts.modulusLength || 2048;
 | |
| 			var exp = opts.publicExponent || 0x10001;
 | |
| 			var pair = require('./generate-privkey.js')(modlen, exp);
 | |
| 			if (pair.private) {
 | |
| 				resolve(pair);
 | |
| 				return;
 | |
| 			}
 | |
| 			pair = toJwks(pair);
 | |
| 			resolve({ private: pair.private, public: pair.public });
 | |
| 		} catch (e) {
 | |
| 			reject(e);
 | |
| 		}
 | |
| 	});
 | |
| };
 | |
| 
 | |
| // PKCS1 to JWK only
 | |
| function toJwks(oldpair) {
 | |
| 	var block = PEM.parseBlock(oldpair.privateKeyPem);
 | |
| 	var asn1 = ASN1.parse(block.bytes);
 | |
| 	var jwk = { kty: 'RSA', n: null, e: null };
 | |
| 	jwk = x509.parsePkcs1(block.bytes, asn1, jwk);
 | |
| 	return { private: jwk, public: RSA.neuter({ jwk: jwk }) };
 | |
| }
 | |
| 
 | |
| // TODO
 | |
| var Enc = require('@root/encoding/base64');
 | |
| x509.parsePkcs1 = function parseRsaPkcs1(buf, asn1, jwk) {
 | |
| 	if (
 | |
| 		!asn1.children.every(function(el) {
 | |
| 			return 0x02 === el.type;
 | |
| 		})
 | |
| 	) {
 | |
| 		throw new Error(
 | |
| 			'not an RSA PKCS#1 public or private key (not all ints)'
 | |
| 		);
 | |
| 	}
 | |
| 
 | |
| 	if (2 === asn1.children.length) {
 | |
| 		jwk.n = Enc.bufToUrlBase64(asn1.children[0].value);
 | |
| 		jwk.e = Enc.bufToUrlBase64(asn1.children[1].value);
 | |
| 		return jwk;
 | |
| 	} else if (asn1.children.length >= 9) {
 | |
| 		// the standard allows for "otherPrimeInfos", hence at least 9
 | |
| 
 | |
| 		jwk.n = Enc.bufToUrlBase64(asn1.children[1].value);
 | |
| 		jwk.e = Enc.bufToUrlBase64(asn1.children[2].value);
 | |
| 		jwk.d = Enc.bufToUrlBase64(asn1.children[3].value);
 | |
| 		jwk.p = Enc.bufToUrlBase64(asn1.children[4].value);
 | |
| 		jwk.q = Enc.bufToUrlBase64(asn1.children[5].value);
 | |
| 		jwk.dp = Enc.bufToUrlBase64(asn1.children[6].value);
 | |
| 		jwk.dq = Enc.bufToUrlBase64(asn1.children[7].value);
 | |
| 		jwk.qi = Enc.bufToUrlBase64(asn1.children[8].value);
 | |
| 		return jwk;
 | |
| 	} else {
 | |
| 		throw new Error(
 | |
| 			'not an RSA PKCS#1 public or private key (wrong number of ints)'
 | |
| 		);
 | |
| 	}
 | |
| };
 |