Skip to content

Commit

Permalink
Merge pull request #26 from elipsion/x509-output
Browse files Browse the repository at this point in the history
Add X.509 output formats
  • Loading branch information
bee-san authored Dec 21, 2023
2 parents 114a319 + daef663 commit 1baa439
Showing 1 changed file with 142 additions and 96 deletions.
238 changes: 142 additions & 96 deletions src/core/operations/ParseX509Certificate.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand All @@ -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 = [
Expand All @@ -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":
Expand All @@ -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";

return output;
}

let pkStr = "",
sigStr = "",
extensions = "";
}
/**
* 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
});

// Public Key fields
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
Expand All @@ -199,8 +247,6 @@ ${sigStr}
Extensions
${extensions}`;
}

}

/**
Expand Down

0 comments on commit 1baa439

Please sign in to comment.