Example #1
0
		return createOCSPReqInternal().then(() =>
		{
			const asn1 = asn1js.fromBER(ocspReqBuffer);
			// noinspection JSUnusedLocalSymbols
			const ocspRequest = new OCSPRequest({ schema: asn1.result });
		});
Example #2
0
//*********************************************************************************
//endregion
//*********************************************************************************
//region Parse "CA Bundle" file
//*********************************************************************************
function parseCAbundle(buffer)
{
	//region Initial variables
	const base64Chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
	
	const startChars = "-----BEGIN CERTIFICATE-----";
	const endChars = "-----END CERTIFICATE-----";
	const endLineChars = "\r\n";
	
	const view = new Uint8Array(buffer);
	
	let waitForStart = false;
	let middleStage = true;
	let waitForEnd = false;
	let waitForEndLine = false;
	let started = false;
	
	let certBodyEncoded = "";
	//endregion
	
	for(let i = 0; i < view.length; i++)
	{
		if(started === true)
		{
			if(base64Chars.indexOf(String.fromCharCode(view[i])) !== (-1))
				certBodyEncoded = certBodyEncoded + String.fromCharCode(view[i]);
			else
			{
				if(String.fromCharCode(view[i]) === "-")
				{
					//region Decoded trustedCertificates
					const asn1 = asn1js.fromBER(stringToArrayBuffer(window.atob(certBodyEncoded)));
					try
					{
						trustedCertificates.push(new Certificate({ schema: asn1.result }));
					}
					catch(ex)
					{
						alert("Wrong certificate format");
						return;
					}
					//endregion
					
					//region Set all "flag variables"
					certBodyEncoded = "";
					
					started = false;
					waitForEnd = true;
					//endregion
				}
			}
		}
		else
		{
			if(waitForEndLine === true)
			{
				if(endLineChars.indexOf(String.fromCharCode(view[i])) === (-1))
				{
					waitForEndLine = false;
					
					if(waitForEnd === true)
					{
						waitForEnd = false;
						middleStage = true;
					}
					else
					{
						if(waitForStart === true)
						{
							waitForStart = false;
							started = true;
							
							certBodyEncoded = certBodyEncoded + String.fromCharCode(view[i]);
						}
						else
							middleStage = true;
					}
				}
			}
			else
			{
				if(middleStage === true)
				{
					if(String.fromCharCode(view[i]) === "-")
					{
						if((i === 0) ||
							((String.fromCharCode(view[i - 1]) === "\r") ||
							(String.fromCharCode(view[i - 1]) === "\n")))
						{
							middleStage = false;
							waitForStart = true;
						}
					}
				}
				else
				{
					if(waitForStart === true)
					{
						if(startChars.indexOf(String.fromCharCode(view[i])) === (-1))
							waitForEndLine = true;
					}
					else
					{
						if(waitForEnd === true)
						{
							if(endChars.indexOf(String.fromCharCode(view[i])) === (-1))
								waitForEndLine = true;
						}
					}
				}
			}
		}
	}
}
Example #3
0
//*********************************************************************************
//endregion 
//*********************************************************************************
//region Parse existing OCSP request  
//*********************************************************************************
function parseOCSPReq()
{
	//region Initial check 
	if(ocspReqBuffer.byteLength === 0)
	{
		alert("Nothing to parse!");
		return;
	}
	//endregion 
	
	//region Initial activities 
	document.getElementById("ocsp-req-extn-div").style.display = "none";
	
	const requestsTable = document.getElementById("ocsp-req-requests");
	while(requestsTable.rows.length > 1)
		requestsTable.deleteRow(requestsTable.rows.length - 1);
	
	const extensionTable = document.getElementById("ocsp-req-extn-table");
	while(extensionTable.rows.length > 1)
		extensionTable.deleteRow(extensionTable.rows.length - 1);
	
	const requestorTable = document.getElementById("ocsp-req-name");
	while(requestorTable.rows.length > 1)
		requestorTable.deleteRow(requestorTable.rows.length - 1);
	//endregion 
	
	//region Decode existing OCSP request
	const asn1 = asn1js.fromBER(ocspReqBuffer);
	const ocspReqSimpl = new OCSPRequest({ schema: asn1.result });
	//endregion 
	
	//region Put information about OCSP request requestor 
	if("requestorName" in ocspReqSimpl.tbsRequest)
	{
		switch(ocspReqSimpl.tbsRequest.requestorName.type)
		{
			case 1: // rfc822Name
			case 2: // dNSName
			case 6: // uniformResourceIdentifier
				// noinspection InnerHTMLJS
				document.getElementById("ocsp-req-name-simpl").innerHTML = ocspReqSimpl.tbsRequest.requestorName.value.valueBlock.value;
				document.getElementById("ocsp-req-nm-simpl").style.display = "block";
				break;
			case 7: // iPAddress
				{
					const view = new Uint8Array(ocspReqSimpl.tbsRequest.requestorName.value.valueBlock.valueHex);
					
					// noinspection InnerHTMLJS
					document.getElementById("ocsp-req-name-simpl").innerHTML = `${view[0].toString()}.${view[1].toString()}.${view[2].toString()}.${view[3].toString()}`;
					document.getElementById("ocsp-req-nm-simpl").style.display = "block";
				}
				break;
			case 3: // x400Address
			case 5: // ediPartyName
				// noinspection InnerHTMLJS
				document.getElementById("ocsp-req-name-simpl").innerHTML = (ocspReqSimpl.tbsRequest.requestorName.type === 3) ? "<type \"x400Address\">" : "<type \"ediPartyName\">";
				document.getElementById("ocsp-req-nm-simpl").style.display = "block";
				break;
			case 4: // directoryName
				{
					const rdnmap = {
						"2.5.4.6": "C",
						"2.5.4.10": "O",
						"2.5.4.11": "OU",
						"2.5.4.3": "CN",
						"2.5.4.7": "L",
						"2.5.4.8": "S",
						"2.5.4.12": "T",
						"2.5.4.42": "GN",
						"2.5.4.43": "I",
						"2.5.4.4": "SN",
						"1.2.840.113549.1.9.1": "E-mail"
					};
					
					for(let i = 0; i < ocspReqSimpl.tbsRequest.requestorName.value.typesAndValues.length; i++)
					{
						let typeval = rdnmap[ocspReqSimpl.tbsRequest.requestorName.value.typesAndValues[i].type];
						if(typeof typeval === "undefined")
							typeval = ocspReqSimpl.tbsRequest.requestorName.value.typesAndValues[i].type;
						
						const subjval = ocspReqSimpl.tbsRequest.requestorName.value.typesAndValues[i].value.valueBlock.value;
						
						const row = requestorTable.insertRow(requestorTable.rows.length);
						const cell0 = row.insertCell(0);
						// noinspection InnerHTMLJS
						cell0.innerHTML = typeval;
						const cell1 = row.insertCell(1);
						// noinspection InnerHTMLJS
						cell1.innerHTML = subjval;
					}
					
					document.getElementById("ocsp-req-name-div").style.display = "block";
				}
				break;
			default:
		}
	}
	//endregion 
	
	//region Put information about requests 
	for(let i = 0; i < ocspReqSimpl.tbsRequest.requestList.length; i++)
	{
		const row = requestsTable.insertRow(requestsTable.rows.length);
		const cell0 = row.insertCell(0);
		// noinspection InnerHTMLJS
		cell0.innerHTML = bufferToHexCodes(ocspReqSimpl.tbsRequest.requestList[i].reqCert.serialNumber.valueBlock.valueHex);
	}
	//endregion 
	
	//region Put information about request extensions 
	if("requestExtensions" in ocspReqSimpl.tbsRequest)
	{
		for(let i = 0; i < ocspReqSimpl.tbsRequest.requestExtensions.length; i++)
		{
			const row = extensionTable.insertRow(extensionTable.rows.length);
			const cell0 = row.insertCell(0);
			// noinspection InnerHTMLJS
			cell0.innerHTML = ocspReqSimpl.tbsRequest.requestExtensions[i].extnID;
		}
		
		document.getElementById("ocsp-req-extn-div").style.display = "block";
	}
	//endregion 
}
Example #4
0
	//**********************************************************************************
	parseInternalValues(parameters)
	{
		//region Check input data from "parameters" 
		if((parameters instanceof Object) === false)
			return Promise.reject("The \"parameters\" must has \"Object\" type");
		
		if(("safeContents" in parameters) === false)
			return Promise.reject("Absent mandatory parameter \"safeContents\"");
		
		if((parameters.safeContents instanceof Array) === false)
			return Promise.reject("The \"parameters.safeContents\" must has \"Array\" type");
		
		if(parameters.safeContents.length !== this.safeContents.length)
			return Promise.reject("Length of \"parameters.safeContents\" must be equal to \"this.safeContents.length\"");
		//endregion 
		
		//region Initial variables 
		let sequence = Promise.resolve();
		//endregion
		
		//region Create value for "this.parsedValue.authenticatedSafe" 
		this.parsedValue = {
			safeContents: []
		};
		
		for(const [index, content] of this.safeContents.entries())
		{
			switch(content.contentType)
			{
				//region data 
				case "1.2.840.113549.1.7.1":
					{
						//region Check that we do have OCTETSTRING as "content"
						if((content.content instanceof asn1js.OctetString) === false)
							return Promise.reject("Wrong type of \"this.safeContents[j].content\"");
						//endregion
						
						//region Parse internal ASN.1 data
						const asn1 = asn1js.fromBER(content.content.valueBlock.valueHex);
						if(asn1.offset === (-1))
							return Promise.reject("Error during parsing of ASN.1 data inside \"content.content\"");
						//endregion
						
						//region Finilly initialize initial values of "SafeContents" type
						this.parsedValue.safeContents.push({
							privacyMode: 0, // No privacy, clear data
							value: new SafeContents({ schema: asn1.result })
						});
						//endregion
					}
					break;
				//endregion 
				//region envelopedData 
				case "1.2.840.113549.1.7.3":
					{
						//region Initial variables
						const cmsEnveloped = new EnvelopedData({ schema: content.content });
						//endregion
						
						//region Check mandatory parameters
						if(("recipientCertificate" in parameters.safeContents[index]) === false)
							return Promise.reject("Absent mandatory parameter \"recipientCertificate\" in \"parameters.safeContents[j]\"");
						
						const recipientCertificate = parameters.safeContents[index].recipientCertificate;
						
						if(("recipientKey" in parameters.safeContents[index]) === false)
							return Promise.reject("Absent mandatory parameter \"recipientKey\" in \"parameters.safeContents[j]\"");
						
						const recipientKey = parameters.safeContents[index].recipientKey;
						//endregion
						
						//region Decrypt CMS EnvelopedData using first recipient information
						sequence = sequence.then(
							() => cmsEnveloped.decrypt(0, {
								recipientCertificate,
								recipientPrivateKey: recipientKey
							})
						);
						
						sequence = sequence.then(
							result =>
							{
								const asn1 = asn1js.fromBER(result);
								if(asn1.offset === (-1))
									return Promise.reject("Error during parsing of decrypted data");
								
								this.parsedValue.safeContents.push({
									privacyMode: 2, // Public-key privacy mode
									value: new SafeContents({ schema: asn1.result })
								});
								
								return Promise.resolve();
							}
						);
						//endregion
					}
					break;
				//endregion   
				//region encryptedData 
				case "1.2.840.113549.1.7.6":
					{
						//region Initial variables
						const cmsEncrypted = new EncryptedData({ schema: content.content });
						//endregion
						
						//region Check mandatory parameters
						if(("password" in parameters.safeContents[index]) === false)
							return Promise.reject("Absent mandatory parameter \"password\" in \"parameters.safeContents[j]\"");
						
						const password = parameters.safeContents[index].password;
						//endregion
						
						//region Decrypt CMS EncryptedData using password
						sequence = sequence.then(
							() => cmsEncrypted.decrypt({
								password
							}),
							error => Promise.reject(error)
						);
						//endregion
						
						//region Initialize internal data
						sequence = sequence.then(
							result =>
							{
								const asn1 = asn1js.fromBER(result);
								if(asn1.offset === (-1))
									return Promise.reject("Error during parsing of decrypted data");
								
								this.parsedValue.safeContents.push({
									privacyMode: 1, // Password-based privacy mode
									value: new SafeContents({ schema: asn1.result })
								});
								
								return Promise.resolve();
							},
							error => Promise.reject(error)
						);
						//endregion
					}
					break;
				//endregion   
				//region default 
				default:
					throw new Error(`Unknown \"contentType\" for AuthenticatedSafe: " ${content.contentType}`);
				//endregion 
			}
		}
		//endregion 
		
		return sequence;
	}
