PKI Agent | Demo

This demo illustrates the use of PKI Agent for signing data in the browser using system certificates.

The JavaScript code used in the example above can be found below in its entirety. This is intended only as an example to demonstrate how to communicate with PKI Agent from within a browser and should not be used in production.

const SIGNING_SERVICE_PORT = 19266; initPage(); function initPage() { document.getElementById("portnum").innerHTML = SIGNING_SERVICE_PORT; document.getElementById("pki-agent-sign-demo").addEventListener("submit", function (evt) { evt.preventDefault(); sign(); }); } function arrayIsEmpty(value) { return (!value || value.length === 0); } function stringIsEmpty(value) { return (!value || value.length === 0); } function randomPin() { return Math.random().toString(36).slice(2, 6).toUpperCase(); } function toHexString(value) { if (arrayIsEmpty(value)) { return ''; } else { return [...new Uint8Array(value)] .map(x => x.toString(16).padStart(2, '0')) .join(''); } } function toByteArray(value) { let utf8Encode = new TextEncoder(); return utf8Encode.encode(value); } async function sign() { try { var pin = randomPin(); var pintr = document.getElementById("pin-info"); pintr.innerHTML = "Your one-time authorization code is
" + pin + ""; pintr.className = pintr.className.replace(/\bd-none\b/, ""); // Preparing the authorization request var http = new XMLHttpRequest(); var url = "http://127.0.0.1:" + SIGNING_SERVICE_PORT + "/authorize"; // preparing authorization prompt - expected by the server var prompt = "pin=" + pin + "&origin=PKI%20Agent%20Demo"; // forming the signature string var sig = "pkpp3:user:12345678:01:" + toHexString(toByteArray(prompt)); // encoding the signature in base64 and appending to the URL url = url + "?sig=" + btoa(sig); // setting the timeout to 40 seconds - longer than the notification wait time http.timeout = 40000; // opening the GET request http.open('GET', url, true); http.ontimeout = function () { showToast("Timeout - is PKI Agent running?"); }; // Response handler http.onreadystatechange = function () { if (http.readyState == 4 && http.status == 200) { // processing the response const resp = JSON.parse(http.responseText); const objs = resp.objects; // extracting the data we need: the auth token, the key id and label, // and the key algorithm var token = resp.cred; var keyid = ""; var label = ""; var mech = ""; var sigMech = ""; for (var i = 0; i < objs.length; i++) { if (objs[i].class == "privatekey") { keyid = objs[i].id; label = objs[i].label; mech = objs[i].mech; if (mech == "rsa") { sigMech = "&mech=sha256WithRSAEncryption"; } else if ((mech == "ec") || (mech == "ecdsa")) { sigMech = "&mech=ecdsa"; } break; } } if (keyid == "") return; // no key, can't proceed with signing // Sending the second request to the signing endpoint http = new XMLHttpRequest(); url = "http://127.0.0.1:" + SIGNING_SERVICE_PORT + "/sign?key=" + keyid + sigMech; // forming the signature string sig = "pkpt3:user:12345678:01:" + token; // encoding the signature in base64 and appending to the URL url = url + "&sig=" + btoa(sig); // opening the GET request http.open('POST', url, true); http.setRequestHeader("Content-Type", "application/octet-stream"); // We want the response as ArrayBuffer http.responseType = "arraybuffer"; // Converting the string to be signed to a byte array. var buf = (new TextEncoder()).encode(document.getElementById('data').value); // Response handler http.onreadystatechange = function () { if (http.readyState == 4 && http.status == 200) { // The signature comes back as a binary byte array, so we need to hex-encode it before displaying. var res = toHexString(http.response); // Updating the page element. var respElem = document.getElementById("signature"); respElem.innerHTML = res; pintr.innerHTML = "Signing succeeded!"; showToast("Signing succeeded"); } else if ((http.readyState == 4) && ((http.status == 401) || (http.status == 400))) { pintr.innerHTML = "Signing authorization expired"; showToast("Signing error: Unauthorized."); } } // Sending the POST http.send(buf); } else if ((http.readyState == 4) && ((http.status == 401) || (http.status == 400))) { pintr.innerHTML = "Signing request rejected"; showToast("Failed to obtain authorization for signing!"); } else { ; } } // Sending the GET http.send(); } catch (err) { showToast("Error: " + err + "\r\n\r\n (Have you provided the correct credentials? Is the sharing service running?)"); } } function showToast(msg) { document.getElementById("toast-body").innerHTML = msg; var toast = document.getElementById("toast-info"); toast.className = toast.className.replace(/\bhide\b/, "") toast.className += " fade show"; setTimeout(function () { toast.className = toast.className.replace(/\bfade show\b/, "hide"); }, 5000); }