From daef66372713bd08096799679a0e326b466ac5aa Mon Sep 17 00:00:00 2001 From: Erik Hansson Date: Sat, 11 Feb 2023 17:27:47 +0100 Subject: [PATCH] Add X.509 output formats --- src/core/operations/ParseX509Certificate.mjs | 238 +++++++++++-------- 1 file changed, 142 insertions(+), 96 deletions(-) diff --git a/src/core/operations/ParseX509Certificate.mjs b/src/core/operations/ParseX509Certificate.mjs index 88678de9..e0aa38b7 100644 --- a/src/core/operations/ParseX509Certificate.mjs +++ b/src/core/operations/ParseX509Certificate.mjs @@ -5,8 +5,8 @@ */ import r from "jsrsasign"; -import { fromBase64 } from "../lib/Base64.mjs"; -import { toHex } from "../lib/Hex.mjs"; +import { fromBase64, toBase64 } from "../lib/Base64.mjs"; +import { fromHex, toHex } from "../lib/Hex.mjs"; import { formatByteStr, formatDnObj } from "../lib/PublicKey.mjs"; import Operation from "../Operation.mjs"; import Utils from "../Utils.mjs"; @@ -33,6 +33,11 @@ class ParseX509Certificate extends Operation { "name": "Input format", "type": "option", "value": ["PEM", "DER Hex", "Base64", "Raw"] + }, + { + "name": "Output format", + "type": "option", + "value": ["Text", "JSON", "PEM", "DER Hex", "Base64", "Raw"] } ]; this.checks = [ @@ -55,9 +60,13 @@ class ParseX509Certificate extends Operation { } const cert = new r.X509(), - inputFormat = args[0]; + inputFormat = args[0], + outputFormat = args[1]; + + let undefinedInputFormat = false, + undefinedOuputFormat = false, + output = ""; - let undefinedInputFormat = false; try { switch (inputFormat) { case "DER Hex": @@ -81,107 +90,146 @@ class ParseX509Certificate extends Operation { } if (undefinedInputFormat) throw "Undefined input format"; - const sn = cert.getSerialNumberHex(), - issuer = cert.getIssuer(), - subject = cert.getSubject(), - pk = cert.getPublicKey(), - pkFields = [], - sig = cert.getSignatureValueHex(); + try { + switch (outputFormat) { + case "Text": + output = formatText(cert); + break; + case "JSON": + output = JSON.stringify(cert.getParam()); + break; + case "DER Hex": + output = cert.hex; + break; + case "PEM": + output = r.hextopem(cert.hex, "CERTIFICATE"); + break; + case "Base64": + output = toBase64(fromHex(cert.hex)); + break; + case "Raw": + output = Utils.byteArrayToChars(fromHex(cert.hex)); + break; + default: + undefinedOuputFormat = true; + } + } catch (e) { + throw "Certificate encoding error (what even hapened?)"; + } + if (undefinedOuputFormat) throw "Undefined output format"; - let pkStr = "", - sigStr = "", - extensions = ""; + return output; + } - // Public Key fields +} +/** + * Format X.509 certificate. + * + * @param {r.X509} cert + * @returns {string} + */ +function formatText(cert) { + const sn = cert.getSerialNumberHex(), + issuer = cert.getIssuer(), + subject = cert.getSubject(), + 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: "Algorithm", - value: pk.type + 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" + }); + } - 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, + " " + )}`; + } - // 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 + } - // 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)} + 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)}`; - } + } 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) {} + // Extensions + try { + extensions = cert.getInfo().split("X509v3 Extensions:\n")[1].split("signature")[0]; + } catch (err) { } - const issuerStr = formatDnObj(issuer, 2), - nbDate = formatDate(cert.getNotBefore()), - naDate = formatDate(cert.getNotAfter()), - subjectStr = formatDnObj(subject, 2); + const issuerStr = formatDnObj(issuer, 2), + nbDate = formatDate(cert.getNotBefore()), + naDate = formatDate(cert.getNotAfter()), + subjectStr = formatDnObj(subject, 2); - return `Version: ${cert.version} (0x${Utils.hex(cert.version - 1)}) + 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 @@ -199,8 +247,6 @@ ${sigStr} Extensions ${extensions}`; - } - } /**