Example #5
0
//*********************************************************************************
//endregion
//*********************************************************************************
//region Parse existing TSP response
//*********************************************************************************
function parseTSPResp()
{
	//region Initial activities
	document.getElementById("resp-accur").style.display = "none";
	document.getElementById("resp-ord").style.display = "none";
	document.getElementById("resp-non").style.display = "none";
	document.getElementById("resp-ts-rdn").style.display = "none";
	document.getElementById("resp-ts-simpl").style.display = "none";
	document.getElementById("resp-ext").style.display = "none";
	
	const imprTable = document.getElementById("resp-imprint");
	while(imprTable.rows.length > 1)
		imprTable.deleteRow(imprTable.rows.length - 1);
	
	const accurTable = document.getElementById("resp-accuracy");
	while(accurTable.rows.length > 1)
		accurTable.deleteRow(accurTable.rows.length - 1);
	
	const tsTable = document.getElementById("resp-tsa");
	while(tsTable.rows.length > 1)
		tsTable.deleteRow(tsTable.rows.length - 1);
	
	const extTable = document.getElementById("resp-extensions");
	while(extTable.rows.length > 1)
		extTable.deleteRow(extTable.rows.length - 1);
	//endregion
	
	//region Decode existing TSP response
	const asn1 = asn1js.fromBER(tspResponseBuffer);
	const tspRespSimpl = new TimeStampResp({ schema: asn1.result });
	//endregion
	
	//region Put information about TSP response status
	let status = "";
	
	switch(tspRespSimpl.status.status)
	{
		case 0:
			status = "granted";
			break;
		case 1:
			status = "grantedWithMods";
			break;
		case 2:
			status = "rejection";
			break;
		case 3:
			status = "waiting";
			break;
		case 4:
			status = "revocationWarning";
			break;
		case 5:
			status = "revocationNotification";
			break;
		default:
	}
	
	document.getElementById("resp-status").innerHTML = status;
	//endregion
	
	//region Parse internal CMS Signed Data
	if(("timeStampToken" in tspRespSimpl) === false)
	{
		alert("No additional info but PKIStatusInfo");
		return;
	}
	
	const signedSimpl = new SignedData({ schema: tspRespSimpl.timeStampToken.content });
	
	const asn1TST = asn1js.fromBER(signedSimpl.encapContentInfo.eContent.valueBlock.valueHex);
	const tstInfoSimpl = new TSTInfo({ schema: asn1TST.result });
	//endregion
	
	//region Put information about policy
	document.getElementById("resp-policy").innerHTML = tstInfoSimpl.policy;
	//endregion
	
	//region Put information about TST info message imprint
	const dgstmap = {
		"1.3.14.3.2.26": "SHA-1",
		"2.16.840.1.101.3.4.2.1": "SHA-256",
		"2.16.840.1.101.3.4.2.2": "SHA-384",
		"2.16.840.1.101.3.4.2.3": "SHA-512"
	};
	
	let hashAlgorithm = dgstmap[tstInfoSimpl.messageImprint.hashAlgorithm.algorithmId];
	if(typeof hashAlgorithm === "undefined")
		hashAlgorithm = tstInfoSimpl.messageImprint.hashAlgorithm.algorithmId;
	
	const imprintTable = document.getElementById("resp-imprint");
	
	const row = imprintTable.insertRow(imprintTable.rows.length);
	const cell0 = row.insertCell(0);
	cell0.innerHTML = hashAlgorithm;
	const cell1 = row.insertCell(1);
	cell1.innerHTML = bufferToHexCodes(tstInfoSimpl.messageImprint.hashedMessage.valueBlock.valueHex);
	//endregion
	
	//region Put information about TST info serial number
	document.getElementById("resp-serial").innerHTML = bufferToHexCodes(tstInfoSimpl.serialNumber.valueBlock.valueHex);
	//endregion
	
	//region Put information about the time when TST info was generated
	document.getElementById("resp-time").innerHTML = tstInfoSimpl.genTime.toString();
	//endregion
	
	//region Put information about TST info accuracy
	if("accuracy" in tstInfoSimpl)
	{
		const accuracyTable = document.getElementById("resp-accuracy");
		
		const rowInner = accuracyTable.insertRow(accuracyTable.rows.length);
		const cell0Inner = rowInner.insertCell(0);
		cell0Inner.innerHTML = ("seconds" in tstInfoSimpl.accuracy) ? tstInfoSimpl.accuracy.seconds : 0;
		const cell1Inner = rowInner.insertCell(1);
		cell1Inner.innerHTML = ("millis" in tstInfoSimpl.accuracy) ? tstInfoSimpl.accuracy.millis : 0;
		const cell2 = rowInner.insertCell(2);
		cell2.innerHTML = ("micros" in tstInfoSimpl.accuracy) ? tstInfoSimpl.accuracy.micros : 0;
		
		document.getElementById("resp-accur").style.display = "block";
	}
	//endregion
	
	//region Put information about TST info ordering
	if("ordering" in tstInfoSimpl)
	{
		document.getElementById("resp-ordering").innerHTML = tstInfoSimpl.ordering.toString();
		document.getElementById("resp-ord").style.display = "block";
	}
	//endregion
	
	//region Put information about TST info nonce value
	if("nonce" in tstInfoSimpl)
	{
		document.getElementById("resp-nonce").innerHTML = bufferToHexCodes(tstInfoSimpl.nonce.valueBlock.valueHex);
		document.getElementById("resp-non").style.display = "block";
	}
	//endregion
	
	//region Put information about TST info TSA
	if("tsa" in tstInfoSimpl)
	{
		switch(tstInfoSimpl.tsa.type)
		{
			case 1: // rfc822Name
			case 2: // dNSName
			case 6: // uniformResourceIdentifier
				document.getElementById("resp-tsa-simpl").innerHTML = tstInfoSimpl.tsa.value.valueBlock.value;
				document.getElementById("resp-ts-simpl").style.display = "block";
				break;
			case 7: // iPAddress
				{
					const view = new Uint8Array(tstInfoSimpl.tsa.value.valueBlock.valueHex);
					
					document.getElementById("resp-tsa-simpl").innerHTML = `${view[0].toString()}.${view[1].toString()}.${view[2].toString()}.${view[3].toString()}`;
					document.getElementById("resp-ts-simpl").style.display = "block";
				}
				break;
			case 3: // x400Address
			case 5: // ediPartyName
				document.getElementById("resp-tsa-simpl").innerHTML = (tstInfoSimpl.tsa.type === 3) ? "<type \"x400Address\">" : "<type \"ediPartyName\">";
				document.getElementById("resp-ts-simpl").style.display = "block";
				break;
			case 4: // directoryName
				{
					const rdnmap = {
						"2.5.4.6": "C",
						"2.5.4.10": "OU",
						"2.5.4.11": "O",
						"2.5.4.3": "CN",
						"2.5.4.7": "L",
						"2.5.4.8": "S",
						"2.5.4.12": "T",
						"2.5.4.42": "GN",
						"2.5.4.43": "I",
						"2.5.4.4": "SN",
						"1.2.840.113549.1.9.1": "E-mail"
					};
					
					const rdnTable = document.getElementById("resp-tsa");
					
					for(let i = 0; i < tstInfoSimpl.tsa.value.typesAndValues.length; i++)
					{
						let typeval = rdnmap[tstInfoSimpl.tsa.value.typesAndValues[i].type];
						if(typeof typeval === "undefined")
							typeval = tstInfoSimpl.tsa.value.typesAndValues[i].type;
						
						const subjval = tstInfoSimpl.tsa.value.typesAndValues[i].value.valueBlock.value;
						
						const rowInner = rdnTable.insertRow(rdnTable.rows.length);
						const cell0Inner = rowInner.insertCell(0);
						cell0Inner.innerHTML = typeval;
						const cell1Inner = rowInner.insertCell(1);
						cell1Inner.innerHTML = subjval;
					}
					
					document.getElementById("resp-ts-rdn").style.display = "block";
				}
				break;
			default:
		}
	}
	//endregion
	
	//region Put information about TST info extensions
	if("extensions" in tstInfoSimpl)
	{
		const extensionTable = document.getElementById("resp-extensions");
		
		for(let i = 0; i < tstInfoSimpl.extensions.length; i++)
		{
			const rowInner = extensionTable.insertRow(extensionTable.rows.length);
			const cell0Inner = rowInner.insertCell(0);
			cell0Inner.innerHTML = tstInfoSimpl.extensions[i].extnID;
		}
		
		document.getElementById("resp-ext").style.display = "block";
	}
	//endregion
}
Example #6
0
//*********************************************************************************
async function parseOpenSSLPrivateKey()
{
	let keyLength = 0;
	let base64 = "";

	const headerExp = /([\x21-\x7e]+):\s*([\x21-\x7e\s^:]+)/;

	const stringPEM = document.getElementById("openssl_data").value.replace(/(-----(BEGIN|END) RSA PRIVATE KEY-----)/g, "");
	const lines = stringPEM.split(/\r?\n/);

	let dekFound = false;
	let iv = new ArrayBuffer(0);

	for(let i = 0; i < lines.length; i++)
	{
		const lineMatch = lines[i].match(headerExp);
		if(lineMatch !== null)
		{
			if(lineMatch[1] === "DEK-Info")
			{
				dekFound = true;

				const values = lineMatch[2].split(",");

				for(let j = 0; j < values.length; j++)
					values[j] = values[j].trim();

				switch(values[0].toLocaleUpperCase())
				{
					case "AES-128-CBC":
						keyLength = 16;
						break;
					case "AES-192-CBC":
						keyLength = 24;
						break;
					case "AES-256-CBC":
						keyLength = 32;
						break;
					default:
						throw new Error(`Unsupported apgorithm ${values[0].toLocaleUpperCase()}`);
				}

				iv = hex2b(values[1]);
			}
		}
		else
		{
			if(dekFound)
				base64 += lines[i];
		}
	}

	if(dekFound === false)
		throw new Error("Can not find DEK-Info section!");

	const dataBuffer = await decryptOpenSSLPrivateKey(stringToArrayBuffer(fromBase64(base64.trim())), stringToArrayBuffer(document.getElementById("password").value), "AES-CBC", keyLength, iv);

	const asn1 = asn1js.fromBER(dataBuffer);
	if(asn1.offset === (-1))
		throw new Error("Incorect encrypted key");

	//const privateKeyInfo = new PrivateKeyInfo({ schema: asn1.result });
	const rsaPrivateKey = new RSAPrivateKey({ schema: asn1.result });

	let resultString = "-----BEGIN RSA PRIVATE KEY-----\r\n";
	//resultString = `${resultString}${formatPEM(toBase64(arrayBufferToString(privateKeyInfo.toSchema().toBER(false))))}`;
	resultString = `${resultString}${formatPEM(toBase64(arrayBufferToString(rsaPrivateKey.toSchema().toBER(false))))}`;
	//resultString = `${resultString}${formatPEM(toBase64(arrayBufferToString(dataBuffer)))}`;
	resultString = `${resultString}\r\n-----END RSA PRIVATE KEY-----\r\n`;

	document.getElementById("pkijs_data").value = resultString;
}
Example #7
0
//*********************************************************************************
function parseCertificate()
{
	//region Initial check
	if(certificateBuffer.byteLength === 0)
	{
		alert("Nothing to parse!");
		return;
	}
	//endregion
	
	//region Initial activities
	document.getElementById("cert-extn-div").style.display = "none";
	
	const issuerTable = document.getElementById("cert-issuer-table");
	while(issuerTable.rows.length > 1)
		issuerTable.deleteRow(issuerTable.rows.length - 1);
	
	const subjectTable = document.getElementById("cert-subject-table");
	while(subjectTable.rows.length > 1)
		subjectTable.deleteRow(subjectTable.rows.length - 1);
	
	const extensionTable = document.getElementById("cert-extn-table");
	while(extensionTable.rows.length > 1)
		extensionTable.deleteRow(extensionTable.rows.length - 1);
	//endregion
	
	//region Decode existing X.509 certificate
	const asn1 = asn1js.fromBER(certificateBuffer);
	const certificate = new Certificate({ schema: asn1.result });
	//endregion
	
	//region Put information about X.509 certificate issuer
	const rdnmap = {
		"2.5.4.6": "C",
		"2.5.4.10": "O",
		"2.5.4.11": "OU",
		"2.5.4.3": "CN",
		"2.5.4.7": "L",
		"2.5.4.8": "S",
		"2.5.4.12": "T",
		"2.5.4.42": "GN",
		"2.5.4.43": "I",
		"2.5.4.4": "SN",
		"1.2.840.113549.1.9.1": "E-mail"
	};
	
	for(const typeAndValue of certificate.issuer.typesAndValues)
	{
		let typeval = rdnmap[typeAndValue.type];
		if(typeof typeval === "undefined")
			typeval = typeAndValue.type;
		
		const subjval = typeAndValue.value.valueBlock.value;
		
		const row = issuerTable.insertRow(issuerTable.rows.length);
		const cell0 = row.insertCell(0);
		// noinspection InnerHTMLJS
		cell0.innerHTML = typeval;
		const cell1 = row.insertCell(1);
		// noinspection InnerHTMLJS
		cell1.innerHTML = subjval;
	}
	//endregion
	
	//region Put information about X.509 certificate subject
	for(const typeAndValue of certificate.subject.typesAndValues)
	{
		let typeval = rdnmap[typeAndValue.type];
		if(typeof typeval === "undefined")
			typeval = typeAndValue.type;
		
		const subjval = typeAndValue.value.valueBlock.value;
		
		const row = subjectTable.insertRow(subjectTable.rows.length);
		const cell0 = row.insertCell(0);
		// noinspection InnerHTMLJS
		cell0.innerHTML = typeval;
		const cell1 = row.insertCell(1);
		// noinspection InnerHTMLJS
		cell1.innerHTML = subjval;
	}
	//endregion
	
	//region Put information about X.509 certificate serial number
	// noinspection InnerHTMLJS
	document.getElementById("cert-serial-number").innerHTML = bufferToHexCodes(certificate.serialNumber.valueBlock.valueHex);
	//endregion
	
	//region Put information about issuance date
	// noinspection InnerHTMLJS
	document.getElementById("cert-not-before").innerHTML = certificate.notBefore.value.toString();
	//endregion
	
	//region Put information about expiration date
	// noinspection InnerHTMLJS
	document.getElementById("cert-not-after").innerHTML = certificate.notAfter.value.toString();
	//endregion
	
	//region Put information about subject public key size
	let publicKeySize = "< unknown >";
	
	if(certificate.subjectPublicKeyInfo.algorithm.algorithmId.indexOf("1.2.840.113549") !== (-1))
	{
		const asn1PublicKey = asn1js.fromBER(certificate.subjectPublicKeyInfo.subjectPublicKey.valueBlock.valueHex);
		const rsaPublicKey = new RSAPublicKey({ schema: asn1PublicKey.result });
		
		const modulusView = new Uint8Array(rsaPublicKey.modulus.valueBlock.valueHex);
		let modulusBitLength = 0;
		
		if(modulusView[0] === 0x00)
			modulusBitLength = (rsaPublicKey.modulus.valueBlock.valueHex.byteLength - 1) * 8;
		else
			modulusBitLength = rsaPublicKey.modulus.valueBlock.valueHex.byteLength * 8;
		
		publicKeySize = modulusBitLength.toString();
	}
	
	// noinspection InnerHTMLJS
	document.getElementById("cert-keysize").innerHTML = publicKeySize;
	//endregion
	
	//region Put information about signature algorithm
	const algomap = {
		"1.2.840.113549.1.1.2": "MD2 with RSA",
		"1.2.840.113549.1.1.4": "MD5 with RSA",
		"1.2.840.10040.4.3": "SHA1 with DSA",
		"1.2.840.10045.4.1": "SHA1 with ECDSA",
		"1.2.840.10045.4.3.2": "SHA256 with ECDSA",
		"1.2.840.10045.4.3.3": "SHA384 with ECDSA",
		"1.2.840.10045.4.3.4": "SHA512 with ECDSA",
		"1.2.840.113549.1.1.10": "RSA-PSS",
		"1.2.840.113549.1.1.5": "SHA1 with RSA",
		"1.2.840.113549.1.1.14": "SHA224 with RSA",
		"1.2.840.113549.1.1.11": "SHA256 with RSA",
		"1.2.840.113549.1.1.12": "SHA384 with RSA",
		"1.2.840.113549.1.1.13": "SHA512 with RSA"
	};       // array mapping of common algorithm OIDs and corresponding types
	
	let signatureAlgorithm = algomap[certificate.signatureAlgorithm.algorithmId];
	if(typeof signatureAlgorithm === "undefined")
		signatureAlgorithm = certificate.signatureAlgorithm.algorithmId;
	else
		signatureAlgorithm = `${signatureAlgorithm} (${certificate.signatureAlgorithm.algorithmId})`;
	
	// noinspection InnerHTMLJS
	document.getElementById("cert-sign-algo").innerHTML = signatureAlgorithm;
	//endregion
	
	//region Put information about certificate extensions
	if("extensions" in certificate)
	{
		for(let i = 0; i < certificate.extensions.length; i++)
		{
			const row = extensionTable.insertRow(extensionTable.rows.length);
			const cell0 = row.insertCell(0);
			// noinspection InnerHTMLJS
			cell0.innerHTML = certificate.extensions[i].extnID;
		}
		
		document.getElementById("cert-extn-div").style.display = "block";
	}
	//endregion
}
Example #8
0
//*********************************************************************************
//endregion
//*********************************************************************************
//region Verify SMIME signature
//*********************************************************************************
function verifySMIME()
{
	//region Parse MIME contents to find signature and detached data
	const parser = parse(document.getElementById("smime_message").value);
	//endregion
	
	// noinspection JSUnresolvedVariable
	if(("childNodes" in parser) || (parser.childNodes.length !== 2))
	{
		// noinspection JSUnresolvedVariable
		const lastNode = parser.childNodes[1];
		if((lastNode.contentType.value === "application/x-pkcs7-signature") || (lastNode.contentType.value === "application/pkcs7-signature"))
		{
			// Parse into pkijs types
			const asn1 = asn1js.fromBER(lastNode.content.buffer);
			if(asn1.offset === (-1))
			{
				alert("Incorrect message format!");
				return;
			}
			
			let cmsContentSimpl;
			let cmsSignedSimpl;
			
			try
			{
				cmsContentSimpl = new ContentInfo({ schema: asn1.result });
				cmsSignedSimpl = new SignedData({ schema: cmsContentSimpl.content });
			}
			catch(ex)
			{
				alert("Incorrect message format!");
				return;
			}
			
			// Get signed data buffer
			// noinspection JSUnresolvedVariable
			const signedDataBuffer = stringToArrayBuffer(parser.childNodes[0].raw.replace(/\n/g, "\r\n"));
			
			// Verify the signed data
			let sequence = Promise.resolve();
			sequence = sequence.then(
				() => cmsSignedSimpl.verify({ signer: 0, data: signedDataBuffer, trustedCerts: trustedCertificates })
			);
			
			sequence.then(
				result =>
				{
					let failed = false;
					if(typeof result !== "undefined")
					{
						if(result === false)
							failed = true;
					}
					
					alert(`S/MIME message ${(failed) ? "verification failed" : "successfully verified"}!`);
				},
				error =>
					alert(`Error during verification: ${error}`)
			);
		}
	}
	else
		alert("No child nodes!");
}
Example #9
0
const trustedCertificates = []; // Array of Certificates
//*********************************************************************************
function verifyPDFSignature(buffer)
{
	try
	{
		const view = new Uint8Array(buffer);
		const pdf = new window.PDFDocument(null, view, null);
		pdf.parseStartXRef();
		pdf.parse();
		
		const acroForm = pdf.xref.root.get("AcroForm");
		if(typeof acroForm === "undefined")
			throw new Error("The PDF has no signature!");
		
		const fields = acroForm.get("Fields");
		if(window.isRef(fields[0]) === false)
			throw new Error("Wrong structure of PDF!");
		
		const sigField = pdf.xref.fetch(fields[0]);
		
		const sigFieldType = sigField.get("FT");
		if((typeof sigFieldType === "undefined") || (sigFieldType.name !== "Sig"))
			throw new Error("Wrong structure of PDF!");
		
		const v = sigField.get("V");
		const byteRange = v.get("ByteRange");
		const contents = v.get("Contents");
		
		const contentLength = contents.length;
		const contentBuffer = new ArrayBuffer(contentLength);
		const contentView = new Uint8Array(contentBuffer);
		
		for(let i = 0; i < contentLength; i++)
			contentView[i] = contents.charCodeAt(i);
		
		let sequence = Promise.resolve();
		
		const asn1 = asn1js.fromBER(contentBuffer);
		
		const cmsContentSimp = new ContentInfo({ schema: asn1.result });
		const cmsSignedSimp = new SignedData({ schema: cmsContentSimp.content });
		
		const signedDataBuffer = new ArrayBuffer(byteRange[1] + byteRange[3]);
		const signedDataView = new Uint8Array(signedDataBuffer);
		
		let count = 0;
		for(let i = byteRange[0]; i < (byteRange[0] + byteRange[1]); i++, count++)
			signedDataView[count] = view[i];
		
		for(let j = byteRange[2]; j < (byteRange[2] + byteRange[3]); j++, count++)
			signedDataView[count] = view[j];
		
		sequence = sequence.then(() => cmsSignedSimp.verify({
			signer: 0,
			data: signedDataBuffer,
			trustedCerts: trustedCertificates
		}));
		
		if("signedAttrs" in cmsSignedSimp.signerInfos[0])
		{
			const crypto = getCrypto();
			if(typeof crypto === "undefined")
				throw new Error("WebCrypto extension is not installed");
			
			let shaAlgorithm = "";
			
			switch(cmsSignedSimp.signerInfos[0].digestAlgorithm.algorithmId)
			{
				case "1.3.14.3.2.26":
					shaAlgorithm = "sha-1";
					break;
				case "2.16.840.1.101.3.4.2.1":
					shaAlgorithm = "sha-256";
					break;
				case "2.16.840.1.101.3.4.2.2":
					shaAlgorithm = "sha-384";
					break;
				case "2.16.840.1.101.3.4.2.3":
					shaAlgorithm = "sha-512";
					break;
				default:
					throw new Error("Unknown hashing algorithm");
			}
			
			sequence = sequence.then((result) =>
			{
				if(result === false)
					return Promise.reject(new Error("Signature verification failed"));
				
				return crypto.digest({ name: shaAlgorithm }, new Uint8Array(signedDataBuffer));
			});
			
			sequence = sequence.then((result) =>
			{
				let messageDigest = new ArrayBuffer(0);
				
				for(let j = 0; j < cmsSignedSimp.signerInfos[0].signedAttrs.attributes.length; j++)
				{
					if(cmsSignedSimp.signerInfos[0].signedAttrs.attributes[j].type === "1.2.840.113549.1.9.4")
					{
						messageDigest = cmsSignedSimp.signerInfos[0].signedAttrs.attributes[j].values[0].valueBlock.valueHex;
						break;
					}
				}
				
				if(messageDigest.byteLength === 0)
					return Promise.reject(new Error("No signed attribute 'MessageDigest'"));
				
				const view1 = new Uint8Array(messageDigest);
				const view2 = new Uint8Array(result);
				
				if(view1.length !== view2.length)
					return Promise.reject(new Error("Hash is not correct"));
				
				for(let i = 0; i < view1.length; i++)
				{
					if(view1[i] !== view2[i])
						return Promise.reject(new Error("Hash is not correct"));
				}
			});
		}
		
		sequence = sequence.then((result) =>
		{
			if(typeof result !== "undefined")
			{
				if(result === false)
				{
					alert("PDF verification failed!");
					return;
				}
			}
			
			alert("PDF successfully verified!");
		});
		
		return sequence.catch((e) =>
		{
			throw e;
		});
	}
	catch(e)
	{
		console.error(e.stack);
	}
}
Example #10
0
	//**********************************************************************************
	/**
	 * Convert parsed asn1js object into current class
	 * @param {!Object} schema
	 */
	fromSchema(schema)
	{
		//region Clear input data first
		clearProps(schema, [
			"algorithm",
			"subjectPublicKey"
		]);
		//endregion
		
		//region Check the schema is valid
		const asn1 = asn1js.compareSchema(schema,
			schema,
			PublicKeyInfo.schema({
				names: {
					algorithm: {
						names: {
							blockName: "algorithm"
						}
					},
					subjectPublicKey: "subjectPublicKey"
				}
			})
		);
		
		if(asn1.verified === false)
			throw new Error("Object's schema was not verified against input data for PublicKeyInfo");
		//endregion
		
		//region Get internal properties from parsed schema
		this.algorithm = new AlgorithmIdentifier({ schema: asn1.result.algorithm });
		this.subjectPublicKey = asn1.result.subjectPublicKey;
		
		switch(this.algorithm.algorithmId)
		{
			case "1.2.840.10045.2.1": // ECDSA
				if("algorithmParams" in this.algorithm)
				{
					if(this.algorithm.algorithmParams instanceof asn1js.ObjectIdentifier)
					{
						try
						{
							this.parsedKey = new ECPublicKey({
								namedCurve: this.algorithm.algorithmParams.valueBlock.toString(),
								schema: this.subjectPublicKey.valueBlock.valueHex
							});
						}
						catch(ex){} // Could be a problems during recognision of internal public key data here. Let's ignore them.
					}
				}
				break;
			case "1.2.840.113549.1.1.1": // RSA
				{
					const publicKeyASN1 = asn1js.fromBER(this.subjectPublicKey.valueBlock.valueHex);
					if(publicKeyASN1.offset !== (-1))
					{
						try
						{
							this.parsedKey = new RSAPublicKey({ schema: publicKeyASN1.result });
						}
						catch(ex){} // Could be a problems during recognision of internal public key data here. Let's ignore them.
					}
				}
				break;
			default:
		}
		//endregion
	}
