ESM: Ported PublicKey operations

This commit is contained in:
Matt C 2018-05-27 23:53:43 +01:00
parent 709630f39b
commit 049656ec6b
7 changed files with 518 additions and 0 deletions

View File

@ -0,0 +1,72 @@
/**
* Public key functions.
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import { toHex, fromHex } from "./Hex";
/**
* Formats Distinguished Name (DN) strings.
*
* @param {string} dnStr
* @param {number} indent
* @returns {string}
*/
export function formatDnStr (dnStr, indent) {
const fields = dnStr.substr(1).replace(/([^\\])\//g, "$1$1/").split(/[^\\]\//);
let output = "",
maxKeyLen = 0,
key,
value,
i,
str;
for (i = 0; i < fields.length; i++) {
if (!fields[i].length) continue;
key = fields[i].split("=")[0];
maxKeyLen = key.length > maxKeyLen ? key.length : maxKeyLen;
}
for (i = 0; i < fields.length; i++) {
if (!fields[i].length) continue;
key = fields[i].split("=")[0];
value = fields[i].split("=")[1];
str = key.padEnd(maxKeyLen, " ") + " = " + value + "\n";
output += str.padStart(indent + str.length, " ");
}
return output.slice(0, -1);
}
/**
* Formats byte strings by adding line breaks and delimiters.
*
* @param {string} byteStr
* @param {number} length - Line width
* @param {number} indent
* @returns {string}
*/
export function formatByteStr (byteStr, length, indent) {
byteStr = toHex(fromHex(byteStr), ":");
length = length * 3;
let output = "";
for (let i = 0; i < byteStr.length; i += length) {
const str = byteStr.slice(i, i + length) + "\n";
if (i === 0) {
output += str;
} else {
output += str.padStart(indent + str.length, " ");
}
}
return output.slice(0, output.length-1);
}

View File

@ -0,0 +1,40 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import r from "jsrsasign";
import Operation from "../Operation";
/**
* Hex to Object Identifier operation
*/
class HexToObjectIdentifier extends Operation {
/**
* HexToObjectIdentifier constructor
*/
constructor() {
super();
this.name = "Hex to Object Identifier";
this.module = "PublicKey";
this.description = "Converts a hexadecimal string into an object identifier (OID).";
this.inputType = "string";
this.outputType = "string";
this.args = [];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
return r.KJUR.asn1.ASN1Util.oidHexToInt(input.replace(/\s/g, ""));
}
}
export default HexToObjectIdentifier;

View File

@ -0,0 +1,46 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import r from "jsrsasign";
import Operation from "../Operation";
/**
* Hex to PEM operation
*/
class HexToPEM extends Operation {
/**
* HexToPEM constructor
*/
constructor() {
super();
this.name = "Hex to PEM";
this.module = "PublicKey";
this.description = "Converts a hexadecimal DER (Distinguished Encoding Rules) string into PEM (Privacy Enhanced Mail) format.";
this.inputType = "string";
this.outputType = "string";
this.args = [
{
"name": "Header string",
"type": "string",
"value": "CERTIFICATE"
}
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
return r.KJUR.asn1.ASN1Util.getPEMStringFromHex(input.replace(/\s/g, ""), args[0]);
}
}
export default HexToPEM;

View File

@ -0,0 +1,40 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import r from "jsrsasign";
import Operation from "../Operation";
/**
* Object Identifier to Hex operation
*/
class ObjectIdentifierToHex extends Operation {
/**
* ObjectIdentifierToHex constructor
*/
constructor() {
super();
this.name = "Object Identifier to Hex";
this.module = "PublicKey";
this.description = "Converts an object identifier (OID) into a hexadecimal string.";
this.inputType = "string";
this.outputType = "string";
this.args = [];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
return r.KJUR.asn1.ASN1Util.oidIntToHex(input);
}
}
export default ObjectIdentifierToHex;

View File

@ -0,0 +1,50 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import r from "jsrsasign";
import Operation from "../Operation";
/**
* PEM to Hex operation
*/
class PEMToHex extends Operation {
/**
* PEMToHex constructor
*/
constructor() {
super();
this.name = "PEM to Hex";
this.module = "PublicKey";
this.description = "Converts PEM (Privacy Enhanced Mail) format to a hexadecimal DER (Distinguished Encoding Rules) string.";
this.inputType = "string";
this.outputType = "string";
this.args = [];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
if (input.indexOf("-----BEGIN") < 0) {
// Add header so that the KEYUTIL function works
input = "-----BEGIN CERTIFICATE-----" + input;
}
if (input.indexOf("-----END") < 0) {
// Add footer so that the KEYUTIL function works
input = input + "-----END CERTIFICATE-----";
}
const cert = new r.X509();
cert.readCertPEM(input);
return cert.hex;
}
}
export default PEMToHex;

View File

@ -0,0 +1,54 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import r from "jsrsasign";
import Operation from "../Operation";
/**
* Parse ASN.1 hex string operation
*/
class ParseASN1HexString extends Operation {
/**
* ParseASN1HexString constructor
*/
constructor() {
super();
this.name = "Parse ASN.1 hex string";
this.module = "PublicKey";
this.description = "Abstract Syntax Notation One (ASN.1) is a standard and notation that describes rules and structures for representing, encoding, transmitting, and decoding data in telecommunications and computer networking.<br><br>This operation parses arbitrary ASN.1 data and presents the resulting tree.";
this.inputType = "string";
this.outputType = "string";
this.args = [
{
"name": "Starting index",
"type": "number",
"value": 0
},
{
"name": "Truncate octet strings longer than",
"type": "number",
"value": 32
}
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
const [index, truncateLen] = args;
return r.ASN1HEX.dump(input.replace(/\s/g, ""), {
"ommitLongOctet": truncateLen
}, index);
}
}
export default ParseASN1HexString;

View File

@ -0,0 +1,216 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import r from "jsrsasign";
import { fromBase64 } from "../lib/Base64";
import { toHex } from "../lib/Hex";
import { formatByteStr, formatDnStr } from "../lib/PublicKey";
import Operation from "../Operation";
import Utils from "../Utils";
/**
* Parse X.509 certificate operation
*/
class ParseX509Certificate extends Operation {
/**
* ParseX509Certificate constructor
*/
constructor() {
super();
this.name = "Parse X.509 certificate";
this.module = "PublicKey";
this.description = "X.509 is an ITU-T standard for a public key infrastructure (PKI) and Privilege Management Infrastructure (PMI). It is commonly involved with SSL/TLS security.<br><br>This operation displays the contents of a certificate in a human readable format, similar to the openssl command line tool.<br><br>Tags: X509, server hello, handshake";
this.inputType = "string";
this.outputType = "string";
this.args = [
{
"name": "Input format",
"type": "option",
"value": ["PEM", "DER Hex", "Base64", "Raw"]
}
];
this.patterns = [
{
"match": "^-+BEGIN CERTIFICATE-+\\r?\\n[\\da-z+/\\n\\r]+-+END CERTIFICATE-+\\r?\\n?$",
"flags": "i",
"args": [
"PEM"
]
}
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
if (!input.length) {
return "No input";
}
const cert = new r.X509(),
inputFormat = args[0];
switch (inputFormat) {
case "DER Hex":
input = input.replace(/\s/g, "");
cert.readCertHex(input);
break;
case "PEM":
cert.readCertPEM(input);
break;
case "Base64":
cert.readCertHex(toHex(fromBase64(input, null, "byteArray"), ""));
break;
case "Raw":
cert.readCertHex(toHex(Utils.strToByteArray(input), ""));
break;
default:
throw "Undefined input format";
}
const sn = cert.getSerialNumberHex(),
issuer = cert.getIssuerString(),
subject = cert.getSubjectString(),
pk = cert.getPublicKey(),
pkFields = [],
sig = cert.getSignatureValueHex();
let pkStr = "",
sigStr = "",
extensions = "";
// Public Key fields
pkFields.push({
key: "Algorithm",
value: pk.type
});
if (pk.type === "EC") { // ECDSA
pkFields.push({
key: "Curve Name",
value: pk.curveName
});
pkFields.push({
key: "Length",
value: (((new r.BigInteger(pk.pubKeyHex, 16)).bitLength()-3) /2) + " bits"
});
pkFields.push({
key: "pub",
value: formatByteStr(pk.pubKeyHex, 16, 18)
});
} else if (pk.type === "DSA") { // DSA
pkFields.push({
key: "pub",
value: formatByteStr(pk.y.toString(16), 16, 18)
});
pkFields.push({
key: "P",
value: formatByteStr(pk.p.toString(16), 16, 18)
});
pkFields.push({
key: "Q",
value: formatByteStr(pk.q.toString(16), 16, 18)
});
pkFields.push({
key: "G",
value: formatByteStr(pk.g.toString(16), 16, 18)
});
} else if (pk.e) { // RSA
pkFields.push({
key: "Length",
value: pk.n.bitLength() + " bits"
});
pkFields.push({
key: "Modulus",
value: formatByteStr(pk.n.toString(16), 16, 18)
});
pkFields.push({
key: "Exponent",
value: pk.e + " (0x" + pk.e.toString(16) + ")"
});
} else {
pkFields.push({
key: "Error",
value: "Unknown Public Key type"
});
}
// Format Public Key fields
for (let i = 0; i < pkFields.length; i++) {
pkStr += ` ${pkFields[i].key}:${(pkFields[i].value + "\n").padStart(
18 - (pkFields[i].key.length + 3) + pkFields[i].value.length + 1,
" "
)}`;
}
// Signature fields
let breakoutSig = false;
try {
breakoutSig = r.ASN1HEX.dump(sig).indexOf("SEQUENCE") === 0;
} catch (err) {
// Error processing signature, output without further breakout
}
if (breakoutSig) { // DSA or ECDSA
sigStr = ` r: ${formatByteStr(r.ASN1HEX.getV(sig, 4), 16, 18)}
s: ${formatByteStr(r.ASN1HEX.getV(sig, 48), 16, 18)}`;
} else { // RSA or unknown
sigStr = ` Signature: ${formatByteStr(sig, 16, 18)}`;
}
// Extensions
try {
extensions = cert.getInfo().split("X509v3 Extensions:\n")[1].split("signature")[0];
} catch (err) {}
const issuerStr = formatDnStr(issuer, 2),
nbDate = formatDate(cert.getNotBefore()),
naDate = formatDate(cert.getNotAfter()),
subjectStr = formatDnStr(subject, 2);
return `Version: ${cert.version} (0x${Utils.hex(cert.version - 1)})
Serial number: ${new r.BigInteger(sn, 16).toString()} (0x${sn})
Algorithm ID: ${cert.getSignatureAlgorithmField()}
Validity
Not Before: ${nbDate} (dd-mm-yy hh:mm:ss) (${cert.getNotBefore()})
Not After: ${naDate} (dd-mm-yy hh:mm:ss) (${cert.getNotAfter()})
Issuer
${issuerStr}
Subject
${subjectStr}
Public Key
${pkStr.slice(0, -1)}
Certificate Signature
Algorithm: ${cert.getSignatureAlgorithmName()}
${sigStr}
Extensions
${extensions}`;
}
}
export default ParseX509Certificate;
/**
* Formats dates.
*
* @param {string} dateStr
* @returns {string}
*/
function formatDate (dateStr) {
return dateStr[4] + dateStr[5] + "/" +
dateStr[2] + dateStr[3] + "/" +
dateStr[0] + dateStr[1] + " " +
dateStr[6] + dateStr[7] + ":" +
dateStr[8] + dateStr[9] + ":" +
dateStr[10] + dateStr[11];
}