diff --git a/javascript/src/utilities/signingUtilities.js b/javascript/src/utilities/signingUtilities.js index 338c627008502fbe3caca72f966d1e15a4596df8..d16b356f08e5203dad8e406db424880353aae0e3 100644 --- a/javascript/src/utilities/signingUtilities.js +++ b/javascript/src/utilities/signingUtilities.js @@ -10,7 +10,7 @@ import { isEqualBuffer } from "pvutils"; import { fromBER } from "asn1js"; -import { ContentInfo, SignedData } from "pkijs"; +import { ContentInfo, SignedData, Certificate } from "pkijs"; import { algomap, rdnmap } from "../constants/certificates"; import { fixNewLines, @@ -85,6 +85,175 @@ const encryptionAlgorithm = { length: 128 }; +// Convert a hex string to a byte array +function hexStringToBytes(hex) { + for (let bytes = [], c = 0; c < hex.length; c += 2) { + bytes.push(parseInt(hex.substr(c, 2), 16)); + } + return bytes; +} + +export class CertificateData { + //********************************************************************************** + /** + * Constructor for SignedData class + * @param {pkijs.Certificate} [certificate] + * @param {Object} [parameters] + */ + constructor(parameters = {}) { + this.serialNumber = null; //string || ArrayBuffer || Uint8Array + + this.keyPair = null; //{publicKey, privateKey} + + this.signatureAlgorithm = null; //TODO remove from here and from dashboard + this.algorithms = { + hashAlg: null, //"SHA-256" + signAlg: null, //"RSASSA-PKCS1-v1_5" + keyLength: 0 //2048 + }; + + this.issuer = null; //same as subject + + this.subject = { + commonName: null, //string + country: null, //string + locality: null, //string + state: null, //string + organization: null, //string + organizationUnit: null, //string + email: null, //string + url: null //string + }; + + this.validity = { + notBefore: null, //new Date() + notAfter: null, //new Date() + validYears: 1 //int + }; + + this.isCA = false; + + if (parameters) { + if (parameters instanceof Certificate) { + this.fromCertificate(parameters); + } else { + this.fromParameters(parameters); + } + } + } + + fromCertificate(certificate) { + this.serialNumber = bufferToHexCodes( + certificate.serialNumber.valueBlock.valueHex + ); + + let signatureAlgorithm = + algomap[certificate.signatureAlgorithm.algorithmId]; + if (typeof signatureAlgorithm === "undefined") { + signatureAlgorithm = certificate.signatureAlgorithm.algorithmId; + } else { + signatureAlgorithm = `${signatureAlgorithm}`; + } + this.signatureAlgorithm = signatureAlgorithm; + this.algorithms.signAlg = signatureAlgorithm; + + this.issuer = {}; + const issuer = certificate.issuer.typesAndValues; + for (const typeAndValue of issuer) { + let typeVal = rdnmap[typeAndValue.type]; + if (typeof typeVal === "undefined") { + typeVal = typeAndValue.type; + } + const subjVal = typeAndValue.value.valueBlock.value; + this.issuer[typeVal] = subjVal; + } + + const subject = certificate.subject.typesAndValues; + for (const typeAndValue of subject) { + let typeVal = rdnmap[typeAndValue.type]; + if (typeof typeVal === "undefined") { + typeVal = typeAndValue.type; + } + const subjVal = typeAndValue.value.valueBlock.value; + this.subject[typeVal] = subjVal; + } + + this.validity.notBefore = certificate.notBefore.value; + this.validity.notAfter = certificate.notAfter.value; + + this.isCA = certificate.issuer.isEqual(certificate.subject); + } + + fromParameters(parameters) { + if ("serialNumber" in parameters) { + this.serialNumber = parameters.serialNumber; + } + + if ("keyPair" in parameters) { + this.keyPair = parameters.keyPair; + } + + if ("signatureAlgorithm" in parameters) { + this.signatureAlgorithm = parameters.signatureAlgorithm; + } + + if ("algorithms" in parameters) { + if ("hashAlg" in parameters.algorithms) { + this.algorithms.hashAlg = parameters.algorithms.hashAlg; + } + if ("signAlg" in parameters.algorithms) { + this.algorithms.signAlg = parameters.algorithms.signAlg; + } + if ("keyLength" in parameters.algorithms) { + this.algorithms.keyLength = parameters.algorithms.keyLength; + } + } + + if ("subject" in parameters) { + if ("commonName" in parameters.subject) { + this.subject.commonName = parameters.subject.commonName; + } + if ("country" in parameters.subject) { + this.subject.country = parameters.subject.country; + } + if ("locality" in parameters.subject) { + this.subject.locality = parameters.subject.locality; + } + if ("state" in parameters.subject) { + this.subject.state = parameters.subject.state; + } + if ("organization" in parameters.subject) { + this.subject.organization = parameters.subject.organization; + } + if ("organizationUnit" in parameters.subject) { + this.subject.organizationUnit = parameters.subject.organizationUnit; + } + if ("email" in parameters.subject) { + this.subject.email = parameters.subject.email; + } + if ("url" in parameters.subject) { + this.subject.url = parameters.subject.url; + } + } + + if ("validity" in parameters) { + if ("notBefore" in parameters.validity) { + this.validity.notBefore = parameters.validity.notBefore; + } + if ("notAfter" in parameters.validity) { + this.validity.notAfter = parameters.validity.notAfter; + } + if ("validYears" in parameters.validity) { + this.validity.validYears = parameters.validity.validYears; + } + } + + if ("isCA" in parameters) { + this.isCA = parameters.isCA; + } + } +} + //********************************************************************************* // Returns promise, resolved to keyPair object {publicKey, privateKey} //********************************************************************************* @@ -219,14 +388,40 @@ function createCertificate(certData, issuerData = null) { //region Put a static values certificate.version = CERTIFIATE_Version_3; - const serialNumberBuffer = new ArrayBuffer(20); - const serialNumberView = new Uint8Array(serialNumberBuffer); - pkijs.getRandomValues(serialNumberView); - serialNumberView[0] &= 0x7f; - // noinspection JSUnresolvedFunction - certificate.serialNumber = new asn1js.Integer({ - valueHex: serialNumberView - }); + certificate.serialNumber = null; + if (certData.serialNumber) { + let serialNumberView = null; + + if (certData.serialNumber instanceof Buffer) { + serialNumberView = new Uint8Array(certData.serialNumber); + } else if (certData.serialNumber instanceof ArrayBuffer) { + serialNumberView = new Uint8Array(certData.serialNumber); + } else if (certData.serialNumber instanceof Uint8Array) { + serialNumberView = certData.serialNumber; + } else if (certData.serialNumber instanceof String) { + try { + serialNumberView = new Uint8Array(hexStringToBytes(certData.serialNumber)); + } catch (ignore) { + serialNumberView = null; + } + } + if (serialNumberView !== null) { + certificate.serialNumber = new asn1js.Integer({ + valueHex: serialNumberView + }); + } + } + + if (certificate.serialNumber === null) { + const serialNumberBuffer = new ArrayBuffer(20); + const serialNumberView = new Uint8Array(serialNumberBuffer); + pkijs.getRandomValues(serialNumberView); + serialNumberView[0] &= 0x7f; + // noinspection JSUnresolvedFunction + certificate.serialNumber = new asn1js.Integer({ + valueHex: serialNumberView + }); + } //endregion Put a static values //region Subject @@ -676,7 +871,7 @@ export function parsePrivateKey(privateKeyPEM) { } export function createPassportCertificate(commonNameArg) { - const certData = { + const certDataParams = { algorithms: { hashAlg: "SHA-256", signAlg: "RSASSA-PKCS1-v1_5", @@ -701,7 +896,9 @@ export function createPassportCertificate(commonNameArg) { isCA: true // optional flag denoting if this is CA certificate or leaf certificate, defaults to false }; - return createCertificate(certData, null); + const certificateData = new CertificateData(certDataParams); + + return createCertificate(certificateData, null); } export function createOneTimePassportCertificate( @@ -1062,48 +1259,8 @@ export const parseSignedData = signatureBase64 => { export const parseCertificates = signedData => { try { - return signedData.certificates.map((certificate, index) => { - const certificateData = { issuer: {}, subject: {}, validity: {} }; - const serialNumber = bufferToHexCodes( - certificate.serialNumber.valueBlock.valueHex - ); - const issuer = certificate.issuer.typesAndValues; - const subject = certificate.subject.typesAndValues; - const notAfter = certificate.notAfter.value; - const notBefore = certificate.notBefore.value; - let signatureAlgorithm = - algomap[certificate.signatureAlgorithm.algorithmId]; - if (typeof signatureAlgorithm === "undefined") { - signatureAlgorithm = certificate.signatureAlgorithm.algorithmId; - } else { - signatureAlgorithm = `${signatureAlgorithm}`; - } - - for (const typeAndValue of issuer) { - let typeVal = rdnmap[typeAndValue.type]; - if (typeof typeVal === "undefined") { - typeVal = typeAndValue.type; - } - const subjVal = typeAndValue.value.valueBlock.value; - certificateData.issuer[typeVal] = subjVal; - } - - for (const typeAndValue of subject) { - let typeVal = rdnmap[typeAndValue.type]; - if (typeof typeVal === "undefined") { - typeVal = typeAndValue.type; - } - const subjVal = typeAndValue.value.valueBlock.value; - certificateData.subject[typeVal] = subjVal; - } - - certificateData.signatureAlgorithm = signatureAlgorithm; - certificateData.serialNumber = serialNumber; - certificateData.validity = { - notAfter, - notBefore - }; - + return signedData.certificates.map((certificate) => { + const certificateData = new CertificateData(certificate); return certificateData; }); } catch (e) {