working der and pem generation #2
14
app.js
14
app.js
|
@ -45,17 +45,23 @@
|
||||||
};
|
};
|
||||||
console.log('opts', opts);
|
console.log('opts', opts);
|
||||||
Keypairs.generate(opts).then(function (results) {
|
Keypairs.generate(opts).then(function (results) {
|
||||||
|
var der;
|
||||||
if (opts.kty == 'EC') {
|
if (opts.kty == 'EC') {
|
||||||
var der = x509.packPkcs8(results.private);
|
der = x509.packPkcs8(results.private);
|
||||||
var pem = Eckles.export({ jwk: results.private })
|
var pem = Eckles.export({ jwk: results.private })
|
||||||
$('.js-der').innerText = JSON.stringify(der, null, 2);
|
|
||||||
$('.js-input-pem').innerText = pem;
|
$('.js-input-pem').innerText = pem;
|
||||||
$('.js-toc-der').hidden = false;
|
|
||||||
$('.js-toc-pem').hidden = false;
|
$('.js-toc-pem').hidden = false;
|
||||||
|
} else {
|
||||||
|
der = x509.packPkcs8(results.private);
|
||||||
|
var pem = Rasha.pack({ jwk: results.private }).then(function (pem) {
|
||||||
|
$('.js-input-pem').innerText = pem;
|
||||||
|
$('.js-toc-pem').hidden = false;
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$('.js-der').innerText = JSON.stringify(der, null, 2);
|
||||||
|
$('.js-toc-der').hidden = false;
|
||||||
$('.js-jwk').innerText = JSON.stringify(results, null, 2);
|
$('.js-jwk').innerText = JSON.stringify(results, null, 2);
|
||||||
//
|
|
||||||
$('.js-loading').hidden = true;
|
$('.js-loading').hidden = true;
|
||||||
$('.js-jwk').hidden = false;
|
$('.js-jwk').hidden = false;
|
||||||
$$('input').map(function ($el) { $el.disabled = false; });
|
$$('input').map(function ($el) { $el.disabled = false; });
|
||||||
|
|
61
lib/rsa.js
61
lib/rsa.js
|
@ -3,6 +3,7 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var RSA = exports.Rasha = {};
|
var RSA = exports.Rasha = {};
|
||||||
|
var x509 = exports.x509;
|
||||||
if ('undefined' !== typeof module) { module.exports = RSA; }
|
if ('undefined' !== typeof module) { module.exports = RSA; }
|
||||||
var Enc = {};
|
var Enc = {};
|
||||||
var textEncoder = new TextEncoder();
|
var textEncoder = new TextEncoder();
|
||||||
|
@ -106,6 +107,66 @@ RSA.thumbprint = function (opts) {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
RSA.export = function (opts) {
|
||||||
|
if (!opts || !opts.jwk || 'object' !== typeof opts.jwk) {
|
||||||
|
throw new Error("must pass { jwk: jwk }");
|
||||||
|
}
|
||||||
|
var jwk = JSON.parse(JSON.stringify(opts.jwk));
|
||||||
|
var format = opts.format;
|
||||||
|
var pub = opts.public;
|
||||||
|
if (pub || -1 !== [ 'spki', 'pkix', 'ssh', 'rfc4716' ].indexOf(format)) {
|
||||||
|
jwk = RSA.nueter(jwk);
|
||||||
|
}
|
||||||
|
if ('RSA' !== jwk.kty) {
|
||||||
|
throw new Error("options.jwk.kty must be 'RSA' for RSA keys");
|
||||||
|
}
|
||||||
|
if (!jwk.p) {
|
||||||
|
// TODO test for n and e
|
||||||
|
pub = true;
|
||||||
|
if (!format || 'pkcs1' === format) {
|
||||||
|
format = 'pkcs1';
|
||||||
|
} else if (-1 !== [ 'spki', 'pkix' ].indexOf(format)) {
|
||||||
|
format = 'spki';
|
||||||
|
} else if (-1 !== [ 'ssh', 'rfc4716' ].indexOf(format)) {
|
||||||
|
format = 'ssh';
|
||||||
|
} else {
|
||||||
|
throw new Error("options.format must be 'spki', 'pkcs1', or 'ssh' for public RSA keys, not ("
|
||||||
|
+ typeof format + ") " + format);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// TODO test for all necessary keys (d, p, q ...)
|
||||||
|
if (!format || 'pkcs1' === format) {
|
||||||
|
format = 'pkcs1';
|
||||||
|
} else if ('pkcs8' !== format) {
|
||||||
|
throw new Error("options.format must be 'pkcs1' or 'pkcs8' for private RSA keys");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ('pkcs1' === format) {
|
||||||
|
if (jwk.d) {
|
||||||
|
return PEM.packBlock({ type: "RSA PRIVATE KEY", bytes: x509.packPkcs1(jwk) });
|
||||||
|
} else {
|
||||||
|
return PEM.packBlock({ type: "RSA PUBLIC KEY", bytes: x509.packPkcs1(jwk) });
|
||||||
|
}
|
||||||
|
} else if ('pkcs8' === format) {
|
||||||
|
return PEM.packBlock({ type: "PRIVATE KEY", bytes: x509.packPkcs8(jwk) });
|
||||||
|
} else if (-1 !== [ 'spki', 'pkix' ].indexOf(format)) {
|
||||||
|
return PEM.packBlock({ type: "PUBLIC KEY", bytes: x509.packSpki(jwk) });
|
||||||
|
} else if (-1 !== [ 'ssh', 'rfc4716' ].indexOf(format)) {
|
||||||
|
return SSH.pack({ jwk: jwk, comment: opts.comment });
|
||||||
|
} else {
|
||||||
|
throw new Error("Sanity Error: reached unreachable code block with format: " + format);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
RSA.pack = function (opts) {
|
||||||
|
// wrapped in a promise for API compatibility
|
||||||
|
// with the forthcoming browser version
|
||||||
|
// (and potential future native node capability)
|
||||||
|
return Promise.resolve().then(function () {
|
||||||
|
return RSA.export(opts);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
Enc.bufToUrlBase64 = function (u8) {
|
Enc.bufToUrlBase64 = function (u8) {
|
||||||
return Enc.bufToBase64(u8)
|
return Enc.bufToBase64(u8)
|
||||||
.replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '');
|
.replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '');
|
||||||
|
|
59
lib/x509.js
59
lib/x509.js
|
@ -55,6 +55,27 @@
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
x509.packPkcs1 = function (jwk) {
|
||||||
|
var n = ASN1.UInt(Enc.base64ToHex(jwk.n));
|
||||||
|
var e = ASN1.UInt(Enc.base64ToHex(jwk.e));
|
||||||
|
|
||||||
|
if (!jwk.d) {
|
||||||
|
return Enc.hexToBuf(ASN1('30', n, e));
|
||||||
|
}
|
||||||
|
|
||||||
|
return Enc.hexToBuf(ASN1('30'
|
||||||
|
, ASN1.UInt('00')
|
||||||
|
, n
|
||||||
|
, e
|
||||||
|
, ASN1.UInt(Enc.base64ToHex(jwk.d))
|
||||||
|
, ASN1.UInt(Enc.base64ToHex(jwk.p))
|
||||||
|
, ASN1.UInt(Enc.base64ToHex(jwk.q))
|
||||||
|
, ASN1.UInt(Enc.base64ToHex(jwk.dp))
|
||||||
|
, ASN1.UInt(Enc.base64ToHex(jwk.dq))
|
||||||
|
, ASN1.UInt(Enc.base64ToHex(jwk.qi))
|
||||||
|
));
|
||||||
|
};
|
||||||
|
|
||||||
x509.parsePkcs8 = function parseEcPkcs8(u8, jwk) {
|
x509.parsePkcs8 = function parseEcPkcs8(u8, jwk) {
|
||||||
var index = 24 + (OBJ_ID_EC.length / 2);
|
var index = 24 + (OBJ_ID_EC.length / 2);
|
||||||
var len = 32;
|
var len = 32;
|
||||||
|
@ -141,6 +162,44 @@
|
||||||
* @param {*} jwk
|
* @param {*} jwk
|
||||||
*/
|
*/
|
||||||
x509.packPkcs8 = function (jwk) {
|
x509.packPkcs8 = function (jwk) {
|
||||||
|
if (jwk.kty == 'RSA') {
|
||||||
|
if (!jwk.d) {
|
||||||
|
// Public RSA
|
||||||
|
return Enc.hexToBuf(ASN1('30'
|
||||||
|
, ASN1('30'
|
||||||
|
, ASN1('06', '2a864886f70d010101')
|
||||||
|
, ASN1('05')
|
||||||
|
)
|
||||||
|
, ASN1.BitStr(ASN1('30'
|
||||||
|
, ASN1.UInt(Enc.base64ToHex(jwk.n))
|
||||||
|
, ASN1.UInt(Enc.base64ToHex(jwk.e))
|
||||||
|
))
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Private RSA
|
||||||
|
return Enc.hexToBuf(ASN1('30'
|
||||||
|
, ASN1.UInt('00')
|
||||||
|
, ASN1('30'
|
||||||
|
, ASN1('06', '2a864886f70d010101')
|
||||||
|
, ASN1('05')
|
||||||
|
)
|
||||||
|
, ASN1('04'
|
||||||
|
, ASN1('30'
|
||||||
|
, ASN1.UInt('00')
|
||||||
|
, ASN1.UInt(Enc.base64ToHex(jwk.n))
|
||||||
|
, ASN1.UInt(Enc.base64ToHex(jwk.e))
|
||||||
|
, ASN1.UInt(Enc.base64ToHex(jwk.d))
|
||||||
|
, ASN1.UInt(Enc.base64ToHex(jwk.p))
|
||||||
|
, ASN1.UInt(Enc.base64ToHex(jwk.q))
|
||||||
|
, ASN1.UInt(Enc.base64ToHex(jwk.dp))
|
||||||
|
, ASN1.UInt(Enc.base64ToHex(jwk.dq))
|
||||||
|
, ASN1.UInt(Enc.base64ToHex(jwk.qi))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
var d = Enc.base64ToHex(jwk.d);
|
var d = Enc.base64ToHex(jwk.d);
|
||||||
var x = Enc.base64ToHex(jwk.x);
|
var x = Enc.base64ToHex(jwk.x);
|
||||||
var y = Enc.base64ToHex(jwk.y);
|
var y = Enc.base64ToHex(jwk.y);
|
||||||
|
|
Loading…
Reference in New Issue