Example #11
0
	//**********************************************************************************
	/**
	 * Convert current object to asn1js object and set correct values
	 * @param {boolean} encodeFlag If param equal to false then create TBS schema via decoding stored value. In othe case create TBS schema via assembling from TBS parts.
	 * @returns {Object} asn1js object
	 */
	toSchema(encodeFlag = false)
	{
		//region Decode stored TBS value 
		let tbsSchema;
		
		if(encodeFlag === false)
		{
			if(this.tbs.length === 0) // No stored certificate TBS part
				return ResponseData.schema();
			
			tbsSchema = asn1js.fromBER(this.tbs).result;
		}
		//endregion 
		//region Create TBS schema via assembling from TBS parts
		else
		{
			const outputArray = [];
			
			if("version" in this)
			{
				outputArray.push(new asn1js.Constructed({
					idBlock: {
						tagClass: 3, // CONTEXT-SPECIFIC
						tagNumber: 0 // [0]
					},
					value: [new asn1js.Integer({ value: this.version })]
				}));
			}
			
			if(this.responderID instanceof RelativeDistinguishedNames)
			{
				outputArray.push(new asn1js.Constructed({
					idBlock: {
						tagClass: 3, // CONTEXT-SPECIFIC
						tagNumber: 1 // [1]
					},
					value: [this.responderID.toSchema()]
				}));
			}
			else
			{
				outputArray.push(new asn1js.Constructed({
					idBlock: {
						tagClass: 3, // CONTEXT-SPECIFIC
						tagNumber: 2 // [2]
					},
					value: [this.responderID]
				}));
			}
			
			outputArray.push(new asn1js.GeneralizedTime({ valueDate: this.producedAt }));

			outputArray.push(new asn1js.Sequence({
				value: Array.from(this.responses, element => element.toSchema())
			}));
			
			if("responseExtensions" in this)
			{
				outputArray.push(new asn1js.Sequence({
					value: Array.from(this.responseExtensions, element => element.toSchema())
				}));
			}
			
			tbsSchema = new asn1js.Sequence({
				value: outputArray
			});
		}
		//endregion 
		
		//region Construct and return new ASN.1 schema for this object 
		return tbsSchema;
		//endregion 
	}
