PKI Agent | Demo
This demo illustrates the use of PKI Agent for signing data in the browser using system certificates.
The button is not working? Check that PKI Agent is up and running on port .
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);
}