"setup/inc/git@code.vereign.com:docker/osticket.git" did not exist on "b4f0f6bf8f5af56a3cdbd3bd0564c49c75e002d1"
Newer
Older
import {
canTryPincode,
failPincodeAttempt,
getTimeLeftInLocalStorage,
makeid
} from "./appUtility";
import {
bufferToHexCodes,
stringToArrayBuffer,
isEqualBuffer
} from "pvutils";
import { ContentInfo, SignedData, Certificate } from "pkijs";
import {
fixNewLines,
getAttachment,
getAttachments,
getGlobalHeaderValue,
getHeaderValue,
parseMIME
} from "../helpers/mailparser";
import dataUriToBlob from "data-uri-to-blob";
import {
extractHtmlBodyFromString,
getFilenameFromHeaders,
SIGNATURE_CONTENT_TYPE
} from "./emailUtilities";
import {
stringToUtf8ByteArray,
utf8ByteArrayToString,
stringToUtf8Base64,
utf8Base64ToString,
base64ToByteArray,
byteArrayToBase64
} from "../utilities/stringUtilities";
const webcryptoLiner = require("webcrypto-liner/build/index");
pkijs.setEngine(
"webcrypto-liner",
webcryptoLiner.crypto,
new pkijs.CryptoEngine({ name: "webcrypto-liner", crypto: webcryptoLiner.crypto, subtle: webcryptoLiner.crypto.subtle })
);
const asn1js = require("asn1js");
const pvutils = require("pvutils");
//*********************************************************************************
const CERTIFIATE_Version_1 = 0;
const CERTIFIATE_Version_3 = 2;
//these bit fields are reversed, WTF!
const KEY_USAGE_DigitalSignature = 0x80; //01;
const KEY_USAGE_NonRepudiation = 0x40; //02;
const KEY_USAGE_KeyEncipherment = 0x20; //04;
const KEY_USAGE_DataEncipherment = 0x10; //08;
const KEY_USAGE_KeyAgreement = 0x08; //10;
const KEY_USAGE_KeyCertSign = 0x04; //20;
const KEY_USAGE_CRLSign = 0x02; //40;
//const KEY_USAGE_EncipherOnly = 0x01;//80; // Not used for now. Must be used together with KEY_USAGE_KeyAgreement (maybe should be ORed as a constant directly?)
//const KEY_USAGE_DecipherOnly = 0x80;//0100; // If used, modify "KeyUsage" extension array buffer size and appropriate bit operators to accomodate for extra byte
const KEY_USAGE_LeafCertificate =
KEY_USAGE_DigitalSignature |
KEY_USAGE_NonRepudiation |
KEY_USAGE_KeyEncipherment |
KEY_USAGE_DataEncipherment;
const KEY_USAGE_CertificateAuthority =
KEY_USAGE_DigitalSignature |
KEY_USAGE_KeyCertSign |
KEY_USAGE_CRLSign;
const OID_EXT_KEY_USAGE_Any = "2.5.29.37.0";
const OID_ID_PKIX_ServerAuth = "1.3.6.1.5.5.7.3.1";
const OID_ID_PKIX_ClientAuth = "1.3.6.1.5.5.7.3.2";
const OID_ID_PKIX_CodeSigning = "1.3.6.1.5.5.7.3.3";
const OID_ID_PKIX_EmailProtection = "1.3.6.1.5.5.7.3.4";
const OID_ID_PKIX_TimeStamping = "1.3.6.1.5.5.7.3.8";
const OID_ID_PKIX_OCSPSigning = "1.3.6.1.5.5.7.3.9";
// const OID_EXT_KEY_USAGE_MS... = "1.3.6.1.4.1.311.10.3.1"; // Microsoft Certificate Trust List signing
// const OID_EXT_KEY_USAGE_MS... = "1.3.6.1.4.1.311.10.3.4"; // Microsoft Encrypted File System
const OID_PKCS7_Data = "1.2.840.113549.1.7.1";
const OID_PKCS7_SignedData = "1.2.840.113549.1.7.2";
const OID_PKCS7_EnvelopedData = "1.2.840.113549.1.7.3";
const OID_PKCS9_EmailAddress = "1.2.840.113549.1.9.1";
const OID_PKCS9_ContentType = "1.2.840.113549.1.9.3";
const OID_PKCS9_MessageDigest = "1.2.840.113549.1.9.4";
const OID_PKCS9_SigningTime = "1.2.840.113549.1.9.5";
const defaultAlgorithms = {
hashAlg: "SHA-256",
signAlg: "RSASSA-PKCS1-v1_5",
keyLength: 2048
};
const encryptionAlgorithm = {
name: "AES-CBC",
length: 128
};
// Convert a hex string to a byte array
function hexStringToBytes(hex) {
let bytes, c;
if (hex.length % 2 === 1) {
hex = "0" + hex;
}
for (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.signatureAlgorithm = null; //read-only, initialized form pkijs.Certificate object
this.algorithms = null; // write only; {hashAlg: "SHA-256", signAlg: "RSASSA-PKCS1-v1_5", keyLength: 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()
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
};
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.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 = {};
if ("publicKey" in parameters.keyPair) {
this.keyPair.publicKey = parameters.keyPair.publicKey;
}
if ("privateKey" in parameters.keyPair) {
this.keyPair.privateKey = parameters.keyPair.privateKey;
}
}
if ("signatureAlgorithm" in parameters) {
this.signatureAlgorithm = parameters.signatureAlgorithm;
}
if ("algorithms" in parameters) {
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
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;
}
}
}
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
//*********************************************************************************
// Returns promise, resolved to keyPair object {publicKey, privateKey}
//*********************************************************************************
function generateKeys(algorithms) {
//region Get a "crypto" extension
const crypto = pkijs.getCrypto();
if (typeof crypto === "undefined") {
return Promise.reject("No WebCrypto extension found");
}
//endregion Get a "crypto" extension
if (!algorithms) {
algorithms = defaultAlgorithms;
} else {
if (!algorithms.hashAlg) {
algorithms.hashAlg = defaultAlgorithms.hashAlg;
}
if (!algorithms.signAlg) {
algorithms.signAlg = defaultAlgorithms.signAlg;
}
if (!algorithms.keyLength) {
algorithms.keyLength = defaultAlgorithms.keyLength;
}
}
//region Get default algorithm parameters for key generation
const algorithm = pkijs.getAlgorithmParameters(
algorithms.signAlg,
"generatekey"
);
if ("hash" in algorithm.algorithm) {
algorithm.algorithm.hash.name = algorithms.hashAlg;
}
algorithm.algorithm.modulusLength = algorithms.keyLength;
//endregion
return crypto.generateKey(algorithm.algorithm, true, algorithm.usages);
}
function fixPkijsRDN() {
pkijs.RelativeDistinguishedNames.prototype.toSchema = function () {
//region Decode stored TBS value
if (this.valueBeforeDecode.byteLength === 0) // No stored encoded array, create "from scratch"
{
value: Array.from(this.typesAndValues, element => new asn1js.Set({value: [element.toSchema()]}))
}
const asn1 = asn1js.fromBER(this.valueBeforeDecode);
//endregion
//region Construct and return new ASN.1 schema for this object
return asn1.result;
//endregion
};
}
//*********************************************************************************
return Promise.reject("No Certificate data provided");
}
if (typeof certData.subject === "undefined" || certData.subject === null) {
return Promise.reject("No Certificate subject data provided");
}
if (typeof certData.subject.commonName === "undefined" || certData.subject.commonName === null) {
return Promise.reject("No Certificate common name provided");
}
//region Get a "crypto" extension
const crypto = pkijs.getCrypto();
if (typeof crypto === "undefined") {
return Promise.reject("No WebCrypto extension found");
}
//endregion Get a "crypto" extension
//region Initial variables
let sequence = Promise.resolve();
const certificate = new pkijs.Certificate();
let publicKey;
let privateKey;
let certificateBuffer; // = new ArrayBuffer(0); // ArrayBuffer with loaded or created CERT
let privateKeyBuffer; // = new ArrayBuffer(0);
let publicKeyBuffer; // = new ArrayBuffer(0);
//endregion Initial variables
if (certData.keyPair) {
//region Create a new key pair
return certData.keyPair;
});
//endregion Create a new key pair
} else {
//region Create a new key pair
return generateKeys(certData.algorithms);
});
//endregion Create a new key pair
}
//region Store new key in an interim variables
sequence = sequence.then(
keyPair => {
publicKey = keyPair.publicKey;
privateKey = keyPair.privateKey;
},
error => Promise.reject(`Error during key generation: ${error}`)
);
//endregion Store new key in an interim variables
//region Exporting public key into "subjectPublicKeyInfo" value of certificate
sequence = sequence.then(() =>
certificate.subjectPublicKeyInfo.importKey(publicKey)
);
//endregion Exporting public key into "subjectPublicKeyInfo" value of certificate
sequence = sequence.then(
//IN IE11 this is NOT a PROMISE !!! but a Crypto Opearation Object
{ name: "SHA-1" },
certificate.subjectPublicKeyInfo.subjectPublicKey.valueBlock.valueHex
error => Promise.reject(`Error during importing public key: ${error}`)
);
//region Fill in cert data
//region Put a static values
certificate.version = CERTIFIATE_Version_3;
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;
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;
while (serialNumberView[0] === 0 && (serialNumberView[1] & 0x80) === 0) {
const firstBytesView = new Uint8Array(serialNumberBuffer, 0, 2);
pkijs.getRandomValues(firstBytesView);
firstBytesView[0] &= 0x7f;
}
// noinspection JSUnresolvedFunction
certificate.serialNumber = new asn1js.Integer({
valueHex: serialNumberView
});
}
//endregion Put a static values
//region Subject
// For reference http://oidref.com/2.5.4.3
if (certData.subject.commonName) {
// noinspection JSUnresolvedFunction
certificate.subject.typesAndValues.push(
new pkijs.AttributeTypeAndValue({
type: "2.5.4.3", // Common name
value: new asn1js.PrintableString({
value: certData.subject.commonName
})
})
);
}
if (certData.subject.country) {
// noinspection JSUnresolvedFunction
certificate.subject.typesAndValues.push(
new pkijs.AttributeTypeAndValue({
type: "2.5.4.6", // Country name
value: new asn1js.PrintableString({ value: certData.subject.country })
})
);
}
if (certData.subject.locality) {
// noinspection JSUnresolvedFunction
certificate.subject.typesAndValues.push(
new pkijs.AttributeTypeAndValue({
type: "2.5.4.7", // Locality Name
value: new asn1js.PrintableString({
value: certData.subject.locality
})
})
);
}
if (certData.subject.state) {
// noinspection JSUnresolvedFunction
certificate.subject.typesAndValues.push(
new pkijs.AttributeTypeAndValue({
type: "2.5.4.8", // State or Province name
value: new asn1js.PrintableString({ value: certData.subject.state })
})
);
}
if (certData.subject.organization) {
// noinspection JSUnresolvedFunction
certificate.subject.typesAndValues.push(
new pkijs.AttributeTypeAndValue({
type: "2.5.4.10", // Organization name
value: new asn1js.PrintableString({
value: certData.subject.organization
})
})
);
}
if (certData.subject.organizationUnit) {
// noinspection JSUnresolvedFunction
certificate.subject.typesAndValues.push(
new pkijs.AttributeTypeAndValue({
type: "2.5.4.11", // Organization unit name
value: new asn1js.PrintableString({
value: certData.subject.organizationUnit
})
})
);
}
if (certData.subject.email) {
// noinspection JSUnresolvedFunction
certificate.subject.typesAndValues.push(
new pkijs.AttributeTypeAndValue({
type: OID_PKCS9_EmailAddress, // Email, deprecated but still widely used
value: new asn1js.IA5String({ value: certData.subject.email })
})
);
}
//endregion Subject
//region Issuer
if (issuerData && issuerData.certificate) {
certificate.issuer = issuerData.certificate.subject;
} else {
certificate.issuer = certificate.subject;
}
//endregion Issuer
//region Validity
if (!certData.validity) {
}
if (certData.validity.notBefore) {
certificate.notBefore.value = certData.validity.notBefore; //date
} else {
const tmp = new Date();
certificate.notBefore.value = new Date(
tmp.getFullYear(),
tmp.getMonth(),
tmp.getDate(),
0,
0,
0
);
}
if (certData.validity.notAfter) {
certificate.notAfter.value = certData.validity.notAfter; //date
} else {
const tmp = certificate.notBefore.value;
const validYears = certData.validity.validYears || 1;
certificate.notAfter.value = new Date(
tmp.getFullYear() + validYears,
tmp.getMonth(),
tmp.getDate(),
23,
59,
59
);
}
//endregion Validity
//region Extensions
certificate.extensions = []; // Extensions are not a part of certificate by default, it's an optional array
//region "BasicConstraints" extension
const basicConstr = new pkijs.BasicConstraints({
//pathLenConstraint: 0 //TODO add logic for leaf CA
});
certificate.extensions.push(
new pkijs.Extension({
extnID: "2.5.29.19",
critical: true,
extnValue: basicConstr.toSchema().toBER(false),
parsedValue: basicConstr // Parsed value for well-known extensions
})
);
//endregion "BasicConstraints" extension
//region "KeyUsage" extension
const keyUsageBuffer = new ArrayBuffer(1);
const keyUsageBitView = new Uint8Array(keyUsageBuffer);
keyUsageBitView[0] = certData.isCA ?
KEY_USAGE_CertificateAuthority :
KEY_USAGE_LeafCertificate;
// noinspection JSUnresolvedFunction
const keyUsage = new asn1js.BitString({ valueHex: keyUsageBuffer });
certificate.extensions.push(
new pkijs.Extension({
extnID: "2.5.29.15",
critical: true,
extnValue: keyUsage.toBER(false),
parsedValue: keyUsage // Parsed value for well-known extensions
})
);
//endregion "KeyUsage" extension
//region "ExtKeyUsage" extension
if (!certData.isCA) {
const keyPurposes = [];
if (certData.subject.url) {
keyPurposes.push(OID_ID_PKIX_ServerAuth, OID_ID_PKIX_ClientAuth);
}
if (certData.subject.email) {
keyPurposes.push(OID_ID_PKIX_EmailProtection);
}
keyPurposes.push(OID_ID_PKIX_TimeStamping);
const extKeyUsage = new pkijs.ExtKeyUsage({
certificate.extensions.push(
new pkijs.Extension({
extnID: "2.5.29.37",
critical: false,
extnValue: extKeyUsage.toSchema().toBER(false),
parsedValue: extKeyUsage // Parsed value for well-known extensions
})
);
}
//endregion "ExtKeyUsage" extension
//region "SubjAltName" extension
if (certData.subject.email || certData.subject.url) {
const names = [];
if (certData.subject.email) {
names.push(
new pkijs.GeneralName({
type: 1, // rfc822Name
value: certData.subject.email
})
);
}
if (certData.subject.url) {
names.push(
new pkijs.GeneralName({
type: 2, // dNSName
value: certData.subject.url
})
);
}
const subjAltNames = new pkijs.GeneralNames({
certificate.extensions.push(
new pkijs.Extension({
extnID: "2.5.29.17",
critical: false,
extnValue: subjAltNames.toSchema().toBER(false),
parsedValue: subjAltNames // Parsed value for well-known extensions
})
);
}
//endregion "SubjAltName" extension
//region "SubjectKeyIdentifier" extension
const subjKeyId = new asn1js.OctetString({ valueHex: subjKeyIdBuffer });
certificate.extensions.push(
new pkijs.Extension({
extnID: "2.5.29.14",
critical: false,
extnValue: subjKeyId.toBER(false),
parsedValue: subjKeyId // Parsed value for well-known extensions
})
);
//endregion "SubjectKeyIdentifier" extension
/* COULD NOT GET IT WORKING
//region "AuthorityKeyIdentifier" extension
if (issuerData && issuerData.certificate) {
let issuerSubjKeyExt = null;
let extLength = issuerData.certificate.extensions.length;
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
let ext = issuerData.certificate.extensions[i];
if (ext.extnID == "2.5.29.14") {
issuerSubjKeyExt = ext;
break;
}
}
if (issuerSubjKeyExt) {
const asn1 = asn1js.fromBER(issuerSubjKeyExt.extnValue);
const authKeyIdentifier = new AuthorityKeyIdentifier({
keyIdentifier: new asn1js.OctetString({
//isHexOnly: true,
//valueHex: issuerSubjKeyExt.parsedValue.valueBlock.valueHex
value: new asn1js.OctetString({ valueHex: subjKeyIdBuffer })
})
});
// const authKeyIdentifier = new AuthorityKeyIdentifier({
// //keyIdentifier: new asn1js.OctetString({ valueHex: subjKeyIdBuffer })
// });
certificate.extensions.push(new Extension({
extnID: "2.5.29.35",
critical: false,
extnValue: authKeyIdentifier.toSchema().toBER(false),
parsedValue: authKeyIdentifier // Parsed value for well-known extensions
}));
}
}
//endregion "AuthorityKeyIdentifier" extension
*/
//endregion Extensions
},error => Promise.reject(`Error calculating hash of public key: ${error}`));
//region Fill in cert data
//region Signing final certificate
const signerKey =
issuerData && issuerData.privateKey ?
issuerData.privateKey :
privateKey;
certData.algorithms && certData.algorithms.hashAlg ?
certData.algorithms.hashAlg :
defaultAlgorithms.hashAlg
error => Promise.reject(`Error during exporting public key: ${error}`)
);
//endregion
//region Encode and store certificate
sequence = sequence.then(
() => {
certificateBuffer = certificate.toSchema(true).toBER(false);
},
error => Promise.reject(`Error during signing: ${error}`)
);
//endregion
//region Exporting public key
sequence = sequence.then(() => crypto.exportKey("spki", publicKey));
//endregion
//region Store exported public key on Web page
sequence = sequence.then(
result => {
publicKeyBuffer = result;
},
error => Promise.reject(`Error during exporting of public key: ${error}`)
);
//endregion
//region Exporting private key
sequence = sequence.then(() => crypto.exportKey("pkcs8", privateKey));
//endregion
//region Store exported key on Web page
sequence = sequence.then(
result => {
privateKeyBuffer = result;
},
error => Promise.reject(`Error during exporting of private key: ${error}`)
);
//endregion
return sequence.then(() => {
const result = {
certificatePEM: encodePEM(certificateBuffer, "CERTIFICATE"),
publicKeyPEM: encodePEM(publicKeyBuffer, "PUBLIC KEY"),
privateKeyPEM: encodePEM(privateKeyBuffer, "PRIVATE KEY")
};
return result;
});
}
function formatPEM(pemString) {
const lineWidth = 64;
let resultString = "";
let start = 0;
let piece;
while ((piece = pemString.substring(start, start + lineWidth)).length > 0) {
start += lineWidth;
}
return resultString;
}
function encodePEM(buffer, label) {
const bufferString = String.fromCharCode.apply(null, new Uint8Array(buffer));
const header = `-----BEGIN ${label}-----\r\n`;
const base64formatted = formatPEM(window.btoa(bufferString));
const footer = `-----END ${label}-----\r\n`;
const resultString = header + base64formatted + footer;
return resultString;
}
function decodePEM(pemString) {
const pemStripped = pemString.replace(
/(-----(BEGIN|END) [a-zA-Z ]*-----|\r|\n)/g,
""
);
const pemDecoded = window.atob(pemStripped);
const buffer = pvutils.stringToArrayBuffer(pemDecoded);
return buffer;
}
//*********************************************************************************
export function parseCertificate(certificatePEM) {
const certificateBuffer = decodePEM(certificatePEM);
const asn1 = asn1js.fromBER(certificateBuffer);
const certificate = new pkijs.Certificate({ schema: asn1.result });
return certificate;
}
//*********************************************************************************
export function encryptMessage(message, password, label) {
const buffer = pvutils.stringToArrayBuffer(message);
const secret = pvutils.stringToArrayBuffer(password);
const enveloped = new pkijs.EnvelopedData();
enveloped.addRecipientByPreDefinedData(
secret,
{},
AES_encryptionVariant_Password
);
return enveloped.encrypt(encryptionAlgorithm, buffer).then(
() => {
const content = new pkijs.ContentInfo();
content.contentType = OID_PKCS7_EnvelopedData;
content.content = enveloped.toSchema();
const ber = content.toSchema().toBER(false);
},
error => Promise.reject(`encryption error: ${error}`)
}
//*********************************************************************************
export function decryptMessage(message, password) {
if (canTryPincode()) {
const secret = pvutils.stringToArrayBuffer(password);
const buffer = decodePEM(message);
const asn1 = asn1js.fromBER(buffer);
const content = new pkijs.ContentInfo({ schema: asn1.result });
const enveloped = new pkijs.EnvelopedData({ schema: content.content });
return enveloped
.decrypt(0, { preDefinedData: secret })
.then(result => {
return pvutils.arrayBufferToString(result);
})
.catch(() => {
failPincodeAttempt(password).then((message) => {
reject(message);
});
});
} else {
return Promise.reject(getTimeLeftInLocalStorage());
}
}
//*********************************************************************************
export function parsePrivateKey(privateKeyPEM) {
const privateKeyBuffer = decodePEM(privateKeyPEM);
const crypto = pkijs.getCrypto();
const privateKeyPromise = crypto.importKey(
"pkcs8",
privateKeyBuffer,
name: "RSASSA-PKCS1-v1_5",
hash: { name: "SHA-256" } //can be "SHA-1", "SHA-256", "SHA-384", or "SHA-512"
},
true,
["sign"]
);
return privateKeyPromise;
}
export function createPassportCertificate(commonNameArg) {
const certDataParams = {
algorithms: {
hashAlg: "SHA-256",
signAlg: "RSASSA-PKCS1-v1_5",
keyLength: 2048
},
//keyPair: generateKeys(), //optional , if provided must be object or promise that resolves to object {publicKey, prvateKey}. If it is not provided, new ones are generated automatically
subject: {
commonName: commonNameArg + "-userdevice", //optional for leaf, recommended for CA
country: "CH", //optional for leaf, recommended for CA
locality: "Zug", //optional for leaf, recommended for CA
state: "Zug", //optional for leaf, recommended for CA
organization: "Vereign AG", //optional for leaf, recommended for CA
organizationUnit: "Business Dep" //optional for leaf, recommended for CA
//email: "damyan.mitev@vereign.com", // added to DN and Subject Alternative Name extension. Optional for CA. Mandatory for leaf certificate, used for email protection
//url: "www.vereign.com" // optional url, recommended for CA, added to Subject Alternative Name extension
},
validity: {
//notBefore: new Date() // optional, defaults to today at 00:00:00
//notAfter: new Date() // optional, defaults to notBefore + validYears at 23:59:59
validYears: 5 //optional, defaults to 1
},
isCA: true // optional flag denoting if this is CA certificate or leaf certificate, defaults to false
};
const certificateData = new CertificateData(certDataParams);
return createCertificate(certificateData, null);
export function createOneTimePassportCertificate(
commonNameArg,
emailArg,
privateKeyIssuerArg,
certicateIssuerArg
) {
certData = {
algorithms: {
hashAlg: "SHA-256",
signAlg: "RSASSA-PKCS1-v1_5",
keyLength: 2048
},
//keyPair: generateKeys(), //optional , if provided must be object or promise that resolves to object {publicKey, prvateKey}. If it is not provided, new ones are generated automatically
subject: {
commonName: commonNameArg + "-onetime", //optional for leaf, recommended for CA
country: "CH", //optional for leaf, recommended for CA
locality: "Zug", //optional for leaf, recommended for CA
state: "Zug", //optional for leaf, recommended for CA
organization: "Vereign AG", //optional for leaf, recommended for CA
organizationUnit: "Business Dep", //optional for leaf, recommended for CA
email: emailArg // added to DN and Subject Alternative Name extension. Optional for CA. Mandatory for leaf certificate, used for email protection
//url: "www.vereign.com" // optional url, recommended for CA, added to Subject Alternative Name extension
},
validity: {
//notBefore: new Date() // optional, defaults to today at 00:00:00
//notAfter: new Date() // optional, defaults to notBefore + validYears at 23:59:59
validYears: 5 //optional, defaults to 1
},
isCA: false // optional flag denoting if this is CA certificate or leaf certificate, defaults to false
return parsePrivateKey(privateKeyIssuerArg).then(privateKeyDecoded => {
const issuerData = {
certificate: parseCertificate(certicateIssuerArg), // vereignCACertPEM),
privateKey: privateKeyDecoded
};
return createCertificate(certData, issuerData);
});
}
function arrayBufferToBase64Formatted(buffer) {
const bufferString = String.fromCharCode.apply(null, new Uint8Array(buffer));