UNPKG

pkijs

Version:

Public Key Infrastructure (PKI) is the basis of how identity and key management is performed on the web today. PKIjs is a pure JavaScript library implementing the formats that are used in PKI applications. It is built on WebCrypto and aspires to make it p

482 lines (414 loc) 19.5 kB
<!DOCTYPE html> <html lang="en" xmlns="http://www.w3.org/1999/xhtml"> <head> <meta charset="utf-8" /> <title>How to create new CMS Signed data</title> <script type="text/javascript" src="org/pkijs/common.js"></script> <script type="text/javascript" src="org/pkijs/asn1.js"></script> <script type="text/javascript" src="org/pkijs/x509_schema.js"></script> <script type="text/javascript" src="org/pkijs/x509_simpl.js"></script> <script type="text/javascript" src="org/pkijs/cms_schema.js"></script> <script type="text/javascript" src="org/pkijs/cms_simpl.js"></script> <style type="text/css"> * { -moz-box-sizing: border-box; -webkit-box-sizing: border-box; box-sizing: border-box; overflow:auto; } html,body { width:100%; max-width:100%; margin:0; padding:0; border-style:none; } body * { font-family:"Trebuchet MS", Arial, Helvetica, sans-serif; } textarea { width:100%; height:300px; resize:none; } div#output_div { position:relative; display:block; left:50%; width:700px; margin-left:-350px; margin-top:20px; margin-bottom:10px; } </style> <script type="text/javascript"> //********************************************************************************* function formatPEM(pem_string) { /// <summary>Format string in order to have each line with length equal to 63</summary> /// <param name="pem_string">String to format</param> var string_length = pem_string.length; var result_string = ""; for(var i = 0, count = 0; i < string_length; i++, count++) { if(count > 63) { result_string = result_string + "\r\n"; count = 0; } result_string = result_string + pem_string[i]; } return result_string; } //********************************************************************************* function create_new_signed_data(buffer) { // #region Initial variables var sequence = Promise.resolve(); var cert_simpl = new org.pkijs.simpl.CERT(); var cms_signed_simpl; var publicKey; var privateKey; var hash_algorithm = "sha-1"; var hash_algorithm_oid = "1.3.14.3.2.26"; var signature_algorithm = "1.2.840.113549.1.1.5"; var hash_option = document.getElementById("hash_alg").value; switch(hash_option) { case "alg_SHA1": hash_algorithm = "sha-1"; hash_algorithm_oid = "1.3.14.3.2.26"; signature_algorithm = "1.2.840.113549.1.1.5"; break; case "alg_SHA256": hash_algorithm = "sha-256"; hash_algorithm_oid = "2.16.840.1.101.3.4.2.1"; signature_algorithm = "1.2.840.113549.1.1.11"; break; case "alg_SHA384": hash_algorithm = "sha-384"; hash_algorithm_oid = "2.16.840.1.101.3.4.2.2"; signature_algorithm = "1.2.840.113549.1.1.12"; break; case "alg_SHA512": hash_algorithm = "sha-512"; hash_algorithm_oid = "2.16.840.1.101.3.4.2.3"; signature_algorithm = "1.2.840.113549.1.1.13"; break; default:; } // #endregion // #region Get a "crypto" extension var crypto = org.pkijs.getCrypto(); if(typeof crypto == "undefined") { alert("No WebCrypto extension found"); return; } // #endregion // #region Put a static values cert_simpl.version = 2; cert_simpl.serialNumber = new org.pkijs.asn1.INTEGER({ value: 1 }); cert_simpl.issuer.types_and_values.push(new org.pkijs.simpl.ATTR_TYPE_AND_VALUE({ type: "2.5.4.6", // Country name value: new org.pkijs.asn1.PRINTABLESTRING({ value: "RU" }) })); cert_simpl.issuer.types_and_values.push(new org.pkijs.simpl.ATTR_TYPE_AND_VALUE({ type: "2.5.4.3", // Common name value: new org.pkijs.asn1.BMPSTRING({ value: "Test" }) })); cert_simpl.subject.types_and_values.push(new org.pkijs.simpl.ATTR_TYPE_AND_VALUE({ type: "2.5.4.6", // Country name value: new org.pkijs.asn1.PRINTABLESTRING({ value: "RU" }) })); cert_simpl.subject.types_and_values.push(new org.pkijs.simpl.ATTR_TYPE_AND_VALUE({ type: "2.5.4.3", // Common name value: new org.pkijs.asn1.BMPSTRING({ value: "Test" }) })); cert_simpl.notBefore.value = new Date(2013, 01, 01); cert_simpl.notAfter.value = new Date(2016, 01, 01); cert_simpl.extensions = new Array(); // Extensions are not a part of certificate by default, it's an optional array // #region "BasicConstraints" extension var basic_constr = new org.pkijs.simpl.x509.BasicConstraints({ cA: true, pathLenConstraint: 3 }); cert_simpl.extensions.push(new org.pkijs.simpl.EXTENSION({ extnID: "2.5.29.19", critical: false, extnValue: basic_constr.toSchema().toBER(false), parsedValue: basic_constr // Parsed value for well-known extensions })); // #endregion // #region "KeyUsage" extension var bit_array = new ArrayBuffer(1); var bit_view = new Uint8Array(bit_array); bit_view[0] = bit_view[0] | 0x02; // Key usage "cRLSign" flag bit_view[0] = bit_view[0] | 0x04; // Key usage "keyCertSign" flag var key_usage = new org.pkijs.asn1.BITSTRING({ value_hex: bit_array }); cert_simpl.extensions.push(new org.pkijs.simpl.EXTENSION({ extnID: "2.5.29.15", critical: false, extnValue: key_usage.toBER(false), parsedValue: key_usage // Parsed value for well-known extensions })); // #endregion cert_simpl.signatureAlgorithm.algorithm_id = signature_algorithm; cert_simpl.signature.algorithm_id = cert_simpl.signatureAlgorithm.algorithm_id; // Must be the same value // #endregion // #region Create a new key pair sequence = sequence.then( function() { return crypto.generateKey({ name: "RSASSA-PKCS1-v1_5", modulusLength: 2048, publicExponent: new Uint8Array([0x01, 0x00, 0x01]), hash: { name: hash_algorithm } }, true, ["sign", "verify"]); } ); // #endregion // #region Store new key in an interim variables sequence = sequence.then( function(keyPair) { publicKey = keyPair.publicKey; privateKey = keyPair.privateKey; }, function(error) { alert("Error during key generation: " + error); } ); // #endregion // #region Exporting public key into "subjectPublicKeyInfo" value of certificate sequence = sequence.then( function() { return cert_simpl.subjectPublicKeyInfo.importKey(publicKey); } ); // #endregion // #region Signing final certificate sequence = sequence.then( function() { return cert_simpl.sign(privateKey); }, function(error) { alert("Error during exporting public key: " + error); } ); // #endregion // #region Encode and store certificate sequence = sequence.then( function() { var cert_simpl_encoded = cert_simpl.toSchema(true).toBER(false); var cert_simpl_string = String.fromCharCode.apply(null, new Uint8Array(cert_simpl_encoded)); var result_string = "-----BEGIN CERTIFICATE-----\r\n"; result_string = result_string + formatPEM(window.btoa(cert_simpl_string)); result_string = result_string + "\r\n-----END CERTIFICATE-----\r\n"; document.getElementById("new_signed_data").innerHTML = result_string; alert("Certificate created successfully!"); }, function(error) { alert("Error during signing: " + error); } ); // #endregion // #region Exporting private key sequence = sequence.then( function() { return crypto.exportKey("pkcs8", privateKey); } ); // #endregion // #region Store exported key on Web page sequence = sequence.then( function(result) { var private_key_string = String.fromCharCode.apply(null, new Uint8Array(result)); var result_string = document.getElementById("new_signed_data").innerHTML; result_string = result_string + "\r\n-----BEGIN PRIVATE KEY-----\r\n"; result_string = result_string + formatPEM(window.btoa(private_key_string)); result_string = result_string + "\r\n-----END PRIVATE KEY-----\r\n"; document.getElementById("new_signed_data").innerHTML = result_string; alert("Private key exported successfully!"); }, function(error) { alert("Error during exporting of private key: " + error); } ); // #endregion if(document.getElementById("add_ext").checked) { // #region Create a message digest sequence = sequence.then( function() { return crypto.digest({ name: hash_algorithm }, new Uint8Array(buffer)); } ); // #endregion // #region Combine all signed extensions sequence = sequence.then( function(result) { var signed_attr = new Array(); signed_attr.push(new org.pkijs.simpl.cms.Attribute({ attrType: "1.2.840.113549.1.9.3", attrValues: [ new org.pkijs.asn1.OID({ value: "1.2.840.113549.1.7.1" }) ] })); // contentType signed_attr.push(new org.pkijs.simpl.cms.Attribute({ attrType: "1.2.840.113549.1.9.5", attrValues: [ new org.pkijs.asn1.UTCTIME({ value_date: new Date() }) ] })); // signingTime signed_attr.push(new org.pkijs.simpl.cms.Attribute({ attrType: "1.2.840.113549.1.9.4", attrValues: [ new org.pkijs.asn1.OCTETSTRING({ value_hex: result }) ] })); // messageDigest return signed_attr; } ); // #endregion } // #region Initialize CMS Signed Data structures and sign it sequence = sequence.then( function(result) { cms_signed_simpl = new org.pkijs.simpl.CMS_SIGNED_DATA({ version: 1, digestAlgorithms: [ new org.pkijs.simpl.ALGORITHM_IDENTIFIER({ algorithm_id: hash_algorithm_oid, algorithm_params: new org.pkijs.asn1.NULL() }) // SHA-1 ], encapContentInfo: new org.pkijs.simpl.cms.EncapsulatedContentInfo({ eContentType: "1.2.840.113549.1.7.1", // "data" content type eContent: new org.pkijs.asn1.OCTETSTRING({ value_hex: buffer }) }), signerInfos: [ new org.pkijs.simpl.CMS_SIGNER_INFO({ version: 1, sid: new org.pkijs.simpl.cms.IssuerAndSerialNumber({ issuer: cert_simpl.issuer, serialNumber: cert_simpl.serialNumber }), digestAlgorithm: new org.pkijs.simpl.ALGORITHM_IDENTIFIER({ algorithm_id: hash_algorithm_oid, algorithm_params: new org.pkijs.asn1.NULL() }), // SHA-1 signatureAlgorithm: new org.pkijs.simpl.ALGORITHM_IDENTIFIER({ algorithm_id: "1.2.840.113549.1.1.1", algorithm_params: new org.pkijs.asn1.NULL() }), // RSA (PKCS #1 v1.5) key transport algorithm }) ], certificates: [cert_simpl] }); if(document.getElementById("add_ext").checked) { cms_signed_simpl.signerInfos[0].signedAttrs = new org.pkijs.simpl.cms.SignedUnsignedAttributes({ type: 0, attributes: result }); } return cms_signed_simpl.sign(privateKey, 0); } ); // #endregion sequence.then( function(result) { var cms_signed_schema = cms_signed_simpl.toSchema(true); var cms_content_simp = new org.pkijs.simpl.CMS_CONTENT_INFO({ contentType: "1.2.840.113549.1.7.2", content: cms_signed_schema }); var cms_signed_schema = cms_content_simp.toSchema(true); // #region Make length of some elements in "indefinite form" cms_signed_schema.len_block.is_indefinite_form = true; var block1 = cms_signed_schema.value_block.value[1]; block1.len_block.is_indefinite_form = true; var block2 = block1.value_block.value[0]; block2.len_block.is_indefinite_form = true; var block3 = block2.value_block.value[2]; block3.len_block.is_indefinite_form = true; block3.value_block.value[1].len_block.is_indefinite_form = true; block3.value_block.value[1].value_block.value[0].len_block.is_indefinite_form = true; // #endregion var cms_signed_encoded = cms_signed_schema.toBER(false); // #region Convert ArrayBuffer to String var signed_data_string = ""; var view = new Uint8Array(cms_signed_encoded); for(var i = 0; i < view.length; i++) signed_data_string = signed_data_string + String.fromCharCode(view[i]); // #endregion var result_string = document.getElementById("new_signed_data").innerHTML; result_string = result_string + "\r\n-----BEGIN CMS-----\r\n"; result_string = result_string + formatPEM(window.btoa(signed_data_string)); result_string = result_string + "\r\n-----END CMS-----\r\n\r\n"; document.getElementById("new_signed_data").innerHTML = result_string; alert("CMS Signed Data created successfully!"); }, function(error) { alert("Erorr during signing of CMS Signed Data: " + error); } ); } //********************************************************************************* function handleFileBrowse(evt) { var temp_reader = new FileReader(); var current_files = evt.target.files; temp_reader.onload = function(event) { create_new_signed_data(event.target.result); }; temp_reader.readAsArrayBuffer(current_files[0]); } //********************************************************************************* </script> </head> <body"> <div id="output_div"> <p> <label for="input_file" style="font-weight:bold">Select a file to make signature for:</label> <input type="file" id="input_file" title="Input file" /> </p> <p> <label for="add_ext" style="font-weight:bold">Add signed extensions in SignerInfo:</label> <input type="checkbox" id="add_ext" value="0" /> </p> <p> <label for="hash_alg" style="font-weight:bold">Hashing algorithm:</label> <select id="hash_alg"> <option value="alg_SHA1">SHA-1</option> <option value="alg_SHA256">SHA-256</option> <option value="alg_SHA384">SHA-384</option> <option value="alg_SHA512">SHA-512</option> </select> </p> <label for="new_signed_data" style="font-weight:bold">CMS Signed Data + BASE-64 encoded new certificate + PKCS#8 private key:</label> <textarea id="new_signed_data">&lt; New CSM signed data + new encoded certificate + PKCS#8 exported private key will be stored here &gt;</textarea> </div> <script> document.getElementById('input_file').addEventListener('change', handleFileBrowse, false); </script> </body> </html>