Browse Source

ESM: Ported PublicKey operations

Matt C 7 years ago
parent
commit
049656ec6b

+ 72 - 0
src/core/lib/PublicKey.mjs

@@ -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);
+}

+ 40 - 0
src/core/operations/HexToObjectIdentifier.mjs

@@ -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;

+ 46 - 0
src/core/operations/HexToPEM.mjs

@@ -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;

+ 40 - 0
src/core/operations/ObjectIdentifierToHex.mjs

@@ -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;

+ 50 - 0
src/core/operations/PEMToHex.mjs

@@ -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;

+ 54 - 0
src/core/operations/ParseASN1HexString.mjs

@@ -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;

+ 216 - 0
src/core/operations/ParseX509Certificate.mjs

@@ -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];
+}