pkijs
Version:
Public Key Infrastructure (PKI) is the basis of how identity and key management is performed on the web today. PKIjs is a pure JavaScript library implementing the formats that are used in PKI applications. It is built on WebCrypto and aspires to make it p
1,824 lines (1,666 loc) • 78.8 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
var _asn1js = require("asn1js");
var asn1js = _interopRequireWildcard(_asn1js);
var _pvutils = require("pvutils");
var _common = require("./common.js");
var _PublicKeyInfo = require("./PublicKeyInfo.js");
var _PublicKeyInfo2 = _interopRequireDefault(_PublicKeyInfo);
var _PrivateKeyInfo = require("./PrivateKeyInfo.js");
var _PrivateKeyInfo2 = _interopRequireDefault(_PrivateKeyInfo);
var _AlgorithmIdentifier = require("./AlgorithmIdentifier.js");
var _AlgorithmIdentifier2 = _interopRequireDefault(_AlgorithmIdentifier);
var _EncryptedContentInfo = require("./EncryptedContentInfo.js");
var _EncryptedContentInfo2 = _interopRequireDefault(_EncryptedContentInfo);
var _RSASSAPSSParams = require("./RSASSAPSSParams.js");
var _RSASSAPSSParams2 = _interopRequireDefault(_RSASSAPSSParams);
var _PBKDF2Params = require("./PBKDF2Params.js");
var _PBKDF2Params2 = _interopRequireDefault(_PBKDF2Params);
var _PBES2Params = require("./PBES2Params.js");
var _PBES2Params2 = _interopRequireDefault(_PBES2Params);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }
//**************************************************************************************
/**
* Making MAC key using algorithm described in B.2 of PKCS#12 standard.
*/
function makePKCS12B2Key(cryptoEngine, hashAlgorithm, keyLength, password, salt, iterationCount) {
//region Initial variables
var u = void 0;
var v = void 0;
var result = [];
//endregion
//region Get "u" and "v" values
switch (hashAlgorithm.toUpperCase()) {
case "SHA-1":
u = 20; // 160
v = 64; // 512
break;
case "SHA-256":
u = 32; // 256
v = 64; // 512
break;
case "SHA-384":
u = 48; // 384
v = 128; // 1024
break;
case "SHA-512":
u = 64; // 512
v = 128; // 1024
break;
default:
throw new Error("Unsupported hashing algorithm");
}
//endregion
//region Main algorithm making key
//region Transform password to UTF-8 like string
var passwordViewInitial = new Uint8Array(password);
var passwordTransformed = new ArrayBuffer(password.byteLength * 2 + 2);
var passwordTransformedView = new Uint8Array(passwordTransformed);
for (var i = 0; i < passwordViewInitial.length; i++) {
passwordTransformedView[i * 2] = 0x00;
passwordTransformedView[i * 2 + 1] = passwordViewInitial[i];
}
passwordTransformedView[passwordTransformedView.length - 2] = 0x00;
passwordTransformedView[passwordTransformedView.length - 1] = 0x00;
password = passwordTransformed.slice(0);
//endregion
//region Construct a string D (the "diversifier") by concatenating v/8 copies of ID
var D = new ArrayBuffer(v);
var dView = new Uint8Array(D);
for (var _i = 0; _i < D.byteLength; _i++) {
dView[_i] = 3;
} // The ID value equal to "3" for MACing (see B.3 of standard)
//endregion
//region Concatenate copies of the salt together to create a string S of length v * ceil(s / v) bytes (the final copy of the salt may be trunacted to create S)
var saltLength = salt.byteLength;
var sLen = v * Math.ceil(saltLength / v);
var S = new ArrayBuffer(sLen);
var sView = new Uint8Array(S);
var saltView = new Uint8Array(salt);
for (var _i2 = 0; _i2 < sLen; _i2++) {
sView[_i2] = saltView[_i2 % saltLength];
} //endregion
//region Concatenate copies of the password together to create a string P of length v * ceil(p / v) bytes (the final copy of the password may be truncated to create P)
var passwordLength = password.byteLength;
var pLen = v * Math.ceil(passwordLength / v);
var P = new ArrayBuffer(pLen);
var pView = new Uint8Array(P);
var passwordView = new Uint8Array(password);
for (var _i3 = 0; _i3 < pLen; _i3++) {
pView[_i3] = passwordView[_i3 % passwordLength];
} //endregion
//region Set I=S||P to be the concatenation of S and P
var sPlusPLength = S.byteLength + P.byteLength;
var I = new ArrayBuffer(sPlusPLength);
var iView = new Uint8Array(I);
iView.set(sView);
iView.set(pView, sView.length);
//endregion
//region Set c=ceil(n / u)
var c = Math.ceil((keyLength >> 3) / u);
//endregion
//region Initial variables
var internalSequence = Promise.resolve(I);
//endregion
//region For i=1, 2, ..., c, do the following:
for (var _i4 = 0; _i4 <= c; _i4++) {
internalSequence = internalSequence.then(function (_I) {
//region Create contecanetion of D and I
var dAndI = new ArrayBuffer(D.byteLength + _I.byteLength);
var dAndIView = new Uint8Array(dAndI);
dAndIView.set(dView);
dAndIView.set(iView, dView.length);
//endregion
return dAndI;
});
//region Make "iterationCount" rounds of hashing
for (var j = 0; j < iterationCount; j++) {
internalSequence = internalSequence.then(function (roundBuffer) {
return cryptoEngine.digest({ name: hashAlgorithm }, new Uint8Array(roundBuffer));
});
} //endregion
internalSequence = internalSequence.then(function (roundBuffer) {
//region Concatenate copies of Ai to create a string B of length v bits (the final copy of Ai may be truncated to create B)
var B = new ArrayBuffer(v);
var bView = new Uint8Array(B);
for (var _j = 0; _j < B.byteLength; _j++) {
bView[_j] = roundBuffer[_j % roundBuffer.length];
} //endregion
//region Make new I value
var k = Math.ceil(saltLength / v) + Math.ceil(passwordLength / v);
var iRound = [];
var sliceStart = 0;
var sliceLength = v;
for (var _j2 = 0; _j2 < k; _j2++) {
var chunk = Array.from(new Uint8Array(I.slice(sliceStart, sliceStart + sliceLength)));
sliceStart += v;
if (sliceStart + v > I.byteLength) sliceLength = I.byteLength - sliceStart;
var x = 0x1ff;
for (var l = B.byteLength - 1; l >= 0; l--) {
x >>= 8;
x += bView[l] + chunk[l];
chunk[l] = x & 0xff;
}
iRound.push.apply(iRound, _toConsumableArray(chunk));
}
I = new ArrayBuffer(iRound.length);
iView = new Uint8Array(I);
iView.set(iRound);
//endregion
result.push.apply(result, _toConsumableArray(new Uint8Array(roundBuffer)));
return I;
});
}
//endregion
//region Initialize final key
internalSequence = internalSequence.then(function () {
var resultBuffer = new ArrayBuffer(keyLength >> 3);
var resultView = new Uint8Array(resultBuffer);
resultView.set(new Uint8Array(result).slice(0, keyLength >> 3));
return resultBuffer;
});
//endregion
//endregion
return internalSequence;
}
//**************************************************************************************
/**
* Default cryptographic engine for Web Cryptography API
*/
var CryptoEngine = function () {
//**********************************************************************************
/**
* Constructor for CryptoEngine class
* @param {Object} [parameters={}]
* @property {Object} [schema] asn1js parsed value
*/
function CryptoEngine() {
var parameters = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
_classCallCheck(this, CryptoEngine);
//region Internal properties of the object
/**
* @type {Object}
* @description Usually here we are expecting "window.crypto" or an equivalent from custom "crypto engine"
*/
this.crypto = (0, _pvutils.getParametersValue)(parameters, "crypto", {});
/**
* @type {Object}
* @description Usually here we are expecting "window.crypto.subtle" or an equivalent from custom "crypto engine"
*/
this.subtle = (0, _pvutils.getParametersValue)(parameters, "subtle", {});
/**
* @type {string}
* @description Name of the "crypto engine"
*/
this.name = (0, _pvutils.getParametersValue)(parameters, "name", "");
//endregion
}
//**********************************************************************************
/**
* Import WebCrypto keys from different formats
* @param {string} format
* @param {ArrayBuffer|Uint8Array} keyData
* @param {Object} algorithm
* @param {boolean} extractable
* @param {Array} keyUsages
* @returns {Promise}
*/
_createClass(CryptoEngine, [{
key: "importKey",
value: function importKey(format, keyData, algorithm, extractable, keyUsages) {
var _this = this;
//region Initial variables
var jwk = {};
//endregion
//region Change "keyData" type if needed
if (keyData instanceof Uint8Array) keyData = keyData.buffer;
//endregion
switch (format.toLowerCase()) {
case "raw":
return this.subtle.importKey("raw", keyData, algorithm, extractable, keyUsages);
case "spki":
{
var asn1 = asn1js.fromBER(keyData);
if (asn1.offset === -1) return Promise.reject("Incorrect keyData");
var publicKeyInfo = new _PublicKeyInfo2.default();
try {
publicKeyInfo.fromSchema(asn1.result);
} catch (ex) {
return Promise.reject("Incorrect keyData");
}
// noinspection FallThroughInSwitchStatementJS
switch (algorithm.name.toUpperCase()) {
case "RSA-PSS":
{
//region Get information about used hash function
switch (algorithm.hash.name.toUpperCase()) {
case "SHA-1":
jwk.alg = "PS1";
break;
case "SHA-256":
jwk.alg = "PS256";
break;
case "SHA-384":
jwk.alg = "PS384";
break;
case "SHA-512":
jwk.alg = "PS512";
break;
default:
return Promise.reject("Incorrect hash algorithm: " + algorithm.hash.name.toUpperCase());
}
//endregion
}
// break omitted
case "RSASSA-PKCS1-V1_5":
{
keyUsages = ["verify"]; // Override existing keyUsages value since the key is a public key
jwk.kty = "RSA";
jwk.ext = extractable;
jwk.key_ops = keyUsages;
if (publicKeyInfo.algorithm.algorithmId !== "1.2.840.113549.1.1.1") return Promise.reject("Incorrect public key algorithm: " + publicKeyInfo.algorithm.algorithmId);
//region Get information about used hash function
if ("alg" in jwk === false) {
switch (algorithm.hash.name.toUpperCase()) {
case "SHA-1":
jwk.alg = "RS1";
break;
case "SHA-256":
jwk.alg = "RS256";
break;
case "SHA-384":
jwk.alg = "RS384";
break;
case "SHA-512":
jwk.alg = "RS512";
break;
default:
return Promise.reject("Incorrect public key algorithm: " + publicKeyInfo.algorithm.algorithmId);
}
}
//endregion
//region Create RSA Public Key elements
var publicKeyJSON = publicKeyInfo.toJSON();
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;
try {
for (var _iterator = Object.keys(publicKeyJSON)[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
var key = _step.value;
jwk[key] = publicKeyJSON[key];
} //endregion
} catch (err) {
_didIteratorError = true;
_iteratorError = err;
} finally {
try {
if (!_iteratorNormalCompletion && _iterator.return) {
_iterator.return();
}
} finally {
if (_didIteratorError) {
throw _iteratorError;
}
}
}
}
break;
case "ECDSA":
keyUsages = ["verify"]; // Override existing keyUsages value since the key is a public key
// break omitted
case "ECDH":
{
//region Initial variables
jwk = {
kty: "EC",
ext: extractable,
key_ops: keyUsages
};
//endregion
//region Get information about algorithm
if (publicKeyInfo.algorithm.algorithmId !== "1.2.840.10045.2.1") return Promise.reject("Incorrect public key algorithm: " + publicKeyInfo.algorithm.algorithmId);
//endregion
//region Create ECDSA Public Key elements
var _publicKeyJSON = publicKeyInfo.toJSON();
var _iteratorNormalCompletion2 = true;
var _didIteratorError2 = false;
var _iteratorError2 = undefined;
try {
for (var _iterator2 = Object.keys(_publicKeyJSON)[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
var _key = _step2.value;
jwk[_key] = _publicKeyJSON[_key];
} //endregion
} catch (err) {
_didIteratorError2 = true;
_iteratorError2 = err;
} finally {
try {
if (!_iteratorNormalCompletion2 && _iterator2.return) {
_iterator2.return();
}
} finally {
if (_didIteratorError2) {
throw _iteratorError2;
}
}
}
}
break;
case "RSA-OAEP":
{
jwk.kty = "RSA";
jwk.ext = extractable;
jwk.key_ops = keyUsages;
if (this.name.toLowerCase() === "safari") jwk.alg = "RSA-OAEP";else {
switch (algorithm.hash.name.toUpperCase()) {
case "SHA-1":
jwk.alg = "RSA-OAEP-1";
break;
case "SHA-256":
jwk.alg = "RSA-OAEP-256";
break;
case "SHA-384":
jwk.alg = "RSA-OAEP-384";
break;
case "SHA-512":
jwk.alg = "RSA-OAEP-512";
break;
default:
return Promise.reject("Incorrect public key algorithm: " + publicKeyInfo.algorithm.algorithmId);
}
}
//region Create ECDSA Public Key elements
var _publicKeyJSON2 = publicKeyInfo.toJSON();
var _iteratorNormalCompletion3 = true;
var _didIteratorError3 = false;
var _iteratorError3 = undefined;
try {
for (var _iterator3 = Object.keys(_publicKeyJSON2)[Symbol.iterator](), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) {
var _key2 = _step3.value;
jwk[_key2] = _publicKeyJSON2[_key2];
} //endregion
} catch (err) {
_didIteratorError3 = true;
_iteratorError3 = err;
} finally {
try {
if (!_iteratorNormalCompletion3 && _iterator3.return) {
_iterator3.return();
}
} finally {
if (_didIteratorError3) {
throw _iteratorError3;
}
}
}
}
break;
default:
return Promise.reject("Incorrect algorithm name: " + algorithm.name.toUpperCase());
}
}
break;
case "pkcs8":
{
var privateKeyInfo = new _PrivateKeyInfo2.default();
//region Parse "PrivateKeyInfo" object
var _asn = asn1js.fromBER(keyData);
if (_asn.offset === -1) return Promise.reject("Incorrect keyData");
try {
privateKeyInfo.fromSchema(_asn.result);
} catch (ex) {
return Promise.reject("Incorrect keyData");
}
if ("parsedKey" in privateKeyInfo === false) return Promise.reject("Incorrect keyData");
//endregion
// noinspection FallThroughInSwitchStatementJS
// noinspection FallThroughInSwitchStatementJS
switch (algorithm.name.toUpperCase()) {
case "RSA-PSS":
{
//region Get information about used hash function
switch (algorithm.hash.name.toUpperCase()) {
case "SHA-1":
jwk.alg = "PS1";
break;
case "SHA-256":
jwk.alg = "PS256";
break;
case "SHA-384":
jwk.alg = "PS384";
break;
case "SHA-512":
jwk.alg = "PS512";
break;
default:
return Promise.reject("Incorrect hash algorithm: " + algorithm.hash.name.toUpperCase());
}
//endregion
}
// break omitted
case "RSASSA-PKCS1-V1_5":
{
keyUsages = ["sign"]; // Override existing keyUsages value since the key is a private key
jwk.kty = "RSA";
jwk.ext = extractable;
jwk.key_ops = keyUsages;
//region Get information about used hash function
if (privateKeyInfo.privateKeyAlgorithm.algorithmId !== "1.2.840.113549.1.1.1") return Promise.reject("Incorrect private key algorithm: " + privateKeyInfo.privateKeyAlgorithm.algorithmId);
//endregion
//region Get information about used hash function
if ("alg" in jwk === false) {
switch (algorithm.hash.name.toUpperCase()) {
case "SHA-1":
jwk.alg = "RS1";
break;
case "SHA-256":
jwk.alg = "RS256";
break;
case "SHA-384":
jwk.alg = "RS384";
break;
case "SHA-512":
jwk.alg = "RS512";
break;
default:
return Promise.reject("Incorrect hash algorithm: " + algorithm.hash.name.toUpperCase());
}
}
//endregion
//region Create RSA Private Key elements
var privateKeyJSON = privateKeyInfo.toJSON();
var _iteratorNormalCompletion4 = true;
var _didIteratorError4 = false;
var _iteratorError4 = undefined;
try {
for (var _iterator4 = Object.keys(privateKeyJSON)[Symbol.iterator](), _step4; !(_iteratorNormalCompletion4 = (_step4 = _iterator4.next()).done); _iteratorNormalCompletion4 = true) {
var _key3 = _step4.value;
jwk[_key3] = privateKeyJSON[_key3];
} //endregion
} catch (err) {
_didIteratorError4 = true;
_iteratorError4 = err;
} finally {
try {
if (!_iteratorNormalCompletion4 && _iterator4.return) {
_iterator4.return();
}
} finally {
if (_didIteratorError4) {
throw _iteratorError4;
}
}
}
}
break;
case "ECDSA":
keyUsages = ["sign"]; // Override existing keyUsages value since the key is a private key
// break omitted
case "ECDH":
{
//region Initial variables
jwk = {
kty: "EC",
ext: extractable,
key_ops: keyUsages
};
//endregion
//region Get information about used hash function
if (privateKeyInfo.privateKeyAlgorithm.algorithmId !== "1.2.840.10045.2.1") return Promise.reject("Incorrect algorithm: " + privateKeyInfo.privateKeyAlgorithm.algorithmId);
//endregion
//region Create ECDSA Private Key elements
var _privateKeyJSON = privateKeyInfo.toJSON();
var _iteratorNormalCompletion5 = true;
var _didIteratorError5 = false;
var _iteratorError5 = undefined;
try {
for (var _iterator5 = Object.keys(_privateKeyJSON)[Symbol.iterator](), _step5; !(_iteratorNormalCompletion5 = (_step5 = _iterator5.next()).done); _iteratorNormalCompletion5 = true) {
var _key4 = _step5.value;
jwk[_key4] = _privateKeyJSON[_key4];
} //endregion
} catch (err) {
_didIteratorError5 = true;
_iteratorError5 = err;
} finally {
try {
if (!_iteratorNormalCompletion5 && _iterator5.return) {
_iterator5.return();
}
} finally {
if (_didIteratorError5) {
throw _iteratorError5;
}
}
}
}
break;
case "RSA-OAEP":
{
jwk.kty = "RSA";
jwk.ext = extractable;
jwk.key_ops = keyUsages;
//region Get information about used hash function
if (this.name.toLowerCase() === "safari") jwk.alg = "RSA-OAEP";else {
switch (algorithm.hash.name.toUpperCase()) {
case "SHA-1":
jwk.alg = "RSA-OAEP-1";
break;
case "SHA-256":
jwk.alg = "RSA-OAEP-256";
break;
case "SHA-384":
jwk.alg = "RSA-OAEP-384";
break;
case "SHA-512":
jwk.alg = "RSA-OAEP-512";
break;
default:
return Promise.reject("Incorrect hash algorithm: " + algorithm.hash.name.toUpperCase());
}
}
//endregion
//region Create RSA Private Key elements
var _privateKeyJSON2 = privateKeyInfo.toJSON();
var _iteratorNormalCompletion6 = true;
var _didIteratorError6 = false;
var _iteratorError6 = undefined;
try {
for (var _iterator6 = Object.keys(_privateKeyJSON2)[Symbol.iterator](), _step6; !(_iteratorNormalCompletion6 = (_step6 = _iterator6.next()).done); _iteratorNormalCompletion6 = true) {
var _key5 = _step6.value;
jwk[_key5] = _privateKeyJSON2[_key5];
} //endregion
} catch (err) {
_didIteratorError6 = true;
_iteratorError6 = err;
} finally {
try {
if (!_iteratorNormalCompletion6 && _iterator6.return) {
_iterator6.return();
}
} finally {
if (_didIteratorError6) {
throw _iteratorError6;
}
}
}
}
break;
default:
return Promise.reject("Incorrect algorithm name: " + algorithm.name.toUpperCase());
}
}
break;
case "jwk":
jwk = keyData;
break;
default:
return Promise.reject("Incorrect format: " + format);
}
//region Special case for Safari browser (since its acting not as WebCrypto standard describes)
if (this.name.toLowerCase() === "safari") {
// Try to use both ways - import using ArrayBuffer and pure JWK (for Safari Technology Preview)
return Promise.resolve().then(function () {
return _this.subtle.importKey("jwk", (0, _pvutils.stringToArrayBuffer)(JSON.stringify(jwk)), algorithm, extractable, keyUsages);
}).then(function (result) {
return result;
}, function () {
return _this.subtle.importKey("jwk", jwk, algorithm, extractable, keyUsages);
});
}
//endregion
return this.subtle.importKey("jwk", jwk, algorithm, extractable, keyUsages);
}
//**********************************************************************************
/**
* Export WebCrypto keys to different formats
* @param {string} format
* @param {Object} key
* @returns {Promise}
*/
}, {
key: "exportKey",
value: function exportKey(format, key) {
var sequence = this.subtle.exportKey("jwk", key);
//region Currently Safari returns ArrayBuffer as JWK thus we need an additional transformation
if (this.name.toLowerCase() === "safari") {
sequence = sequence.then(function (result) {
// Some additional checks for Safari Technology Preview
if (result instanceof ArrayBuffer) return JSON.parse((0, _pvutils.arrayBufferToString)(result));
return result;
});
}
//endregion
switch (format.toLowerCase()) {
case "raw":
return this.subtle.exportKey("raw", key);
case "spki":
sequence = sequence.then(function (result) {
var publicKeyInfo = new _PublicKeyInfo2.default();
try {
publicKeyInfo.fromJSON(result);
} catch (ex) {
return Promise.reject("Incorrect key data");
}
return publicKeyInfo.toSchema().toBER(false);
});
break;
case "pkcs8":
sequence = sequence.then(function (result) {
var privateKeyInfo = new _PrivateKeyInfo2.default();
try {
privateKeyInfo.fromJSON(result);
} catch (ex) {
return Promise.reject("Incorrect key data");
}
return privateKeyInfo.toSchema().toBER(false);
});
break;
case "jwk":
break;
default:
return Promise.reject("Incorrect format: " + format);
}
return sequence;
}
//**********************************************************************************
/**
* Convert WebCrypto keys between different export formats
* @param {string} inputFormat
* @param {string} outputFormat
* @param {ArrayBuffer|Object} keyData
* @param {Object} algorithm
* @param {boolean} extractable
* @param {Array} keyUsages
* @returns {Promise}
*/
}, {
key: "convert",
value: function convert(inputFormat, outputFormat, keyData, algorithm, extractable, keyUsages) {
var _this2 = this;
switch (inputFormat.toLowerCase()) {
case "raw":
switch (outputFormat.toLowerCase()) {
case "raw":
return Promise.resolve(keyData);
case "spki":
return Promise.resolve().then(function () {
return _this2.importKey("raw", keyData, algorithm, extractable, keyUsages);
}).then(function (result) {
return _this2.exportKey("spki", result);
});
case "pkcs8":
return Promise.resolve().then(function () {
return _this2.importKey("raw", keyData, algorithm, extractable, keyUsages);
}).then(function (result) {
return _this2.exportKey("pkcs8", result);
});
case "jwk":
return Promise.resolve().then(function () {
return _this2.importKey("raw", keyData, algorithm, extractable, keyUsages);
}).then(function (result) {
return _this2.exportKey("jwk", result);
});
default:
return Promise.reject("Incorrect outputFormat: " + outputFormat);
}
case "spki":
switch (outputFormat.toLowerCase()) {
case "raw":
return Promise.resolve().then(function () {
return _this2.importKey("spki", keyData, algorithm, extractable, keyUsages);
}).then(function (result) {
return _this2.exportKey("raw", result);
});
case "spki":
return Promise.resolve(keyData);
case "pkcs8":
return Promise.reject("Impossible to convert between SPKI/PKCS8");
case "jwk":
return Promise.resolve().then(function () {
return _this2.importKey("spki", keyData, algorithm, extractable, keyUsages);
}).then(function (result) {
return _this2.exportKey("jwk", result);
});
default:
return Promise.reject("Incorrect outputFormat: " + outputFormat);
}
case "pkcs8":
switch (outputFormat.toLowerCase()) {
case "raw":
return Promise.resolve().then(function () {
return _this2.importKey("pkcs8", keyData, algorithm, extractable, keyUsages);
}).then(function (result) {
return _this2.exportKey("raw", result);
});
case "spki":
return Promise.reject("Impossible to convert between SPKI/PKCS8");
case "pkcs8":
return Promise.resolve(keyData);
case "jwk":
return Promise.resolve().then(function () {
return _this2.importKey("pkcs8", keyData, algorithm, extractable, keyUsages);
}).then(function (result) {
return _this2.exportKey("jwk", result);
});
default:
return Promise.reject("Incorrect outputFormat: " + outputFormat);
}
case "jwk":
switch (outputFormat.toLowerCase()) {
case "raw":
return Promise.resolve().then(function () {
return _this2.importKey("jwk", keyData, algorithm, extractable, keyUsages);
}).then(function (result) {
return _this2.exportKey("raw", result);
});
case "spki":
return Promise.resolve().then(function () {
return _this2.importKey("jwk", keyData, algorithm, extractable, keyUsages);
}).then(function (result) {
return _this2.exportKey("spki", result);
});
case "pkcs8":
return Promise.resolve().then(function () {
return _this2.importKey("jwk", keyData, algorithm, extractable, keyUsages);
}).then(function (result) {
return _this2.exportKey("pkcs8", result);
});
case "jwk":
return Promise.resolve(keyData);
default:
return Promise.reject("Incorrect outputFormat: " + outputFormat);
}
default:
return Promise.reject("Incorrect inputFormat: " + inputFormat);
}
}
//**********************************************************************************
/**
* Wrapper for standard function "encrypt"
* @param args
* @returns {Promise}
*/
}, {
key: "encrypt",
value: function encrypt() {
var _subtle;
return (_subtle = this.subtle).encrypt.apply(_subtle, arguments);
}
//**********************************************************************************
/**
* Wrapper for standard function "decrypt"
* @param args
* @returns {Promise}
*/
}, {
key: "decrypt",
value: function decrypt() {
var _subtle2;
return (_subtle2 = this.subtle).decrypt.apply(_subtle2, arguments);
}
//**********************************************************************************
/**
* Wrapper for standard function "sign"
* @param args
* @returns {Promise}
*/
}, {
key: "sign",
value: function sign() {
var _subtle3;
return (_subtle3 = this.subtle).sign.apply(_subtle3, arguments);
}
//**********************************************************************************
/**
* Wrapper for standard function "verify"
* @param args
* @returns {Promise}
*/
}, {
key: "verify",
value: function verify() {
var _subtle4;
return (_subtle4 = this.subtle).verify.apply(_subtle4, arguments);
}
//**********************************************************************************
/**
* Wrapper for standard function "digest"
* @param args
* @returns {Promise}
*/
}, {
key: "digest",
value: function digest() {
var _subtle5;
return (_subtle5 = this.subtle).digest.apply(_subtle5, arguments);
}
//**********************************************************************************
/**
* Wrapper for standard function "generateKey"
* @param args
* @returns {Promise}
*/
}, {
key: "generateKey",
value: function generateKey() {
var _subtle6;
return (_subtle6 = this.subtle).generateKey.apply(_subtle6, arguments);
}
//**********************************************************************************
/**
* Wrapper for standard function "deriveKey"
* @param args
* @returns {Promise}
*/
}, {
key: "deriveKey",
value: function deriveKey() {
var _subtle7;
return (_subtle7 = this.subtle).deriveKey.apply(_subtle7, arguments);
}
//**********************************************************************************
/**
* Wrapper for standard function "deriveBits"
* @param args
* @returns {Promise}
*/
}, {
key: "deriveBits",
value: function deriveBits() {
var _subtle8;
return (_subtle8 = this.subtle).deriveBits.apply(_subtle8, arguments);
}
//**********************************************************************************
/**
* Wrapper for standard function "wrapKey"
* @param args
* @returns {Promise}
*/
}, {
key: "wrapKey",
value: function wrapKey() {
var _subtle9;
return (_subtle9 = this.subtle).wrapKey.apply(_subtle9, arguments);
}
//**********************************************************************************
/**
* Wrapper for standard function "unwrapKey"
* @param args
* @returns {Promise}
*/
}, {
key: "unwrapKey",
value: function unwrapKey() {
var _subtle10;
return (_subtle10 = this.subtle).unwrapKey.apply(_subtle10, arguments);
}
//**********************************************************************************
/**
* Initialize input Uint8Array by random values (with help from current "crypto engine")
* @param {!Uint8Array} view
* @returns {*}
*/
}, {
key: "getRandomValues",
value: function getRandomValues(view) {
if ("getRandomValues" in this.crypto === false) throw new Error("No support for getRandomValues");
return this.crypto.getRandomValues(view);
}
//**********************************************************************************
/**
* Get WebCrypto algorithm by wel-known OID
* @param {string} oid well-known OID to search for
* @returns {Object}
*/
}, {
key: "getAlgorithmByOID",
value: function getAlgorithmByOID(oid) {
switch (oid) {
case "1.2.840.113549.1.1.1":
case "1.2.840.113549.1.1.5":
return {
name: "RSASSA-PKCS1-v1_5",
hash: {
name: "SHA-1"
}
};
case "1.2.840.113549.1.1.11":
return {
name: "RSASSA-PKCS1-v1_5",
hash: {
name: "SHA-256"
}
};
case "1.2.840.113549.1.1.12":
return {
name: "RSASSA-PKCS1-v1_5",
hash: {
name: "SHA-384"
}
};
case "1.2.840.113549.1.1.13":
return {
name: "RSASSA-PKCS1-v1_5",
hash: {
name: "SHA-512"
}
};
case "1.2.840.113549.1.1.10":
return {
name: "RSA-PSS"
};
case "1.2.840.113549.1.1.7":
return {
name: "RSA-OAEP"
};
case "1.2.840.10045.2.1":
case "1.2.840.10045.4.1":
return {
name: "ECDSA",
hash: {
name: "SHA-1"
}
};
case "1.2.840.10045.4.3.2":
return {
name: "ECDSA",
hash: {
name: "SHA-256"
}
};
case "1.2.840.10045.4.3.3":
return {
name: "ECDSA",
hash: {
name: "SHA-384"
}
};
case "1.2.840.10045.4.3.4":
return {
name: "ECDSA",
hash: {
name: "SHA-512"
}
};
case "1.3.133.16.840.63.0.2":
return {
name: "ECDH",
kdf: "SHA-1"
};
case "1.3.132.1.11.1":
return {
name: "ECDH",
kdf: "SHA-256"
};
case "1.3.132.1.11.2":
return {
name: "ECDH",
kdf: "SHA-384"
};
case "1.3.132.1.11.3":
return {
name: "ECDH",
kdf: "SHA-512"
};
case "2.16.840.1.101.3.4.1.2":
return {
name: "AES-CBC",
length: 128
};
case "2.16.840.1.101.3.4.1.22":
return {
name: "AES-CBC",
length: 192
};
case "2.16.840.1.101.3.4.1.42":
return {
name: "AES-CBC",
length: 256
};
case "2.16.840.1.101.3.4.1.6":
return {
name: "AES-GCM",
length: 128
};
case "2.16.840.1.101.3.4.1.26":
return {
name: "AES-GCM",
length: 192
};
case "2.16.840.1.101.3.4.1.46":
return {
name: "AES-GCM",
length: 256
};
case "2.16.840.1.101.3.4.1.4":
return {
name: "AES-CFB",
length: 128
};
case "2.16.840.1.101.3.4.1.24":
return {
name: "AES-CFB",
length: 192
};
case "2.16.840.1.101.3.4.1.44":
return {
name: "AES-CFB",
length: 256
};
case "2.16.840.1.101.3.4.1.5":
return {
name: "AES-KW",
length: 128
};
case "2.16.840.1.101.3.4.1.25":
return {
name: "AES-KW",
length: 192
};
case "2.16.840.1.101.3.4.1.45":
return {
name: "AES-KW",
length: 256
};
case "1.2.840.113549.2.7":
return {
name: "HMAC",
hash: {
name: "SHA-1"
}
};
case "1.2.840.113549.2.9":
return {
name: "HMAC",
hash: {
name: "SHA-256"
}
};
case "1.2.840.113549.2.10":
return {
name: "HMAC",
hash: {
name: "SHA-384"
}
};
case "1.2.840.113549.2.11":
return {
name: "HMAC",
hash: {
name: "SHA-512"
}
};
case "1.2.840.113549.1.9.16.3.5":
return {
name: "DH"
};
case "1.3.14.3.2.26":
return {
name: "SHA-1"
};
case "2.16.840.1.101.3.4.2.1":
return {
name: "SHA-256"
};
case "2.16.840.1.101.3.4.2.2":
return {
name: "SHA-384"
};
case "2.16.840.1.101.3.4.2.3":
return {
name: "SHA-512"
};
case "1.2.840.113549.1.5.12":
return {
name: "PBKDF2"
};
//region Special case - OIDs for ECC curves
case "1.2.840.10045.3.1.7":
return {
name: "P-256"
};
case "1.3.132.0.34":
return {
name: "P-384"
};
case "1.3.132.0.35":
return {
name: "P-521"
};
//endregion
default:
}
return {};
}
//**********************************************************************************
/**
* Get OID for each specific algorithm
* @param {Object} algorithm
* @returns {string}
*/
}, {
key: "getOIDByAlgorithm",
value: function getOIDByAlgorithm(algorithm) {
var result = "";
switch (algorithm.name.toUpperCase()) {
case "RSASSA-PKCS1-V1_5":
switch (algorithm.hash.name.toUpperCase()) {
case "SHA-1":
result = "1.2.840.113549.1.1.5";
break;
case "SHA-256":
result = "1.2.840.113549.1.1.11";
break;
case "SHA-384":
result = "1.2.840.113549.1.1.12";
break;
case "SHA-512":
result = "1.2.840.113549.1.1.13";
break;
default:
}
break;
case "RSA-PSS":
result = "1.2.840.113549.1.1.10";
break;
case "RSA-OAEP":
result = "1.2.840.113549.1.1.7";
break;
case "ECDSA":
switch (algorithm.hash.name.toUpperCase()) {
case "SHA-1":
result = "1.2.840.10045.4.1";
break;
case "SHA-256":
result = "1.2.840.10045.4.3.2";
break;
case "SHA-384":
result = "1.2.840.10045.4.3.3";
break;
case "SHA-512":
result = "1.2.840.10045.4.3.4";
break;
default:
}
break;
case "ECDH":
switch (algorithm.kdf.toUpperCase()) {// Non-standard addition - hash algorithm of KDF function
case "SHA-1":
result = "1.3.133.16.840.63.0.2"; // dhSinglePass-stdDH-sha1kdf-scheme
break;
case "SHA-256":
result = "1.3.132.1.11.1"; // dhSinglePass-stdDH-sha256kdf-scheme
break;
case "SHA-384":
result = "1.3.132.1.11.2"; // dhSinglePass-stdDH-sha384kdf-scheme
break;
case "SHA-512":
result = "1.3.132.1.11.3"; // dhSinglePass-stdDH-sha512kdf-scheme
break;
default:
}
break;
case "AES-CTR":
break;
case "AES-CBC":
switch (algorithm.length) {
case 128:
result = "2.16.840.1.101.3.4.1.2";
break;
case 192:
result = "2.16.840.1.101.3.4.1.22";
break;
case 256:
result = "2.16.840.1.101.3.4.1.42";
break;
default:
}
break;
case "AES-CMAC":
break;
case "AES-GCM":
switch (algorithm.length) {
case 128:
result = "2.16.840.1.101.3.4.1.6";
break;
case 192:
result = "2.16.840.1.101.3.4.1.26";
break;
case 256:
result = "2.16.840.1.101.3.4.1.46";
break;
default:
}
break;
case "AES-CFB":
switch (algorithm.length) {
case 128:
result = "2.16.840.1.101.3.4.1.4";
break;
case 192:
result = "2.16.840.1.101.3.4.1.24";
break;
case 256:
result = "2.16.840.1.101.3.4.1.44";
break;
default:
}
break;
case "AES-KW":
switch (algorithm.length) {
case 128:
result = "2.16.840.1.101.3.4.1.5";
break;
case 192:
result = "2.16.840.1.101.3.4.1.25";
break;
case 256:
result = "2.16.840.1.101.3.4.1.45";
break;
default:
}
break;
case "HMAC":
switch (algorithm.hash.name.toUpperCase()) {
case "SHA-1":
result = "1.2.840.113549.2.7";
break;
case "SHA-256":
result = "1.2.840.113549.2.9";
break;
case "SHA-384":
result = "1.2.840.113549.2.10";
break;
case "SHA-512":
result = "1.2.840.113549.2.11";
break;
default:
}
break;
case "DH":
result = "1.2.840.113549.1.9.16.3.5";
break;
case "SHA-1":
result = "1.3.14.3.2.26";
break;
case "SHA-256":
result = "2.16.840.1.101.3.4.2.1";
break;
case "SHA-384":
result = "2.16.840.1.101.3.4.2.2";
break;
case "SHA-512":
result = "2.16.840.1.101.3.4.2.3";
break;
case "CONCAT":
break;
case "HKDF":
break;
case "PBKDF2":
result = "1.2.840.113549.1.5.12";
break;
//region Special case - OIDs for ECC curves
case "P-256":
result = "1.2.840.10045.3.1.7";
break;
case "P-384":
result = "1.3.132.0.34";
break;
case "P-521":
result = "1.3.132.0.35";
break;
//endregion
default:
}
return result;
}
//**********************************************************************************
/**
* Get default algorithm parameters for each kind of operation
* @param {string} algorithmName Algorithm name to get common parameters for
* @param {string} operation Kind of operation: "sign", "encrypt", "generatekey", "importkey", "exportkey", "verify"
* @returns {*}
*/
}, {
key: "getAlgorithmParameters",
value: function getAlgorithmParameters(algorithmName, operation) {
var result = {
algorithm: {},
usages: []
};
switch (algorithmName.toUpperCase()) {
case "RSASSA-PKCS1-V1_5":
switch (operation.toLowerCase()) {
case "generatekey":
result = {
algorithm: {
name: "RSASSA-PKCS1-v1_5",
modulusLength: 2048,
publicExponent: new Uint8Array([0x01, 0x00, 0x01]),
hash: {
name: "SHA-256"
}
},
usages: ["sign", "verify"]
};
break;
case "verify":
case "sign":
case "importkey":
result = {
algorithm: {
name: "RSASSA-PKCS1-v1_5",
hash: {
name: "SHA-256"
}
},
usages: ["verify"] // For importKey("pkcs8") usage must be "sign" only
};
break;
case "exportkey":
default:
return {
algorithm: {
name: "RSASSA-PKCS1-v1_5"
},
usages: []
};
}
break;
case "RSA-PSS":
switch (operation.toLowerCase()) {
case "sign":
case "verify":
result = {
algorithm: {
name: "RSA-PSS",
hash: {
name: "SHA-1"
},
saltLength: 20
},
usages: ["sign", "verify"]
};
break;
case "generatekey":
result = {
algorithm: {
name: "RSA-PSS",
modulusLength: 2048,
publicExponent: new Uint8Array([0x01, 0x00, 0x01]),
hash: {
name: "SHA-1"
}
},
usages: ["sign", "verify"]
};
break;
case "importkey":
result = {
algorithm: {
name: "RSA-PSS",
hash: {
name: "SHA-1"
}
},
usages: ["verify"] // For importKey("pkcs8") usage must be "sign" only
};
break;
case "exportkey":
default:
return {
algorithm: {
name: "RSA-PSS"
},
usages: []
};
}
break;
case "RSA-OAEP":
switch (operation.toLowerCase()) {
case "encrypt":
case "decrypt":
result = {
algorithm: {
name: "RSA-OAEP"
},
usages: ["encrypt", "decrypt"]
};
break;
case "generatekey":
result = {
algorithm: {
name: "RSA-OAEP",
modulusLength: 2048,
publicExponent: new Uint8Array([0x01, 0x00, 0x01]),
hash: {
name: "SHA-256"
}
},
usages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"]
};
break;
case "importkey":
result = {
algorithm: {
name: "RSA-OAEP",
hash: {
name: "SHA-256"
}
},
usages: ["encrypt"] // encrypt for "spki" and decrypt for "pkcs8"
};
break;
case "exportkey":
default:
return {
algorithm: {
name: "RSA-OAEP"
},
usages: []
};
}
break;
case "ECDSA":
switch (operation.toLowerCase()) {
case "generatekey":
result = {
algorithm: {
name: "ECDSA",
namedCurve: "P-256"
},
usages: ["sign", "verify"]
};
break;
case "importkey":
result = {
algorithm: {
name: "ECDSA",
namedCurve: "P-256"
},
usages: ["verify"] // "sign" for "pkcs8"
};
break;
case "verify":
case "sign":
result = {
algorithm: {
name: "ECDSA",
hash: {
name: "SHA-256"
}
},
usages: ["sign"]
};
break;
default:
return {
algorithm: {
name: "ECDSA"
},
usages: []
};
}
break;
case "ECDH":
switch (operation.toLowerCase()) {
case "exportkey":
case "importkey":
case "generatekey":
result = {
algorithm: {
name: "ECDH",
namedCurve: "P-256"
},
usages: ["deriveKey", "deriveBits"]
};
break;
case "derivekey":
case "derivebits":
result = {
algorithm: {
name: "ECDH",
namedCurve: "P-256",
public: [] // Must be a "publicKey"
},
usages: ["encrypt", "decrypt"]
};
break;
default:
return {
algorithm: {
name: "ECDH"
},
usages: []
};
}
break;
case "AES-CTR":
switch (operation.toLowerCase()) {
case "importkey":
case "exportkey":
case "generatekey":
result = {
algorithm: {
name: "AES-CTR",
length: 256
},
usages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"]
};
break;
case "decrypt":
case "encrypt":
result = {
algorithm: {
name: "AES-CTR",
counter: new Uint8Array(16),
length: 10
},
usages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"]
};
break;
default:
return {
algorithm: {
name: "AES-CTR"
},
usages: []
};
}
break;
case "AES-CBC":
switch (operation.toLowerCase()) {
case "importkey":
case "exportkey":
case "generatekey":
result = {
algorithm: {
name: "AES-CBC",
length: 256
},
usages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"]
};
break;
case "decrypt":
case "encrypt":
result = {
algorithm: {
name: "AES-CBC",
iv: this.getRandomValues(new Uint8Array(16)) // For "decrypt" the value should be replaced with value got on "encrypt" step
},
usages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"]
};
break;
default:
return {
algorithm: {
name: "AES-CBC"
},
usages: []
};
}
break;
case "AES-GCM":
switch (operation.toLowerCase()) {
case "importkey":
case "exportkey":
case "generatekey":
result = {
algorithm: {
name: "AES-GCM",
length: 256
},
usages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"]
};
break;
case "decrypt":
case "encrypt":
result = {
algorithm: {
name: "AES-GCM",
iv: this.getRandomValues(new Uint8Array(16)) // For "decrypt" the value should be replaced with value got on "encrypt" step
},
usages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"]
};
break;
default:
return {
algorithm: {
name: "AES-GCM"
},
usages: []
};
}
break;
case "AES-KW":
switch (operation.toLowerCase()) {
case "importkey":
case "exportkey":
case "generatekey":
case "wrapkey":
case "unwrapkey":
result = {
algorithm: {
name: "AES-KW",
length: 256
},
usages: ["wrapKey", "unwrapKey"]
};
break;
default:
return {
algorithm: {
name: "AES-KW"
},
usages: []
};
}
break;
case "HMAC":
switch (operation.toLowerCase()) {