Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add X.509 output formats #26

Merged
merged 1 commit into from
Dec 21, 2023
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading