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