Example #12
0
	//**********************************************************************************
	/**
	 * Convert parsed asn1js object into current class
	 * @param {!Object} schema
	 */
	fromSchema(schema)
	{
		//region Clear input data first
		clearProps(schema, [
			"extnID",
			"critical",
			"extnValue"
		]);
		//endregion
		
		//region Check the schema is valid
		let asn1 = asn1js.compareSchema(schema,
			schema,
			Extension.schema({
				names: {
					extnID: "extnID",
					critical: "critical",
					extnValue: "extnValue"
				}
			})
		);

		if(asn1.verified === false)
			throw new Error("Object's schema was not verified against input data for Extension");
		//endregion

		//region Get internal properties from parsed schema
		this.extnID = asn1.result.extnID.valueBlock.toString();
		if("critical" in asn1.result)
			this.critical = asn1.result.critical.valueBlock.value;
		this.extnValue = asn1.result.extnValue;

		//region Get "parsedValue" for well-known extensions
		asn1 = asn1js.fromBER(this.extnValue.valueBlock.valueHex);
		if(asn1.offset === (-1))
			return;

		switch(this.extnID)
		{
			case "2.5.29.9": // SubjectDirectoryAttributes
				try
				{
					this.parsedValue = new SubjectDirectoryAttributes({ schema: asn1.result });
				}
				catch(ex)
				{
					this.parsedValue = new SubjectDirectoryAttributes();
					this.parsedValue.parsingError = "Incorrectly formated SubjectDirectoryAttributes";
				}
				break;
			case "2.5.29.14": // SubjectKeyIdentifier
				this.parsedValue = asn1.result; // Should be just a simple OCTETSTRING
				break;
			case "2.5.29.15": // KeyUsage
				this.parsedValue = asn1.result; // Should be just a simple BITSTRING
				break;
			case "2.5.29.16": // PrivateKeyUsagePeriod
				try
				{
					this.parsedValue = new PrivateKeyUsagePeriod({ schema: asn1.result });
				}
				catch(ex)
				{
					this.parsedValue = new PrivateKeyUsagePeriod();
					this.parsedValue.parsingError = "Incorrectly formated PrivateKeyUsagePeriod";
				}
				break;
			case "2.5.29.17": // SubjectAltName
			case "2.5.29.18": // IssuerAltName
				try
				{
					this.parsedValue = new AltName({ schema: asn1.result });
				}
				catch(ex)
				{
					this.parsedValue = new AltName();
					this.parsedValue.parsingError = "Incorrectly formated AltName";
				}
				break;
			case "2.5.29.19": // BasicConstraints
				try
				{
					this.parsedValue = new BasicConstraints({ schema: asn1.result });
				}
				catch(ex)
				{
					this.parsedValue = new BasicConstraints();
					this.parsedValue.parsingError = "Incorrectly formated BasicConstraints";
				}
				break;
			case "2.5.29.20": // CRLNumber
			case "2.5.29.27": // BaseCRLNumber (delta CRL indicator)
				this.parsedValue = asn1.result; // Should be just a simple INTEGER
				break;
			case "2.5.29.21": // CRLReason
				this.parsedValue = asn1.result; // Should be just a simple ENUMERATED
				break;
			case "2.5.29.24": // InvalidityDate
				this.parsedValue = asn1.result; // Should be just a simple GeneralizedTime
				break;
			case "2.5.29.28": // IssuingDistributionPoint
				try
				{
					this.parsedValue = new IssuingDistributionPoint({ schema: asn1.result });
				}
				catch(ex)
				{
					this.parsedValue = new IssuingDistributionPoint();
					this.parsedValue.parsingError = "Incorrectly formated IssuingDistributionPoint";
				}
				break;
			case "2.5.29.29": // CertificateIssuer
				try
				{
					this.parsedValue = new GeneralNames({ schema: asn1.result }); // Should be just a simple
				}
				catch(ex)
				{
					this.parsedValue = new GeneralNames();
					this.parsedValue.parsingError = "Incorrectly formated GeneralNames";
				}
				break;
			case "2.5.29.30": // NameConstraints
				try
				{
					this.parsedValue = new NameConstraints({ schema: asn1.result });
				}
				catch(ex)
				{
					this.parsedValue = new NameConstraints();
					this.parsedValue.parsingError = "Incorrectly formated NameConstraints";
				}
				break;
			case "2.5.29.31": // CRLDistributionPoints
			case "2.5.29.46": // FreshestCRL
				try
				{
					this.parsedValue = new CRLDistributionPoints({ schema: asn1.result });
				}
				catch(ex)
				{
					this.parsedValue = new CRLDistributionPoints();
					this.parsedValue.parsingError = "Incorrectly formated CRLDistributionPoints";
				}
				break;
			case "2.5.29.32": // CertificatePolicies
			case "1.3.6.1.4.1.311.21.10": // szOID_APPLICATION_CERT_POLICIES - Microsoft-specific OID
				try
				{
					this.parsedValue = new CertificatePolicies({ schema: asn1.result });
				}
				catch(ex)
				{
					this.parsedValue = new CertificatePolicies();
					this.parsedValue.parsingError = "Incorrectly formated CertificatePolicies";
				}
				break;
			case "2.5.29.33": // PolicyMappings
				try
				{
					this.parsedValue = new PolicyMappings({ schema: asn1.result });
				}
				catch(ex)
				{
					this.parsedValue = new PolicyMappings();
					this.parsedValue.parsingError = "Incorrectly formated CertificatePolicies";
				}
				break;
			case "2.5.29.35": // AuthorityKeyIdentifier
				try
				{
					this.parsedValue = new AuthorityKeyIdentifier({ schema: asn1.result });
				}
				catch(ex)
				{
					this.parsedValue = new AuthorityKeyIdentifier();
					this.parsedValue.parsingError = "Incorrectly formated AuthorityKeyIdentifier";
				}
				break;
			case "2.5.29.36": // PolicyConstraints
				try
				{
					this.parsedValue = new PolicyConstraints({ schema: asn1.result });
				}
				catch(ex)
				{
					this.parsedValue = new PolicyConstraints();
					this.parsedValue.parsingError = "Incorrectly formated PolicyConstraints";
				}
				break;
			case "2.5.29.37": // ExtKeyUsage
				try
				{
					this.parsedValue = new ExtKeyUsage({ schema: asn1.result });
				}
				catch(ex)
				{
					this.parsedValue = new ExtKeyUsage();
					this.parsedValue.parsingError = "Incorrectly formated ExtKeyUsage";
				}
				break;
			case "2.5.29.54": // InhibitAnyPolicy
				this.parsedValue = asn1.result; // Should be just a simple INTEGER
				break;
			case "1.3.6.1.5.5.7.1.1": // AuthorityInfoAccess
			case "1.3.6.1.5.5.7.1.11": // SubjectInfoAccess
				try
				{
					this.parsedValue = new InfoAccess({ schema: asn1.result });
				}
				catch(ex)
				{
					this.parsedValue = new InfoAccess();
					this.parsedValue.parsingError = "Incorrectly formated InfoAccess";
				}
				break;
			case "1.3.6.1.4.1.11129.2.4.2": // SignedCertificateTimestampList
				try
				{
					this.parsedValue = new SignedCertificateTimestampList({ schema: asn1.result });
				}
				catch(ex)
				{
					this.parsedValue = new SignedCertificateTimestampList();
					this.parsedValue.parsingError = "Incorrectly formated SignedCertificateTimestampList";
				}
				break;
			case "1.3.6.1.4.1.311.20.2": // szOID_ENROLL_CERTTYPE_EXTENSION - Microsoft-specific extension
				this.parsedValue = asn1.result; // Used to be simple Unicode string
				break;
			case "1.3.6.1.4.1.311.21.2": // szOID_CERTSRV_PREVIOUS_CERT_HASH - Microsoft-specific extension
				this.parsedValue = asn1.result; // Used to be simple OctetString
				break;
			case "1.3.6.1.4.1.311.21.7": // szOID_CERTIFICATE_TEMPLATE - Microsoft-specific extension
				try
				{
					this.parsedValue = new CertificateTemplate({ schema: asn1.result });
				}
				catch(ex)
				{
					this.parsedValue = new CertificateTemplate();
					this.parsedValue.parsingError = "Incorrectly formated CertificateTemplate";
				}
				break;
			case "1.3.6.1.4.1.311.21.1": // szOID_CERTSRV_CA_VERSION - Microsoft-specific extension
				try
				{
					this.parsedValue = new CAVersion({ schema: asn1.result });
				}
				catch(ex)
				{
					this.parsedValue = new CAVersion();
					this.parsedValue.parsingError = "Incorrectly formated CAVersion";
				}
				break;
			default:
		}
		//endregion
		//endregion
	}