src/RSAPrivateKey.js
import * as asn1js from "asn1js";
import { getParametersValue, toBase64, arrayBufferToString, stringToArrayBuffer, fromBase64, clearProps } from "pvutils";
import OtherPrimeInfo from "./OtherPrimeInfo.js";
//**************************************************************************************
/**
* Class from RFC3447
*/
export default class RSAPrivateKey
{
//**********************************************************************************
/**
* Constructor for RSAPrivateKey class
* @param {Object} [parameters={}]
* @property {Object} [schema] asn1js parsed value
*/
constructor(parameters = {})
{
//region Internal properties of the object
/**
* @type {number}
* @description version
*/
this.version = getParametersValue(parameters, "version", RSAPrivateKey.defaultValues("version"));
/**
* @type {Integer}
* @description modulus
*/
this.modulus = getParametersValue(parameters, "modulus", RSAPrivateKey.defaultValues("modulus"));
/**
* @type {Integer}
* @description publicExponent
*/
this.publicExponent = getParametersValue(parameters, "publicExponent", RSAPrivateKey.defaultValues("publicExponent"));
/**
* @type {Integer}
* @description privateExponent
*/
this.privateExponent = getParametersValue(parameters, "privateExponent", RSAPrivateKey.defaultValues("privateExponent"));
/**
* @type {Integer}
* @description prime1
*/
this.prime1 = getParametersValue(parameters, "prime1", RSAPrivateKey.defaultValues("prime1"));
/**
* @type {Integer}
* @description prime2
*/
this.prime2 = getParametersValue(parameters, "prime2", RSAPrivateKey.defaultValues("prime2"));
/**
* @type {Integer}
* @description exponent1
*/
this.exponent1 = getParametersValue(parameters, "exponent1", RSAPrivateKey.defaultValues("exponent1"));
/**
* @type {Integer}
* @description exponent2
*/
this.exponent2 = getParametersValue(parameters, "exponent2", RSAPrivateKey.defaultValues("exponent2"));
/**
* @type {Integer}
* @description coefficient
*/
this.coefficient = getParametersValue(parameters, "coefficient", RSAPrivateKey.defaultValues("coefficient"));
if("otherPrimeInfos" in parameters)
/**
* @type {Array.<OtherPrimeInfo>}
* @description otherPrimeInfos
*/
this.otherPrimeInfos = getParametersValue(parameters, "otherPrimeInfos", RSAPrivateKey.defaultValues("otherPrimeInfos"));
//endregion
//region If input argument array contains "schema" for this object
if("schema" in parameters)
this.fromSchema(parameters.schema);
//endregion
//region If input argument array contains "json" for this object
if("json" in parameters)
this.fromJSON(parameters.json);
//endregion
}
//**********************************************************************************
/**
* Return default values for all class members
* @param {string} memberName String name for a class member
*/
static defaultValues(memberName)
{
switch(memberName)
{
case "version":
return 0;
case "modulus":
return new asn1js.Integer();
case "publicExponent":
return new asn1js.Integer();
case "privateExponent":
return new asn1js.Integer();
case "prime1":
return new asn1js.Integer();
case "prime2":
return new asn1js.Integer();
case "exponent1":
return new asn1js.Integer();
case "exponent2":
return new asn1js.Integer();
case "coefficient":
return new asn1js.Integer();
case "otherPrimeInfos":
return [];
default:
throw new Error(`Invalid member name for RSAPrivateKey class: ${memberName}`);
}
}
//**********************************************************************************
/**
* Return value of asn1js schema for current class
* @param {Object} parameters Input parameters for the schema
* @returns {Object} asn1js schema object
*/
static schema(parameters = {})
{
//RSAPrivateKey ::= Sequence {
// version Version,
// modulus Integer, -- n
// publicExponent Integer, -- e
// privateExponent Integer, -- d
// prime1 Integer, -- p
// prime2 Integer, -- q
// exponent1 Integer, -- d mod (p-1)
// exponent2 Integer, -- d mod (q-1)
// coefficient Integer, -- (inverse of q) mod p
// otherPrimeInfos OtherPrimeInfos OPTIONAL
//}
//
//OtherPrimeInfos ::= Sequence SIZE(1..MAX) OF OtherPrimeInfo
/**
* @type {Object}
* @property {string} [blockName]
* @property {string} [version]
* @property {string} [modulus]
* @property {string} [publicExponent]
* @property {string} [privateExponent]
* @property {string} [prime1]
* @property {string} [prime2]
* @property {string} [exponent1]
* @property {string} [exponent2]
* @property {string} [coefficient]
* @property {string} [otherPrimeInfosName]
* @property {Object} [otherPrimeInfo]
*/
const names = getParametersValue(parameters, "names", {});
return (new asn1js.Sequence({
name: (names.blockName || ""),
value: [
new asn1js.Integer({ name: (names.version || "") }),
new asn1js.Integer({ name: (names.modulus || "") }),
new asn1js.Integer({ name: (names.publicExponent || "") }),
new asn1js.Integer({ name: (names.privateExponent || "") }),
new asn1js.Integer({ name: (names.prime1 || "") }),
new asn1js.Integer({ name: (names.prime2 || "") }),
new asn1js.Integer({ name: (names.exponent1 || "") }),
new asn1js.Integer({ name: (names.exponent2 || "") }),
new asn1js.Integer({ name: (names.coefficient || "") }),
new asn1js.Sequence({
optional: true,
value: [
new asn1js.Repeated({
name: (names.otherPrimeInfosName || ""),
value: OtherPrimeInfo.schema(names.otherPrimeInfo || {})
})
]
})
]
}));
}
//**********************************************************************************
/**
* Convert parsed asn1js object into current class
* @param {!Object} schema
*/
fromSchema(schema)
{
//region Clear input data first
clearProps(schema, [
"version",
"modulus",
"publicExponent",
"privateExponent",
"prime1",
"prime2",
"exponent1",
"exponent2",
"coefficient",
"otherPrimeInfos"
]);
//endregion
//region Check the schema is valid
const asn1 = asn1js.compareSchema(schema,
schema,
RSAPrivateKey.schema({
names: {
version: "version",
modulus: "modulus",
publicExponent: "publicExponent",
privateExponent: "privateExponent",
prime1: "prime1",
prime2: "prime2",
exponent1: "exponent1",
exponent2: "exponent2",
coefficient: "coefficient",
otherPrimeInfo: {
names: {
blockName: "otherPrimeInfos"
}
}
}
})
);
if(asn1.verified === false)
throw new Error("Object's schema was not verified against input data for RSAPrivateKey");
//endregion
//region Get internal properties from parsed schema
this.version = asn1.result.version.valueBlock.valueDec;
this.modulus = asn1.result.modulus.convertFromDER(256);
this.publicExponent = asn1.result.publicExponent;
this.privateExponent = asn1.result.privateExponent.convertFromDER(256);
this.prime1 = asn1.result.prime1.convertFromDER(128);
this.prime2 = asn1.result.prime2.convertFromDER(128);
this.exponent1 = asn1.result.exponent1.convertFromDER(128);
this.exponent2 = asn1.result.exponent2.convertFromDER(128);
this.coefficient = asn1.result.coefficient.convertFromDER(128);
if("otherPrimeInfos" in asn1.result)
this.otherPrimeInfos = Array.from(asn1.result.otherPrimeInfos, element => new OtherPrimeInfo({ schema: element }));
//endregion
}
//**********************************************************************************
/**
* Convert current object to asn1js object and set correct values
* @returns {Object} asn1js object
*/
toSchema()
{
//region Create array for output sequence
const outputArray = [];
outputArray.push(new asn1js.Integer({ value: this.version }));
outputArray.push(this.modulus.convertToDER());
outputArray.push(this.publicExponent);
outputArray.push(this.privateExponent.convertToDER());
outputArray.push(this.prime1.convertToDER());
outputArray.push(this.prime2.convertToDER());
outputArray.push(this.exponent1.convertToDER());
outputArray.push(this.exponent2.convertToDER());
outputArray.push(this.coefficient.convertToDER());
if("otherPrimeInfos" in this)
{
outputArray.push(new asn1js.Sequence({
value: Array.from(this.otherPrimeInfos, element => element.toSchema())
}));
}
//endregion
//region Construct and return new ASN.1 schema for this object
return (new asn1js.Sequence({
value: outputArray
}));
//endregion
}
//**********************************************************************************
/**
* Convertion for the class to JSON object
* @returns {Object}
*/
toJSON()
{
const jwk = {
n: toBase64(arrayBufferToString(this.modulus.valueBlock.valueHex), true, true, true),
e: toBase64(arrayBufferToString(this.publicExponent.valueBlock.valueHex), true, true, true),
d: toBase64(arrayBufferToString(this.privateExponent.valueBlock.valueHex), true, true, true),
p: toBase64(arrayBufferToString(this.prime1.valueBlock.valueHex), true, true, true),
q: toBase64(arrayBufferToString(this.prime2.valueBlock.valueHex), true, true, true),
dp: toBase64(arrayBufferToString(this.exponent1.valueBlock.valueHex), true, true, true),
dq: toBase64(arrayBufferToString(this.exponent2.valueBlock.valueHex), true, true, true),
qi: toBase64(arrayBufferToString(this.coefficient.valueBlock.valueHex), true, true, true)
};
if("otherPrimeInfos" in this)
jwk.oth = Array.from(this.otherPrimeInfos, element => element.toJSON());
return jwk;
}
//**********************************************************************************
/**
* Convert JSON value into current object
* @param {Object} json
*/
fromJSON(json)
{
if("n" in json)
this.modulus = new asn1js.Integer({ valueHex: stringToArrayBuffer(fromBase64(json.n, true, true)) });
else
throw new Error("Absent mandatory parameter \"n\"");
if("e" in json)
this.publicExponent = new asn1js.Integer({ valueHex: stringToArrayBuffer(fromBase64(json.e, true, true)) });
else
throw new Error("Absent mandatory parameter \"e\"");
if("d" in json)
this.privateExponent = new asn1js.Integer({ valueHex: stringToArrayBuffer(fromBase64(json.d, true, true)) });
else
throw new Error("Absent mandatory parameter \"d\"");
if("p" in json)
this.prime1 = new asn1js.Integer({ valueHex: stringToArrayBuffer(fromBase64(json.p, true, true)) });
else
throw new Error("Absent mandatory parameter \"p\"");
if("q" in json)
this.prime2 = new asn1js.Integer({ valueHex: stringToArrayBuffer(fromBase64(json.q, true, true)) });
else
throw new Error("Absent mandatory parameter \"q\"");
if("dp" in json)
this.exponent1 = new asn1js.Integer({ valueHex: stringToArrayBuffer(fromBase64(json.dp, true, true)) });
else
throw new Error("Absent mandatory parameter \"dp\"");
if("dq" in json)
this.exponent2 = new asn1js.Integer({ valueHex: stringToArrayBuffer(fromBase64(json.dq, true, true)) });
else
throw new Error("Absent mandatory parameter \"dq\"");
if("qi" in json)
this.coefficient = new asn1js.Integer({ valueHex: stringToArrayBuffer(fromBase64(json.qi, true, true)) });
else
throw new Error("Absent mandatory parameter \"qi\"");
if("oth" in json)
this.otherPrimeInfos = Array.from(json.oth, element => new OtherPrimeInfo({ json: element }));
}
//**********************************************************************************
}
//**************************************************************************************