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

676 lines (597 loc) 26.6 kB
<!DOCTYPE html> <html lang="en" xmlns="http://www.w3.org/1999/xhtml"> <head> <meta charset="utf-8" /> <title>CRL's complex example</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> <script type="text/javascript" src="org/pkijs/ocsp_tsp_schema.js"></script> <script type="text/javascript" src="org/pkijs/ocsp_tsp_simpl.js"></script> <style type="text/css"> body{background:#EFEFEF;font:normal 14px/16px Helvetica, Arial, sans-serif;} .wrapper{ width:600px; margin:50px auto; padding:50px; border:solid 2px #CCC; border-radius:10px; -webkit-border-radius:10px; box-shadow:0 0 12px 3px #CDCDCD; -webkit-box-shadow:0 0 12px 3px #CDCDCD; background:#FFF; } label{ font:bold 16px/20px Helvetica, Arial, sans-serif; margin:0 0 8px; } textarea{ width:500px; border:solid 1px #999; border-radius:5px; -webkit-border-radius:5px; height:340px; font:normal 12px/15px monospace; display:block; margin:0 0 12px; box-shadow:0 0 5px 5px #EFEFEF inset; -webkit-box-shadow:0 0 5px 5px #EFEFEF inset; padding:20px; resize: none; } a{ display:inline-block; padding:5px 15px; background:#ACD0EC; border:solid 1px #4C6181; color:#000; font:normal 14px/16px Helvetica, Arial, sans-serif; } a:hover{ background:#DAEBF8; cursor:pointer; } .header-block { margin-top:30px; font:bold 16px/20px Helvetica, Arial, sans-serif; } .border-block{ border:solid 2px #999; border-radius:5px; -webkit-border-radius:5px; margin:10px 0 0; padding:20px 30px; background:#F0F4FF; } .border-block h2{ margin:0 0 16px; font:bold 22px/24px Helvetica, Arial, sans-serif; } .border-block p{ margin:0 0 12px; } .border-block p .type{ font-weight:bold; display:inline-block; width:176px; } .border-block .two-col{ overflow:hidden; margin:0 0 16px; } .border-block .two-col .subject{ width:180px; font-weight:bold; margin:0 0 12px; float:left; } .border-block .two-col #crl-attributes{ margin:0; padding:0; float:left; list-style:none; } .border-block .two-col #crl-attributes li p{ margin:0; } .border-block .two-col #crl-attributes li p span{ width:40px; display:inline-block; margin:0 0 5px; } .border-block .two-col #crl-exten{ overflow:hidden; padding:0 0 0 17px; margin:0; list-style-type:square; } table { border:solid; border-collapse:collapse; border-color:black; } th { text-align:center; background: #ccc; padding: 5px; border: 1px solid black; } td { padding: 5px; border: 1px solid black; } </style> <script> //********************************************************************************* var crlBuffer = new ArrayBuffer(0); // ArrayBuffer with loaded or created CRL var issuerCertificate = null; var issuerPublicKey = null; //********************************************************************************* // #region Auxiliary functions //********************************************************************************* function formatPEM(pem_string) { /// <summary>Format string in order to have each line with length equal to 63</summary> /// <param name="pem_string" type="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 arrayBufferToString(buffer) { /// <summary>Create a string from ArrayBuffer</summary> /// <param name="buffer" type="ArrayBuffer">ArrayBuffer to create a string from</param> var result_string = ""; var view = new Uint8Array(buffer); for(var i = 0; i < view.length; i++) result_string = result_string + String.fromCharCode(view[i]); return result_string; } //********************************************************************************* function stringToArrayBuffer(str) { /// <summary>Create an ArrayBuffer from string</summary> /// <param name="str" type="String">String to create ArrayBuffer from</param> var stringLength = str.length; var resultBuffer = new ArrayBuffer(stringLength); var resultView = new Uint8Array(resultBuffer); for(var i = 0; i < stringLength; i++) resultView[i] = str.charCodeAt(i); return resultBuffer; } //********************************************************************************* function handleFileBrowse(evt) { var temp_reader = new FileReader(); var current_files = evt.target.files; temp_reader.onload = function(event) { crlBuffer = event.target.result; parse_CRL(); }; temp_reader.readAsArrayBuffer(current_files[0]); } //********************************************************************************* function handleIssuerCert(evt) { var temp_reader = new FileReader(); var current_files = evt.target.files; temp_reader.onload = function(event) { try { issuerPublicKey = null; var asn1 = org.pkijs.fromBER(event.target.result); issuerCertificate = new org.pkijs.simpl.CERT({ schema: asn1.result }); } catch(ex) { } }; temp_reader.readAsArrayBuffer(current_files[0]); } //********************************************************************************* // #endregion //********************************************************************************* // #region Create CRL //********************************************************************************* function create_CRL(buffer) { // #region Initial variables var sequence = Promise.resolve(); var crl_simpl = new org.pkijs.simpl.CRL(); 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 var crl_simpl = new org.pkijs.simpl.CRL(); crl_simpl.version = 1; crl_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" }) })); crl_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" }) })); crl_simpl.thisUpdate = new org.pkijs.simpl.TIME({ type: 0, value: new Date() }); var revokedCertificate = new org.pkijs.simpl.REV_CERT({ userCertificate: new org.pkijs.asn1.INTEGER({ value: 999 }), revocationDate: new org.pkijs.simpl.TIME({ value: new Date() }), crlEntryExtensions: [ new org.pkijs.simpl.EXTENSION({ extnID: "2.5.29.21", // cRLReason extnValue: (new org.pkijs.asn1.ENUMERATED({ value: 1 })).toBER(false) }) ] }); crl_simpl.revokedCertificates = new Array(); crl_simpl.revokedCertificates.push(revokedCertificate); crl_simpl.crlExtensions = [new org.pkijs.simpl.EXTENSION({ extnID: "2.5.29.20", // cRLNumber extnValue: (new org.pkijs.asn1.INTEGER({ value: 2 })).toBER(false) })]; crl_simpl.signatureAlgorithm.algorithm_id = signature_algorithm; crl_simpl.signature.algorithm_id = signature_algorithm; // #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; issuerPublicKey = new org.pkijs.simpl.PUBLIC_KEY_INFO(); issuerPublicKey.importKey(publicKey); }, function(error) { alert("Error during key generation: " + error); } ); // #endregion // #region Signing final CRL sequence = sequence.then( function() { return crl_simpl.sign(privateKey); }, function(error) { alert("Error during exporting public key: " + error); } ); // #endregion // #region Encode and store CRL sequence = sequence.then( function() { crlBuffer = crl_simpl.toSchema(true).toBER(false); var crl_simpl_string = String.fromCharCode.apply(null, new Uint8Array(crlBuffer)); var result_string = "-----BEGIN X509 CRL-----\r\n"; result_string = result_string + formatPEM(window.btoa(crl_simpl_string)); result_string = result_string + "\r\n-----END X509 CRL-----\r\n"; document.getElementById("new_signed_data").innerHTML = result_string; parse_CRL(); alert("CRL 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 } //********************************************************************************* // #endregion //********************************************************************************* // #region Parse existing CRL //********************************************************************************* function parse_CRL() { // #region Initial check if(crlBuffer.byteLength === 0) { alert("Nothing to parse!"); return; } // #endregion // #region Initial activities document.getElementById("crl-extn-div").style.display = "none"; var revokedTable = document.getElementById("crl-rev-certs"); while(revokedTable.rows.length > 1) { revokedTable.deleteRow(revokedTable.rows.length - 1); } var extensionTable = document.getElementById("crl-extn-table"); while(extensionTable.rows.length > 1) { extensionTable.deleteRow(extensionTable.rows.length - 1); } var issuerTable = document.getElementById("crl-issuer-table"); while(issuerTable.rows.length > 1) { issuerTable.deleteRow(issuerTable.rows.length - 1); } // #endregion // #region Decode existing CRL var asn1 = org.pkijs.fromBER(crlBuffer); var crl_simpl = new org.pkijs.simpl.CRL({ schema: asn1.result }); // #endregion // #region Put information about CRL issuer var 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" }; for(var i = 0; i < crl_simpl.issuer.types_and_values.length; i++) { var typeval = rdnmap[crl_simpl.issuer.types_and_values[i].type]; if(typeof typeval === "undefined") typeval = crl_simpl.issuer.types_and_values[i].type; var subjval = crl_simpl.issuer.types_and_values[i].value.value_block.value; var row = issuerTable.insertRow(issuerTable.rows.length); var cell0 = row.insertCell(0); cell0.innerHTML = typeval; var cell1 = row.insertCell(1); cell1.innerHTML = subjval; } // #endregion // #region Put information about issuance date document.getElementById("crl-this-update").innerHTML = crl_simpl.thisUpdate.value.toString(); // #endregion // #region Put information about expiration date if("nextUpdate" in crl_simpl) { document.getElementById("crl-next-update").innerHTML = crl_simpl.nextUpdate.value.toString(); document.getElementById("crl-next-update-div").style.display = "block"; } // #endregion // #region Put information about signature algorithm var algomap = { "1.2.840.113549.2.1": "MD2", "1.2.840.113549.1.1.2": "MD2 with RSA", "1.2.840.113549.2.5": "MD5", "1.2.840.113549.1.1.4": "MD5 with RSA", "1.3.14.3.2.26": "SHA1", "1.2.840.10040.4.3": "SHA1 with DSA", "1.2.840.10045.4.1": "SHA1 with ECDSA", "1.2.840.113549.1.1.5": "SHA1 with RSA", "2.16.840.1.101.3.4.2.4": "SHA224", "1.2.840.113549.1.1.14": "SHA224 with RSA", "2.16.840.1.101.3.4.2.1": "SHA256", "1.2.840.113549.1.1.11": "SHA256 with RSA", "2.16.840.1.101.3.4.2.2": "SHA384", "1.2.840.113549.1.1.12": "SHA384 with RSA", "2.16.840.1.101.3.4.2.3": "SHA512", "1.2.840.113549.1.1.13": "SHA512 with RSA" }; // array mapping of common algorithm OIDs and corresponding types var signatureAlgorithm = algomap[crl_simpl.signature.algorithm_id]; if(typeof signatureAlgorithm === "undefined") signatureAlgorithm = crl_simpl.signature.algorithm_id; else signatureAlgorithm = signatureAlgorithm + " (" + crl_simpl.signature.algorithm_id + ")"; document.getElementById("crl-sign-algo").innerHTML = signatureAlgorithm; // #endregion // #region Put information about revoked certificates if("revokedCertificates" in crl_simpl) { for(var i = 0; i < crl_simpl.revokedCertificates.length; i++) { var row = revokedTable.insertRow(revokedTable.rows.length); var cell0 = row.insertCell(0); cell0.innerHTML = org.pkijs.bufferToHexCodes(crl_simpl.revokedCertificates[i].userCertificate.value_block.value_hex); var cell1 = row.insertCell(1); cell1.innerHTML = crl_simpl.revokedCertificates[i].revocationDate.value.toString(); } document.getElementById("crl-rev-certs-div").style.display = "block"; } // #endregion // #region Put information about CRL extensions if("crlExtensions" in crl_simpl) { for(var i = 0; i < crl_simpl.crlExtensions.length; i++) { var row = extensionTable.insertRow(extensionTable.rows.length); var cell0 = row.insertCell(0); cell0.innerHTML = crl_simpl.crlExtensions[i].extnID; } document.getElementById("crl-extn-div").style.display = "block"; } // #endregion } //********************************************************************************* // #endregion //********************************************************************************* // #region Verify existing CRL //********************************************************************************* function verify_CRL() { // #region Initial check if(crlBuffer.byteLength === 0) { alert("Nothing to verify!"); return; } if((issuerCertificate === null) && (issuerPublicKey === null)) { alert("Please load CRL's issuer certificate!"); return; } // #endregion // #region Decode existing CRL var asn1 = org.pkijs.fromBER(crlBuffer); var crl_simpl = new org.pkijs.simpl.CRL({ schema: asn1.result }); // #endregion // #region Verify CRL var verifyObject = {}; if(issuerCertificate !== null) verifyObject.issuerCertificate = issuerCertificate; if(issuerPublicKey !== null) verifyObject.publicKeyInfo = issuerPublicKey; crl_simpl.verify(verifyObject). then( function(result) { alert("Verification result: " + result); }, function(error) { alert("Error during verification: " + error); } ); // #endregion } //********************************************************************************* // #endregion //********************************************************************************* </script> </head> <body> <div class="wrapper"> <p class="header-block">Create new CRL</p> <div id="output_div" class="border-block"> <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;float:left;">BASE-64 encoded new certificate + PKCS#8 private key:</label> <textarea id="new_signed_data">&lt; New encoded certificate + PKCS#8 exported private key will be stored here &gt;</textarea> <a onclick="create_CRL();">Create</a> </div> <p class="header-block">Parse loaded/created CRL</p> <div id="crl-data-block" class="border-block"> <p> <label for="crl-file">Select binary CRL file:</label> <input type="file" id="crl-file" title="CRL" /> </p> <div id="crl-issuer-div" class="two-col"> <p class="subject">Issuer:</p> <table id="crl-issuer-table"><tr><th>OID</th><th>Value</th></tr></table> </div> <p><span class="type">This update:</span> <span id="crl-this-update"></span></p> <p id="crl-next-update-div" style="display:none;"><span class="type">Next update:</span> <span id="crl-next-update"></span></p> <p><span class="type">Signature algorithm:</span> <span id="crl-sign-algo"></span></p> <div id="crl-rev-certs-div" class="two-col"> <p class="subject">Revoked certificates:</p> <table id="crl-rev-certs"><tr><th>Serial number</th><th>Rev. time</th></tr></table> </div> <div id="crl-extn-div" class="two-col" style="display:none;"> <p class="subject">Extensions:</p> <table id="crl-extn-table"><tr><th>OID</th></tr></table> </div> </div> <p class="header-block">Verify loaded/created CRL</p> <div id="add-crl-block" class="border-block"> <p> <label for="issuer_cert">Load issuer's certificate (binary):</label> <input type="file" id="issuer_cert" title="Load issuer's cert" /> </p> <a onclick="verify_CRL();">Verify</a> </div> </div> <script> document.getElementById('crl-file').addEventListener('change', handleFileBrowse, false); document.getElementById('issuer_cert').addEventListener('change', handleIssuerCert, false); </script> </body> </html>