mirror of
https://github.com/gchq/CyberChef.git
synced 2025-04-21 07:16:17 -04:00
Merge branch 'master' into master
This commit is contained in:
commit
f6c5a04088
21 changed files with 1586 additions and 20 deletions
66
src/core/operations/JA4ServerFingerprint.mjs
Normal file
66
src/core/operations/JA4ServerFingerprint.mjs
Normal file
|
@ -0,0 +1,66 @@
|
|||
/**
|
||||
* @author n1474335 [n1474335@gmail.com]
|
||||
* @copyright Crown Copyright 2024
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation.mjs";
|
||||
import Utils from "../Utils.mjs";
|
||||
import {toJA4S} from "../lib/JA4.mjs";
|
||||
|
||||
/**
|
||||
* JA4Server Fingerprint operation
|
||||
*/
|
||||
class JA4ServerFingerprint extends Operation {
|
||||
|
||||
/**
|
||||
* JA4ServerFingerprint constructor
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.name = "JA4Server Fingerprint";
|
||||
this.module = "Crypto";
|
||||
this.description = "Generates a JA4Server Fingerprint (JA4S) to help identify TLS servers or sessions based on hashing together values from the Server Hello.<br><br>Input: A hex stream of the TLS or QUIC Server Hello packet application layer.";
|
||||
this.infoURL = "https://medium.com/foxio/ja4-network-fingerprinting-9376fe9ca637";
|
||||
this.inputType = "string";
|
||||
this.outputType = "string";
|
||||
this.args = [
|
||||
{
|
||||
name: "Input format",
|
||||
type: "option",
|
||||
value: ["Hex", "Base64", "Raw"]
|
||||
},
|
||||
{
|
||||
name: "Output format",
|
||||
type: "option",
|
||||
value: ["JA4S", "JA4S Raw", "Both"]
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
run(input, args) {
|
||||
const [inputFormat, outputFormat] = args;
|
||||
input = Utils.convertToByteArray(input, inputFormat);
|
||||
const ja4s = toJA4S(new Uint8Array(input));
|
||||
|
||||
// Output
|
||||
switch (outputFormat) {
|
||||
case "JA4S":
|
||||
return ja4s.JA4S;
|
||||
case "JA4S Raw":
|
||||
return ja4s.JA4S_r;
|
||||
case "Both":
|
||||
default:
|
||||
return `JA4S: ${ja4s.JA4S}\nJA4S_r: ${ja4s.JA4S_r}`;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default JA4ServerFingerprint;
|
80
src/core/operations/JWKToPem.mjs
Normal file
80
src/core/operations/JWKToPem.mjs
Normal file
|
@ -0,0 +1,80 @@
|
|||
/**
|
||||
* @author cplussharp
|
||||
* @copyright Crown Copyright 2021
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import r from "jsrsasign";
|
||||
import Operation from "../Operation.mjs";
|
||||
import OperationError from "../errors/OperationError.mjs";
|
||||
|
||||
/**
|
||||
* PEM to JWK operation
|
||||
*/
|
||||
class PEMToJWK extends Operation {
|
||||
|
||||
/**
|
||||
* PEMToJWK constructor
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.name = "JWK to PEM";
|
||||
this.module = "PublicKey";
|
||||
this.description = "Converts Keys in JSON Web Key format to PEM format (PKCS#8).";
|
||||
this.infoURL = "https://datatracker.ietf.org/doc/html/rfc7517";
|
||||
this.inputType = "string";
|
||||
this.outputType = "string";
|
||||
this.args = [];
|
||||
this.checks = [
|
||||
{
|
||||
"pattern": "\"kty\":\\s*\"(EC|RSA)\"",
|
||||
"flags": "gm",
|
||||
"args": []
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
run(input, args) {
|
||||
const inputJson = JSON.parse(input);
|
||||
|
||||
let keys = [];
|
||||
if (Array.isArray(inputJson)) {
|
||||
// list of keys => transform all keys
|
||||
keys = inputJson;
|
||||
} else if (Array.isArray(inputJson.keys)) {
|
||||
// JSON Web Key Set => transform all keys
|
||||
keys = inputJson.keys;
|
||||
} else if (typeof inputJson === "object") {
|
||||
// single key
|
||||
keys.push(inputJson);
|
||||
} else {
|
||||
throw new OperationError("Input is not a JSON Web Key");
|
||||
}
|
||||
|
||||
let output = "";
|
||||
for (let i=0; i<keys.length; i++) {
|
||||
const jwk = keys[i];
|
||||
if (typeof jwk.kty !== "string") {
|
||||
throw new OperationError("Invalid JWK format");
|
||||
} else if ("|RSA|EC|".indexOf(jwk.kty) === -1) {
|
||||
throw new OperationError(`Unsupported JWK key type '${inputJson.kty}'`);
|
||||
}
|
||||
|
||||
const key = r.KEYUTIL.getKey(jwk);
|
||||
const pem = key.isPrivate ? r.KEYUTIL.getPEM(key, "PKCS8PRV") : r.KEYUTIL.getPEM(key);
|
||||
|
||||
// PEM ends with '\n', so a new key always starts on a new line
|
||||
output += pem;
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
}
|
||||
|
||||
export default PEMToJWK;
|
88
src/core/operations/PEMToJWK.mjs
Normal file
88
src/core/operations/PEMToJWK.mjs
Normal file
|
@ -0,0 +1,88 @@
|
|||
/**
|
||||
* @author cplussharp
|
||||
* @copyright Crown Copyright 2021
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import r from "jsrsasign";
|
||||
import Operation from "../Operation.mjs";
|
||||
import OperationError from "../errors/OperationError.mjs";
|
||||
|
||||
/**
|
||||
* PEM to JWK operation
|
||||
*/
|
||||
class PEMToJWK extends Operation {
|
||||
|
||||
/**
|
||||
* PEMToJWK constructor
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.name = "PEM to JWK";
|
||||
this.module = "PublicKey";
|
||||
this.description = "Converts Keys in PEM format to a JSON Web Key format.";
|
||||
this.infoURL = "https://datatracker.ietf.org/doc/html/rfc7517";
|
||||
this.inputType = "string";
|
||||
this.outputType = "string";
|
||||
this.args = [];
|
||||
this.checks = [
|
||||
{
|
||||
"pattern": "-----BEGIN ((RSA |EC )?(PRIVATE|PUBLIC) KEY|CERTIFICATE)-----",
|
||||
"args": []
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
run(input, args) {
|
||||
let output = "";
|
||||
let match;
|
||||
const regex = /-----BEGIN ([A-Z][A-Z ]+[A-Z])-----/g;
|
||||
while ((match = regex.exec(input)) !== null) {
|
||||
// find corresponding end tag
|
||||
const indexBase64 = match.index + match[0].length;
|
||||
const header = input.substring(match.index, indexBase64);
|
||||
const footer = `-----END ${match[1]}-----`;
|
||||
const indexFooter = input.indexOf(footer, indexBase64);
|
||||
if (indexFooter === -1) {
|
||||
throw new OperationError(`PEM footer '${footer}' not found`);
|
||||
}
|
||||
|
||||
const pem = input.substring(match.index, indexFooter + footer.length);
|
||||
if (match[1].indexOf("KEY") !== -1) {
|
||||
if (header === "-----BEGIN RSA PUBLIC KEY-----") {
|
||||
throw new OperationError("Unsupported RSA public key format. Only PKCS#8 is supported.");
|
||||
}
|
||||
|
||||
const key = r.KEYUTIL.getKey(pem);
|
||||
if (key.type === "DSA") {
|
||||
throw new OperationError("DSA keys are not supported for JWK");
|
||||
}
|
||||
const jwk = r.KEYUTIL.getJWKFromKey(key);
|
||||
if (output.length > 0) {
|
||||
output += "\n";
|
||||
}
|
||||
output += JSON.stringify(jwk);
|
||||
} else if (match[1] === "CERTIFICATE") {
|
||||
const cert = new r.X509();
|
||||
cert.readCertPEM(pem);
|
||||
const key = cert.getPublicKey();
|
||||
const jwk = r.KEYUTIL.getJWKFromKey(key);
|
||||
if (output.length > 0) {
|
||||
output += "\n";
|
||||
}
|
||||
output += JSON.stringify(jwk);
|
||||
} else {
|
||||
throw new OperationError(`Unsupported PEM type '${match[1]}'`);
|
||||
}
|
||||
}
|
||||
return output;
|
||||
}
|
||||
}
|
||||
|
||||
export default PEMToJWK;
|
68
src/core/operations/PubKeyFromCert.mjs
Normal file
68
src/core/operations/PubKeyFromCert.mjs
Normal file
|
@ -0,0 +1,68 @@
|
|||
/**
|
||||
* @author cplussharp
|
||||
* @copyright Crown Copyright 2023
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import r from "jsrsasign";
|
||||
import Operation from "../Operation.mjs";
|
||||
import OperationError from "../errors/OperationError.mjs";
|
||||
|
||||
/**
|
||||
* Public Key from Certificate operation
|
||||
*/
|
||||
class PubKeyFromCert extends Operation {
|
||||
|
||||
/**
|
||||
* PubKeyFromCert constructor
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.name = "Public Key from Certificate";
|
||||
this.module = "PublicKey";
|
||||
this.description = "Extracts the Public Key from a Certificate.";
|
||||
this.infoURL = "https://en.wikipedia.org/wiki/X.509";
|
||||
this.inputType = "string";
|
||||
this.outputType = "string";
|
||||
this.args = [];
|
||||
this.checks = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
run(input, args) {
|
||||
let output = "";
|
||||
let match;
|
||||
const regex = /-----BEGIN CERTIFICATE-----/g;
|
||||
while ((match = regex.exec(input)) !== null) {
|
||||
// find corresponding end tag
|
||||
const indexBase64 = match.index + match[0].length;
|
||||
const footer = "-----END CERTIFICATE-----";
|
||||
const indexFooter = input.indexOf(footer, indexBase64);
|
||||
if (indexFooter === -1) {
|
||||
throw new OperationError(`PEM footer '${footer}' not found`);
|
||||
}
|
||||
|
||||
const certPem = input.substring(match.index, indexFooter + footer.length);
|
||||
const cert = new r.X509();
|
||||
cert.readCertPEM(certPem);
|
||||
let pubKey;
|
||||
try {
|
||||
pubKey = cert.getPublicKey();
|
||||
} catch {
|
||||
throw new OperationError("Unsupported public key type");
|
||||
}
|
||||
const pubKeyPem = r.KEYUTIL.getPEM(pubKey);
|
||||
|
||||
// PEM ends with '\n', so a new key always starts on a new line
|
||||
output += pubKeyPem;
|
||||
}
|
||||
return output;
|
||||
}
|
||||
}
|
||||
|
||||
export default PubKeyFromCert;
|
82
src/core/operations/PubKeyFromPrivKey.mjs
Normal file
82
src/core/operations/PubKeyFromPrivKey.mjs
Normal file
|
@ -0,0 +1,82 @@
|
|||
/**
|
||||
* @author cplussharp
|
||||
* @copyright Crown Copyright 2023
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import r from "jsrsasign";
|
||||
import Operation from "../Operation.mjs";
|
||||
import OperationError from "../errors/OperationError.mjs";
|
||||
|
||||
/**
|
||||
* Public Key from Private Key operation
|
||||
*/
|
||||
class PubKeyFromPrivKey extends Operation {
|
||||
|
||||
/**
|
||||
* PubKeyFromPrivKey constructor
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.name = "Public Key from Private Key";
|
||||
this.module = "PublicKey";
|
||||
this.description = "Extracts the Public Key from a Private Key.";
|
||||
this.infoURL = "https://en.wikipedia.org/wiki/PKCS_8";
|
||||
this.inputType = "string";
|
||||
this.outputType = "string";
|
||||
this.args = [];
|
||||
this.checks = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
run(input, args) {
|
||||
let output = "";
|
||||
let match;
|
||||
const regex = /-----BEGIN ((RSA |EC |DSA )?PRIVATE KEY)-----/g;
|
||||
while ((match = regex.exec(input)) !== null) {
|
||||
// find corresponding end tag
|
||||
const indexBase64 = match.index + match[0].length;
|
||||
const footer = `-----END ${match[1]}-----`;
|
||||
const indexFooter = input.indexOf(footer, indexBase64);
|
||||
if (indexFooter === -1) {
|
||||
throw new OperationError(`PEM footer '${footer}' not found`);
|
||||
}
|
||||
|
||||
const privKeyPem = input.substring(match.index, indexFooter + footer.length);
|
||||
let privKey;
|
||||
try {
|
||||
privKey = r.KEYUTIL.getKey(privKeyPem);
|
||||
} catch (err) {
|
||||
throw new OperationError(`Unsupported key type: ${err}`);
|
||||
}
|
||||
let pubKey;
|
||||
if (privKey.type && privKey.type === "EC") {
|
||||
pubKey = new r.KJUR.crypto.ECDSA({ curve: privKey.curve });
|
||||
pubKey.setPublicKeyHex(privKey.generatePublicKeyHex());
|
||||
} else if (privKey.type && privKey.type === "DSA") {
|
||||
if (!privKey.y) {
|
||||
throw new OperationError(`DSA Private Key in PKCS#8 is not supported`);
|
||||
}
|
||||
pubKey = new r.KJUR.crypto.DSA();
|
||||
pubKey.setPublic(privKey.p, privKey.q, privKey.g, privKey.y);
|
||||
} else if (privKey.n && privKey.e) {
|
||||
pubKey = new r.RSAKey();
|
||||
pubKey.setPublic(privKey.n, privKey.e);
|
||||
} else {
|
||||
throw new OperationError(`Unsupported key type`);
|
||||
}
|
||||
const pubKeyPem = r.KEYUTIL.getPEM(pubKey);
|
||||
|
||||
// PEM ends with '\n', so a new key always starts on a new line
|
||||
output += pubKeyPem;
|
||||
}
|
||||
return output;
|
||||
}
|
||||
}
|
||||
|
||||
export default PubKeyFromPrivKey;
|
Loading…
Add table
Add a link
Reference in a new issue