mirror of
https://github.com/Ylianst/MeshCommander
synced 2025-12-05 21:53:19 +00:00
384 lines
12 KiB
JavaScript
384 lines
12 KiB
JavaScript
import * as asn1js from "asn1js";
|
|
import { getParametersValue, bufferToHexCodes, clearProps } from "pvutils";
|
|
import { getEngine } from "./common.js";
|
|
import PublicKeyInfo from "./PublicKeyInfo.js";
|
|
import RelativeDistinguishedNames from "./RelativeDistinguishedNames.js";
|
|
import AlgorithmIdentifier from "./AlgorithmIdentifier.js";
|
|
import Attribute from "./Attribute.js";
|
|
//**************************************************************************************
|
|
function CertificationRequestInfo(parameters = {})
|
|
{
|
|
//CertificationRequestInfo ::= SEQUENCE {
|
|
// version INTEGER { v1(0) } (v1,...),
|
|
// subject Name,
|
|
// subjectPKInfo SubjectPublicKeyInfo{{ PKInfoAlgorithms }},
|
|
// attributes [0] Attributes{{ CRIAttributes }}
|
|
//}
|
|
|
|
/**
|
|
* @type {Object}
|
|
* @property {string} [blockName]
|
|
* @property {string} [CertificationRequestInfo]
|
|
* @property {string} [CertificationRequestInfoVersion]
|
|
* @property {string} [subject]
|
|
* @property {string} [CertificationRequestInfoAttributes]
|
|
* @property {string} [attributes]
|
|
*/
|
|
const names = getParametersValue(parameters, "names", {});
|
|
|
|
return (new asn1js.Sequence({
|
|
name: (names.CertificationRequestInfo || "CertificationRequestInfo"),
|
|
value: [
|
|
new asn1js.Integer({ name: (names.CertificationRequestInfoVersion || "CertificationRequestInfo.version") }),
|
|
RelativeDistinguishedNames.schema(names.subject || {
|
|
names: {
|
|
blockName: "CertificationRequestInfo.subject"
|
|
}
|
|
}),
|
|
PublicKeyInfo.schema({
|
|
names: {
|
|
blockName: "CertificationRequestInfo.subjectPublicKeyInfo"
|
|
}
|
|
}),
|
|
new asn1js.Constructed({
|
|
optional: true,
|
|
idBlock: {
|
|
tagClass: 3, // CONTEXT-SPECIFIC
|
|
tagNumber: 0 // [0]
|
|
},
|
|
value: [
|
|
new asn1js.Repeated({
|
|
optional: true, // Because OpenSSL makes wrong "attributes" field
|
|
name: (names.CertificationRequestInfoAttributes || "CertificationRequestInfo.attributes"),
|
|
value: Attribute.schema(names.attributes || {})
|
|
})
|
|
]
|
|
})
|
|
]
|
|
}));
|
|
}
|
|
//**************************************************************************************
|
|
/**
|
|
* Class from RFC2986
|
|
*/
|
|
export default class CertificationRequest
|
|
{
|
|
//**********************************************************************************
|
|
/**
|
|
* Constructor for Attribute class
|
|
* @param {Object} [parameters={}]
|
|
* @param {Object} [parameters.schema] asn1js parsed value to initialize the class from
|
|
*/
|
|
constructor(parameters = {})
|
|
{
|
|
//region Internal properties of the object
|
|
/**
|
|
* @type {ArrayBuffer}
|
|
* @desc tbs
|
|
*/
|
|
this.tbs = getParametersValue(parameters, "tbs", CertificationRequest.defaultValues("tbs"));
|
|
/**
|
|
* @type {number}
|
|
* @desc version
|
|
*/
|
|
this.version = getParametersValue(parameters, "version", CertificationRequest.defaultValues("version"));
|
|
/**
|
|
* @type {RelativeDistinguishedNames}
|
|
* @desc subject
|
|
*/
|
|
this.subject = getParametersValue(parameters, "subject", CertificationRequest.defaultValues("subject"));
|
|
/**
|
|
* @type {PublicKeyInfo}
|
|
* @desc subjectPublicKeyInfo
|
|
*/
|
|
this.subjectPublicKeyInfo = getParametersValue(parameters, "subjectPublicKeyInfo", CertificationRequest.defaultValues("subjectPublicKeyInfo"));
|
|
|
|
if("attributes" in parameters)
|
|
/**
|
|
* @type {Array.<Attribute>}
|
|
* @desc attributes
|
|
*/
|
|
this.attributes = getParametersValue(parameters, "attributes", CertificationRequest.defaultValues("attributes"));
|
|
|
|
/**
|
|
* @type {AlgorithmIdentifier}
|
|
* @desc signatureAlgorithm
|
|
*/
|
|
this.signatureAlgorithm = getParametersValue(parameters, "signatureAlgorithm", CertificationRequest.defaultValues("signatureAlgorithm"));
|
|
/**
|
|
* @type {BitString}
|
|
* @desc signatureAlgorithm
|
|
*/
|
|
this.signatureValue = getParametersValue(parameters, "signatureValue", CertificationRequest.defaultValues("signatureValue"));
|
|
//endregion
|
|
|
|
//region If input argument array contains "schema" for this object
|
|
if("schema" in parameters)
|
|
this.fromSchema(parameters.schema);
|
|
//endregion
|
|
}
|
|
//**********************************************************************************
|
|
/**
|
|
* Return default values for all class members
|
|
* @param {string} memberName String name for a class member
|
|
*/
|
|
static defaultValues(memberName)
|
|
{
|
|
switch(memberName)
|
|
{
|
|
case "tbs":
|
|
return new ArrayBuffer(0);
|
|
case "version":
|
|
return 0;
|
|
case "subject":
|
|
return new RelativeDistinguishedNames();
|
|
case "subjectPublicKeyInfo":
|
|
return new PublicKeyInfo();
|
|
case "attributes":
|
|
return [];
|
|
case "signatureAlgorithm":
|
|
return new AlgorithmIdentifier();
|
|
case "signatureValue":
|
|
return new asn1js.BitString();
|
|
default:
|
|
throw new Error(`Invalid member name for CertificationRequest class: ${memberName}`);
|
|
}
|
|
}
|
|
//**********************************************************************************
|
|
/**
|
|
* Return value of pre-defined ASN.1 schema for current class
|
|
*
|
|
* ASN.1 schema:
|
|
* ```asn1
|
|
* CertificationRequest ::= SEQUENCE {
|
|
* certificationRequestInfo CertificationRequestInfo,
|
|
* signatureAlgorithm AlgorithmIdentifier{{ SignatureAlgorithms }},
|
|
* signature BIT STRING
|
|
* }
|
|
* ```
|
|
*
|
|
* @param {Object} parameters Input parameters for the schema
|
|
* @returns {Object} asn1js schema object
|
|
*/
|
|
static schema(parameters = {})
|
|
{
|
|
/**
|
|
* @type {Object}
|
|
* @property {string} [blockName]
|
|
* @property {string} [certificationRequestInfo]
|
|
* @property {string} [signatureAlgorithm]
|
|
* @property {string} [signatureValue]
|
|
*/
|
|
const names = getParametersValue(parameters, "names", {});
|
|
|
|
return (new asn1js.Sequence({
|
|
value: [
|
|
CertificationRequestInfo(names.certificationRequestInfo || {}),
|
|
new asn1js.Sequence({
|
|
name: (names.signatureAlgorithm || "signatureAlgorithm"),
|
|
value: [
|
|
new asn1js.ObjectIdentifier(),
|
|
new asn1js.Any({ optional: true })
|
|
]
|
|
}),
|
|
new asn1js.BitString({ name: (names.signatureValue || "signatureValue") })
|
|
]
|
|
}));
|
|
}
|
|
//**********************************************************************************
|
|
/**
|
|
* Convert parsed asn1js object into current class
|
|
* @param {!Object} schema
|
|
*/
|
|
fromSchema(schema)
|
|
{
|
|
//region Clear input data first
|
|
clearProps(schema, [
|
|
"CertificationRequestInfo",
|
|
"CertificationRequestInfo.version",
|
|
"CertificationRequestInfo.subject",
|
|
"CertificationRequestInfo.subjectPublicKeyInfo",
|
|
"CertificationRequestInfo.attributes",
|
|
"signatureAlgorithm",
|
|
"signatureValue"
|
|
]);
|
|
//endregion
|
|
|
|
//region Check the schema is valid
|
|
const asn1 = asn1js.compareSchema(schema,
|
|
schema,
|
|
CertificationRequest.schema()
|
|
);
|
|
|
|
if(asn1.verified === false)
|
|
throw new Error("Object's schema was not verified against input data for CertificationRequest");
|
|
//endregion
|
|
|
|
//region Get internal properties from parsed schema
|
|
this.tbs = asn1.result.CertificationRequestInfo.valueBeforeDecode;
|
|
|
|
this.version = asn1.result["CertificationRequestInfo.version"].valueBlock.valueDec;
|
|
this.subject = new RelativeDistinguishedNames({ schema: asn1.result["CertificationRequestInfo.subject"] });
|
|
this.subjectPublicKeyInfo = new PublicKeyInfo({ schema: asn1.result["CertificationRequestInfo.subjectPublicKeyInfo"] });
|
|
if("CertificationRequestInfo.attributes" in asn1.result)
|
|
this.attributes = Array.from(asn1.result["CertificationRequestInfo.attributes"], element => new Attribute({ schema: element }));
|
|
|
|
this.signatureAlgorithm = new AlgorithmIdentifier({ schema: asn1.result.signatureAlgorithm });
|
|
this.signatureValue = asn1.result.signatureValue;
|
|
//endregion
|
|
}
|
|
//**********************************************************************************
|
|
/**
|
|
* Aux function making ASN1js Sequence from current TBS
|
|
* @returns {Sequence}
|
|
*/
|
|
encodeTBS()
|
|
{
|
|
//region Create array for output sequence
|
|
const outputArray = [
|
|
new asn1js.Integer({ value: this.version }),
|
|
this.subject.toSchema(),
|
|
this.subjectPublicKeyInfo.toSchema()
|
|
];
|
|
|
|
if("attributes" in this)
|
|
{
|
|
outputArray.push(new asn1js.Constructed({
|
|
idBlock: {
|
|
tagClass: 3, // CONTEXT-SPECIFIC
|
|
tagNumber: 0 // [0]
|
|
},
|
|
value: Array.from(this.attributes, element => element.toSchema())
|
|
}));
|
|
}
|
|
//endregion
|
|
|
|
return (new asn1js.Sequence({
|
|
value: outputArray
|
|
}));
|
|
}
|
|
//**********************************************************************************
|
|
/**
|
|
* Convert current object to asn1js object and set correct values
|
|
* @returns {Object} asn1js object
|
|
*/
|
|
toSchema(encodeFlag = false)
|
|
{
|
|
//region Decode stored TBS value
|
|
let tbsSchema;
|
|
|
|
if(encodeFlag === false)
|
|
{
|
|
if(this.tbs.byteLength === 0) // No stored TBS part
|
|
return CertificationRequest.schema();
|
|
|
|
tbsSchema = asn1js.fromBER(this.tbs).result;
|
|
}
|
|
//endregion
|
|
//region Create TBS schema via assembling from TBS parts
|
|
else
|
|
tbsSchema = this.encodeTBS();
|
|
//endregion
|
|
|
|
//region Construct and return new ASN.1 schema for this object
|
|
return (new asn1js.Sequence({
|
|
value: [
|
|
tbsSchema,
|
|
this.signatureAlgorithm.toSchema(),
|
|
this.signatureValue
|
|
]
|
|
}));
|
|
//endregion
|
|
}
|
|
//**********************************************************************************
|
|
/**
|
|
* Convertion for the class to JSON object
|
|
* @returns {Object}
|
|
*/
|
|
toJSON()
|
|
{
|
|
const object = {
|
|
tbs: bufferToHexCodes(this.tbs, 0, this.tbs.byteLength),
|
|
version: this.version,
|
|
subject: this.subject.toJSON(),
|
|
subjectPublicKeyInfo: this.subjectPublicKeyInfo.toJSON(),
|
|
signatureAlgorithm: this.signatureAlgorithm.toJSON(),
|
|
signatureValue: this.signatureValue.toJSON()
|
|
};
|
|
|
|
if("attributes" in this)
|
|
object.attributes = Array.from(this.attributes, element => element.toJSON());
|
|
|
|
return object;
|
|
}
|
|
//**********************************************************************************
|
|
/**
|
|
* Makes signature for currect certification request
|
|
* @param {Object} privateKey WebCrypto private key
|
|
* @param {string} [hashAlgorithm=SHA-1] String representing current hashing algorithm
|
|
*/
|
|
sign(privateKey, hashAlgorithm = "SHA-1")
|
|
{
|
|
//region Initial checking
|
|
//region Get a private key from function parameter
|
|
if(typeof privateKey === "undefined")
|
|
return Promise.reject("Need to provide a private key for signing");
|
|
//endregion
|
|
//endregion
|
|
|
|
//region Initial variables
|
|
let sequence = Promise.resolve();
|
|
let parameters;
|
|
|
|
const engine = getEngine();
|
|
//endregion
|
|
|
|
//region Get a "default parameters" for current algorithm and set correct signature algorithm
|
|
sequence = sequence.then(() => engine.subtle.getSignatureParameters(privateKey, hashAlgorithm));
|
|
|
|
sequence = sequence.then(result =>
|
|
{
|
|
parameters = result.parameters;
|
|
this.signatureAlgorithm = result.signatureAlgorithm;
|
|
});
|
|
//endregion
|
|
|
|
//region Create TBS data for signing
|
|
sequence = sequence.then(() =>
|
|
{
|
|
this.tbs = this.encodeTBS().toBER(false);
|
|
});
|
|
//endregion
|
|
|
|
//region Signing TBS data on provided private key
|
|
sequence = sequence.then(() => engine.subtle.signWithPrivateKey(this.tbs, privateKey, parameters));
|
|
|
|
sequence = sequence.then(result =>
|
|
{
|
|
this.signatureValue = new asn1js.BitString({ valueHex: result });
|
|
});
|
|
//endregion
|
|
|
|
return sequence;
|
|
}
|
|
//**********************************************************************************
|
|
/**
|
|
* Verify existing certification request signature
|
|
* @returns {*}
|
|
*/
|
|
verify()
|
|
{
|
|
return getEngine().subtle.verifyWithPublicKey(this.tbs, this.signatureValue, this.subjectPublicKeyInfo, this.signatureAlgorithm);
|
|
}
|
|
//**********************************************************************************
|
|
/**
|
|
* Importing public key for current certificate request
|
|
*/
|
|
getPublicKey(parameters = null)
|
|
{
|
|
return getEngine().getPublicKey(this.subjectPublicKeyInfo, this.signatureAlgorithm, parameters);
|
|
}
|
|
//**********************************************************************************
|
|
}
|
|
//**************************************************************************************
|