PortalPersonalizer.js
Summary
Personalization Client forthe PKI-as-a-Service Portal
var SmartCardHSM = require('scsh/sc-hsm/SmartCardHSM').SmartCardHSM;
var CVC = require("scsh/eac/CVC").CVC;
var DICATask = require("DICATask").DICATask;
function PortalPersonalizer(job) {
this.job = job;
}
exports.PortalPersonalizer = PortalPersonalizer;
PortalPersonalizer.prototype.configure = function() {
this.parseSubjectURL();
this.login();
if (typeof(this.job.dicaId) != "undefined") {
this.dicaId = this.job.dicaId;
} else {
this.getDICA();
}
if (Dialog.prompt("Connect DICA Token to Portal ?")) {
this.attachDICA();
}
}
PortalPersonalizer.prototype.parseSubjectURL = function(url) {
var url = "https://www.pki-as-a-service.net/se/paas/subject/<id>";
var subjectPath = "/se/paas/subject/";
var prompt = true;
if (typeof(this.job.url) != "undefined") {
url = this.job.url;
}
while (true) {
var pathIdx = url.indexOf(subjectPath);
if (pathIdx < 0) {
assert(Dialog.prompt("URL must contain " + subjectPath));
} else {
var id = parseInt(url.substring(pathIdx + subjectPath.length));
if (isNaN(id)) {
assert(Dialog.prompt("URL must contain a subject id after " + subjectPath));
} else {
break;
}
}
url = Dialog.prompt("Enter the URL of the Device Issuer", url);
assert(url, "User abort");
}
this.host = url.substring(0, pathIdx);
this.subjectId = id;
}
PortalPersonalizer.prototype.attachDICA = function() {
var readerList = Card.getReaderList();
var lastreader = _scsh3.DICAToken;
if (!lastreader) {
lastreader = _scsh3.reader;
}
while (true) {
var reader = Dialog.prompt("Please select reader with DICA token", lastreader, readerList);
assert(reader, "User abort");
_scsh3.setProperty("DICAToken", reader);
try {
var card = new Card(reader);
var sc = new SmartCardHSM(card);
sc.verifyUserPIN();
break;
}
catch(e) {
print(e);
}
}
var url = this.host + "/rt/hsm";
this.dicatask = new DICATask(card, url);
this.dicatask.start();
}
PortalPersonalizer.prototype.login = function() {
default xml namespace = "http://www.w3.org/1999/xhtml";
var baseURL = this.host + "/se";
var c = new URLConnection(baseURL);
var result = c.get();
var cookie = c.getHeaderField("Set-Cookie");
this.session = cookie[0].substr(0, cookie[0].indexOf(";"));
c.addHeaderField("Cookie", this.session);
var result = c.get();
var html = result.match(/<html xmlns="http:\/\/www.w3.org\/1999\/xhtml" lang="en" xml:lang="en">[\s\S]*<\/html>/);
var page = new XML(html);
var content = page..div.(@id == "content");
var img = content..img;
var remoteStr = img.@src.toXMLString();
var queryStr = remoteStr.substr(remoteStr.indexOf("?") + 1);
var params = queryStr.split("&");
var conInfo = {};
for (var i = 0; i < params.length; i++) {
var param = params[i];
var res = param.split("=");
conInfo[res[0]] = res[1];
}
var readerList = Card.getReaderList();
var lastreader = _scsh3.portalReader;
if (!lastreader) {
lastreader = _scsh3.reader;
}
var reader = Dialog.prompt("Please select reader with token to log in", lastreader, readerList);
assert(reader, "User abort");
_scsh3.setProperty("portalReader", reader);
var card = new Card(reader);
if (conInfo.pinrequired == 1) {
GPSystem.trace("Authenticate User");
var sc = new SmartCardHSM(card);
sc.verifyUserPIN();
}
GPSystem.trace("Connect Card");
card.remoteUpdate(conInfo.url, conInfo.sessionId);
var sel = Dialog.prompt("Please remove the token to prevent accidently overwriting it");
assert(sel, "User abort");
}
PortalPersonalizer.prototype.getDICA = function() {
var dicaURL = this.host + "/se/paas/subject-rest/" + this.subjectId + "/dica";
var c = new URLConnection(dicaURL);
c.addHeaderField("Cookie", this.session);
var result = c.get();
GPSystem.trace(result);
var deviceIssuer = JSON.parse(result);
var labels = [];
for (var i = 0; i < deviceIssuer.dica.length; i++) {
labels.push(deviceIssuer.dica[i].label);
}
var dicaLabel = Dialog.prompt("Select DICA", deviceIssuer.dica[0].label, labels);
for (var i = 0; i < deviceIssuer.dica.length; i++) {
var dica = deviceIssuer.dica[i];
if (dica.label == dicaLabel) {
this.dicaId = dica.id;
print("++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++");
print("You have selected the dica " + dicaLabel + " of Device Issuer " + deviceIssuer.name + ".");
print("Update your jobs.js configuration to the following to remember this decision for future use.");
print("");
print("PortalPersonalizer: {");
print("\turl: \"" + this.host + "/se/paas/subject/" + this.subjectId + "\",");
print("\tdicaId: " + this.dicaId);
print("}");
print("++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++");
}
}
}
PortalPersonalizer.prototype.createServiceRequest = function() {
var srURL = this.host + "/se/paas/sr-rest/new";
srURL += "?process=SmartCardHSMPersonalization";
srURL += "&ca=" + this.subjectId;
srURL += "&issuer=" + this.dicaId;
if (typeof(this.job.ccr) != "undefined") {
srURL += "&ccr=" + this.job.ccr;
}
var c = new URLConnection(srURL);
c.addHeaderField("Cookie", this.session);
var result = c.post("");
if (c.responseCode != 201) {
throw new GPError(module.id, GPError.OBJECTCREATIONFAILED, 0, "Failed to create personalization service request (" + c.responseCode + ")");
}
var authToken = JSON.parse(result);
return authToken;
}
PortalPersonalizer.prototype.handleCard = function(card) {
var authToken = this.createServiceRequest();
var remoteUpdateURL = this.host + "/rt/paas?" + authToken;
GPSystem.trace("Connect card to service request");
card.remoteUpdate(remoteUpdateURL);
if (card.remoteMessageId != 0) {
throw new GPError(module.id, GPError.OBJECTCREATIONFAILED, 0, "Personalization service request failed: " + card.remoteMessage);
}
GPSystem.wait(1000);
var inp = new ByteString(authToken, HEX);
var mac = inp.right(8);
var inp = inp.bytes(0, inp.length - mac.length);
var id = inp.toUnsigned();
var srURL = this.host + "/se/paas/sr-rest/" + id;
var c = new URLConnection(srURL);
c.addHeaderField("Cookie", this.session);
var result = c.get();
var sr = JSON.parse(result);
if (sr.lifeCycle != 19) {
throw new GPError(module.id, GPError.OBJECTCREATIONFAILED, 0, "Personalization service request failed: " + sr.statusInfo);
}
return this.testCard(card);
}
PortalPersonalizer.prototype.testCard = function(card) {
card.reset(Card.RESET_COLD);
var id = card.sendApdu(0x00, 0xA4, 0x04, 0x00, new ByteString("A000000167413000FF", HEX), [0x6A82,0x6A86]);
var sc = new SmartCardHSM(card);
var devAutCert = sc.readBinary(new ByteString("2F02", HEX));
var cvc = new CVC(devAutCert);
GPSystem.trace("Device Certificate : " + cvc);
var crypto = new Crypto();
var dica = new CVC(devAutCert.bytes(cvc.getASN1().size));
GPSystem.trace("Device Issuer CA : " + dica);
var srca = SmartCardHSM.rootCerts[dica.getCAR()];
GPSystem.trace("SmartCard-HSM Root CA : " + srca);
if (!srca) {
var certbin = new ByteString("7F218201B27F4E82016A5F290100420C5858535243413130303030317F4982011D060A04007F000702020202038120A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E537782207D5A0975FC2C3057EEF67530417AFFE7FB8055C126DC5C6CE94A4B44F330B5D9832026DC5C6CE94A4B44F330B5D9BBD77CBF958416295CF7E1CE6BCCDC18FF8C07B68441048BD2AEB9CB7E57CB2C4B482FFC81B7AFB9DE27E1E3BD23C23A4453BD9ACE3262547EF835C3DAC4FD97F8461A14611DC9C27745132DED8E545C1D54C72F0469978520A9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A78641040E456F4C06B7D27B12CC7A4C7C75E0E6C0B32EA7066177D75D8E220F68B47FDB709819A1B82354508A996C023412A374472F4C050958E1585A88430B108FA17B8701015F200C5858535243413130303030317F4C10060B2B0601040181C31F0301015301C05F25060200000502065F240604000005020565005F3740522575F269042788FAB77C6364D229705F607F7768BD008BBBEA74654E754E6747D26EA6D2996BD4AE2D6EACFB7BBDD44F78187545AA96D25606B0DA3C9205FE", HEX);
var srca = new CVC(certbin);
SmartCardHSM.rootCerts.XXSRCA100001 = srca;
}
var srcapuk = srca.getPublicKey();
var oid = srca.getPublicKeyOID();
assert(dica.verifyWith(crypto, srcapuk, oid), "DICA certificate not verified");
var dicapuk = dica.getPublicKey(srcapuk);
assert(cvc.verifyWith(crypto, dicapuk, oid), "Device certificate verification failed");
var devicepuk = cvc.getPublicKey(srcapuk);
sc.openSecureChannel(crypto, devicepuk);
if ((id.length > 0) && (id.byteAt(14) == 0x00)) {
print("\nWARNING: Device is ***not*** fused. For internal use only.\n");
}
return "OK " + cvc.getCHR().toString();
}
Documentation generated by
JSDoc on Sat Feb 24 15:17:19 2024