135 lines
3.7 KiB
JavaScript
135 lines
3.7 KiB
JavaScript
;(function (exports) {
|
|
'use strict';
|
|
|
|
var PromiseA;
|
|
try {
|
|
/*global Promise*/
|
|
PromiseA = Promise;
|
|
} catch(e) {
|
|
PromiseA = require('bluebird');
|
|
}
|
|
|
|
|
|
// https://stackoverflow.com/questions/40314257/export-webcrypto-key-to-pem-format
|
|
function derToPem(keydata, pemName, privacy){
|
|
var keydataS = arrayBufferToString(keydata);
|
|
var keydataB64 = window.btoa(keydataS);
|
|
var keydataB64Pem = formatAsPem(keydataB64, pemName, privacy);
|
|
return keydataB64Pem;
|
|
}
|
|
|
|
function arrayBufferToString( buffer ) {
|
|
var binary = [];
|
|
var bytes = new Uint8Array( buffer );
|
|
var len = bytes.byteLength;
|
|
for (var i = 0; i < len; i++) {
|
|
binary.push(String.fromCharCode( bytes[ i ] ));
|
|
}
|
|
return binary.join('');
|
|
}
|
|
|
|
|
|
function formatAsPem(str, pemName, privacy) {
|
|
var privstr = (privacy ? privacy + ' ' : '');
|
|
var finalString = '-----BEGIN ' + pemName + ' ' + privstr + 'KEY-----\n';
|
|
|
|
while (str.length > 0) {
|
|
finalString += str.substring(0, 64) + '\n';
|
|
str = str.substring(64);
|
|
}
|
|
|
|
finalString = finalString + '-----END ' + pemName + ' ' + privstr + 'KEY-----';
|
|
|
|
return finalString;
|
|
}
|
|
|
|
var Keypairs = exports.Keypairs = {
|
|
generate: function(opts) {
|
|
if (!opts) { opts = {}; }
|
|
if (!opts.type) { opts.type = 'EC'; }
|
|
|
|
var supported = [ 'EC', 'RSA' ];
|
|
if (-1 === supported.indexOf(opts.type)) {
|
|
return PromiseA.reject(new Error("'" + opts.type + "' not implemented. Try one of " + supported.join(', ')));
|
|
}
|
|
|
|
if ('EC' === opts.type) {
|
|
return Keypairs._generateEc(opts);
|
|
}
|
|
if ('RSA' === opts.type) {
|
|
return Keypairs._generateRsa(opts);
|
|
}
|
|
}
|
|
, _generateEc: function (opts) {
|
|
if (!opts.namedCurve) { opts.namedCurve = 'P-256'; }
|
|
if ('P-256' !== opts.namedCurve) {
|
|
console.warn("'" + opts.namedCurve + "' is not supported, but it _might_ happen to work anyway.");
|
|
}
|
|
|
|
// https://github.com/diafygi/webcrypto-examples#ecdsa---generatekey
|
|
var extractable = true;
|
|
|
|
return window.crypto.subtle.generateKey(
|
|
{ name: "ECDSA", namedCurve: opts.namedCurve }
|
|
, extractable
|
|
, [ 'sign', 'verify' ]
|
|
).then(function (result) {
|
|
return window.crypto.subtle.exportKey(
|
|
"jwk"
|
|
, result.privateKey
|
|
).then(function (jwk) {
|
|
return window.crypto.subtle.exportKey(
|
|
"pkcs8"
|
|
, result.privateKey
|
|
).then(function (keydata) {
|
|
return {
|
|
type: 'EC'
|
|
, privateJwk: jwk
|
|
, privatePem: derToPem(keydata, 'EC', 'PRIVATE')
|
|
};
|
|
});
|
|
});
|
|
});
|
|
}
|
|
, _generateRsa: function (opts) {
|
|
if (!opts.bitlength) { opts.bitlength = 2048; }
|
|
if (-1 === [ 2048, 4096 ].indexOf(opts.bitlength)) {
|
|
return PromiseA.reject("opts.bitlength = (" + typeof opts.bitlength + ") " + opts.bitlength + ": Are you serious?");
|
|
}
|
|
|
|
// https://github.com/diafygi/webcrypto-examples#rsa---generatekey
|
|
var extractable = true;
|
|
|
|
return window.crypto.subtle.generateKey(
|
|
{ name: "RSASSA-PKCS1-v1_5"
|
|
, modulusLength: opts.bitlength
|
|
, publicExponent: new Uint8Array([0x01, 0x00, 0x01])
|
|
, hash: { name: "SHA-256" }
|
|
}
|
|
, extractable
|
|
, [ 'sign', 'verify' ]
|
|
).then(function (result) {
|
|
return window.crypto.subtle.exportKey(
|
|
"jwk"
|
|
, result.privateKey
|
|
).then(function (jwk) {
|
|
return window.crypto.subtle.exportKey(
|
|
"pkcs8"
|
|
, result.privateKey
|
|
).then(function (keydata) {
|
|
return {
|
|
type: 'RSA'
|
|
, privateJwk: jwk
|
|
, privatePem: derToPem(keydata, 'RSA', 'PRIVATE')
|
|
};
|
|
});
|
|
});
|
|
});
|
|
}
|
|
};
|
|
|
|
}('undefined' === typeof module ? window : module.exports));
|
|
|
|
// How we might use this
|
|
// var Keypairs = require('keypairs').Keypairs
|