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)'
|
|
);
|
|
}
|
|
};
|