Skip to content

Commit 99eb1cc

Browse files
committed
Merge branch 'rsa' of https://github.com/mattnotmitt/CyberChef into mattnotmitt-rsa
2 parents c880ecf + d4ae241 commit 99eb1cc

File tree

12 files changed

+806
-25
lines changed

12 files changed

+806
-25
lines changed

src/core/config/Categories.json

+5
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,11 @@
105105
"Derive EVP key",
106106
"Bcrypt",
107107
"Scrypt",
108+
"Generate RSA Key Pair",
109+
"RSA Sign",
110+
"RSA Verify",
111+
"RSA Encrypt",
112+
"RSA Decrypt",
108113
"JWT Sign",
109114
"JWT Verify",
110115
"JWT Decode",

src/core/lib/RSA.mjs

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import forge from "node-forge/dist/forge.min.js";
2+
3+
export const MD_ALGORITHMS = {
4+
"SHA-1": forge.md.sha1,
5+
"MD5": forge.md.md5,
6+
"SHA-256": forge.md.sha256,
7+
"SHA-384": forge.md.sha384,
8+
"SHA-512": forge.md.sha512,
9+
};
+83
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
/**
2+
* @author Matt C [me@mitt.dev]
3+
* @author gchq77703 []
4+
* @copyright Crown Copyright 2018
5+
* @license Apache-2.0
6+
*/
7+
8+
import Operation from "../Operation";
9+
import forge from "node-forge/dist/forge.min.js";
10+
11+
/**
12+
* Generate RSA Key Pair operation
13+
*/
14+
class GenerateRSAKeyPair extends Operation {
15+
16+
/**
17+
* GenerateRSAKeyPair constructor
18+
*/
19+
constructor() {
20+
super();
21+
22+
this.name = "Generate RSA Key Pair";
23+
this.module = "Ciphers";
24+
this.description = "Generate an RSA key pair with a given number of bits";
25+
this.infoURL = "https://wikipedia.org/wiki/RSA_(cryptosystem)";
26+
this.inputType = "string";
27+
this.outputType = "string";
28+
this.args = [
29+
{
30+
name: "RSA Key Length",
31+
type: "option",
32+
value: [
33+
"1024",
34+
"2048",
35+
"4096"
36+
]
37+
},
38+
{
39+
name: "Output Format",
40+
type: "option",
41+
value: [
42+
"PEM",
43+
"JSON",
44+
"DER"
45+
]
46+
}
47+
];
48+
}
49+
50+
/**
51+
* @param {string} input
52+
* @param {Object[]} args
53+
* @returns {string}
54+
*/
55+
async run(input, args) {
56+
const [keyLength, outputFormat] = args;
57+
58+
return new Promise((resolve, reject) => {
59+
forge.pki.rsa.generateKeyPair({ bits: Number(keyLength), workers: -1, workerScript: "assets/forge/prime.worker.min.js"}, (err, keypair) => {
60+
if (err) return reject(err);
61+
62+
let result;
63+
64+
switch (outputFormat) {
65+
case "PEM":
66+
result = forge.pki.publicKeyToPem(keypair.publicKey) + "\n" + forge.pki.privateKeyToPem(keypair.privateKey);
67+
break;
68+
case "JSON":
69+
result = JSON.stringify(keypair);
70+
break;
71+
case "DER":
72+
result = forge.asn1.toDer(forge.pki.privateKeyToAsn1(keypair.privateKey)).getBytes();
73+
break;
74+
}
75+
76+
resolve(result);
77+
});
78+
});
79+
}
80+
81+
}
82+
83+
export default GenerateRSAKeyPair;

src/core/operations/RSADecrypt.mjs

+87
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
/**
2+
* @author Matt C [me@mitt.dev]
3+
* @copyright Crown Copyright 2020
4+
* @license Apache-2.0
5+
*/
6+
7+
import Operation from "../Operation.mjs";
8+
import OperationError from "../errors/OperationError.mjs";
9+
import Utils from "../Utils.mjs";
10+
import forge from "node-forge/dist/forge.min.js";
11+
import { MD_ALGORITHMS } from "../lib/RSA.mjs";
12+
13+
/**
14+
* RSA Decrypt operation
15+
*/
16+
class RSADecrypt extends Operation {
17+
18+
/**
19+
* RSADecrypt constructor
20+
*/
21+
constructor() {
22+
super();
23+
24+
this.name = "RSA Decrypt";
25+
this.module = "Ciphers";
26+
this.description = "Decrypt an RSA encrypted message with a PEM encoded private key.";
27+
this.infoURL = "https://wikipedia.org/wiki/RSA_(cryptosystem)";
28+
this.inputType = "ArrayBuffer";
29+
this.outputType = "string";
30+
this.args = [
31+
{
32+
name: "RSA Private Key (PEM)",
33+
type: "text",
34+
value: "-----BEGIN RSA PRIVATE KEY-----"
35+
},
36+
{
37+
name: "Key Password",
38+
type: "text",
39+
value: ""
40+
},
41+
{
42+
name: "Encryption Scheme",
43+
type: "argSelector",
44+
value: [
45+
{
46+
name: "RSA-OAEP",
47+
on: [3]
48+
},
49+
{
50+
name: "RSAES-PKCS1-V1_5",
51+
off: [3]
52+
},
53+
{
54+
name: "RAW",
55+
off: [3]
56+
}]
57+
},
58+
{
59+
name: "Message Digest Algorithm",
60+
type: "option",
61+
value: Object.keys(MD_ALGORITHMS)
62+
}
63+
];
64+
}
65+
66+
/**
67+
* @param {string} input
68+
* @param {Object[]} args
69+
* @returns {string}
70+
*/
71+
run(input, args) {
72+
const [pemKey, password, scheme, md] = args;
73+
if (pemKey.replace("-----BEGIN RSA PRIVATE KEY-----", "").length === 0) {
74+
throw new OperationError("Please enter a private key.");
75+
}
76+
try {
77+
const privKey = forge.pki.decryptRsaPrivateKey(pemKey, password);
78+
const dMsg = privKey.decrypt(Utils.arrayBufferToStr(input), scheme, {md: MD_ALGORITHMS[md].create()});
79+
return dMsg;
80+
} catch (err) {
81+
throw new OperationError(err);
82+
}
83+
}
84+
85+
}
86+
87+
export default RSADecrypt;

src/core/operations/RSAEncrypt.mjs

+88
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
/**
2+
* @author Matt C [me@mitt.dev]
3+
* @copyright Crown Copyright 2020
4+
* @license Apache-2.0
5+
*/
6+
7+
import Operation from "../Operation.mjs";
8+
import OperationError from "../errors/OperationError.mjs";
9+
import Utils from "../Utils.mjs";
10+
import forge from "node-forge/dist/forge.min.js";
11+
import { MD_ALGORITHMS } from "../lib/RSA.mjs";
12+
13+
/**
14+
* RSA Encrypt operation
15+
*/
16+
class RSAEncrypt extends Operation {
17+
18+
/**
19+
* RSAEncrypt constructor
20+
*/
21+
constructor() {
22+
super();
23+
24+
this.name = "RSA Encrypt";
25+
this.module = "Ciphers";
26+
this.description = "Encrypt a message with a PEM encoded RSA public key.";
27+
this.infoURL = "https://wikipedia.org/wiki/RSA_(cryptosystem)";
28+
this.inputType = "string";
29+
this.outputType = "ArrayBuffer";
30+
this.args = [
31+
{
32+
name: "RSA Public Key (PEM)",
33+
type: "text",
34+
value: "-----BEGIN RSA PUBLIC KEY-----"
35+
},
36+
{
37+
name: "Encryption Scheme",
38+
type: "argSelector",
39+
value: [
40+
{
41+
name: "RSA-OAEP",
42+
on: [2]
43+
},
44+
{
45+
name: "RSAES-PKCS1-V1_5",
46+
off: [2]
47+
},
48+
{
49+
name: "RAW",
50+
off: [2]
51+
}]
52+
},
53+
{
54+
name: "Message Digest Algorithm",
55+
type: "option",
56+
value: Object.keys(MD_ALGORITHMS)
57+
}
58+
];
59+
}
60+
61+
/**
62+
* @param {string} input
63+
* @param {Object[]} args
64+
* @returns {string}
65+
*/
66+
run(input, args) {
67+
const [pemKey, scheme, md] = args;
68+
69+
if (pemKey.replace("-----BEGIN RSA PUBLIC KEY-----", "").length === 0) {
70+
throw new OperationError("Please enter a public key.");
71+
}
72+
try {
73+
// Load public key
74+
const pubKey = forge.pki.publicKeyFromPem(pemKey);
75+
// Encrypt message
76+
const eMsg = pubKey.encrypt(input, scheme, {md: MD_ALGORITHMS[md].create()});
77+
return Utils.strToArrayBuffer(eMsg);
78+
} catch (err) {
79+
if (err.message === "RSAES-OAEP input message length is too long.") {
80+
throw new OperationError(`RSAES-OAEP input message length (${err.length}) is longer than the maximum allowed length (${err.maxLength}).`);
81+
}
82+
throw new OperationError(err);
83+
}
84+
}
85+
86+
}
87+
88+
export default RSAEncrypt;

src/core/operations/RSASign.mjs

+74
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
/**
2+
* @author Matt C [me@mitt.dev]
3+
* @author gchq77703 []
4+
* @copyright Crown Copyright 2020
5+
* @license Apache-2.0
6+
*/
7+
8+
import Operation from "../Operation";
9+
import OperationError from "../errors/OperationError";
10+
import forge from "node-forge/dist/forge.min.js";
11+
import { MD_ALGORITHMS } from "../lib/RSA.mjs";
12+
13+
/**
14+
* RSA Sign operation
15+
*/
16+
class RSASign extends Operation {
17+
18+
/**
19+
* RSASign constructor
20+
*/
21+
constructor() {
22+
super();
23+
24+
this.name = "RSA Sign";
25+
this.module = "Ciphers";
26+
this.description = "Sign a plaintext message with a PEM encoded RSA key.";
27+
this.infoURL = "https://wikipedia.org/wiki/RSA_(cryptosystem)";
28+
this.inputType = "string";
29+
this.outputType = "string";
30+
this.args = [
31+
{
32+
name: "RSA Private Key (PEM)",
33+
type: "text",
34+
value: "-----BEGIN RSA PRIVATE KEY-----"
35+
},
36+
{
37+
name: "Key Password",
38+
type: "text",
39+
value: ""
40+
},
41+
{
42+
name: "Message Digest Algorithm",
43+
type: "option",
44+
value: Object.keys(MD_ALGORITHMS)
45+
}
46+
];
47+
}
48+
49+
/**
50+
* @param {string} input
51+
* @param {Object[]} args
52+
* @returns {string}
53+
*/
54+
run(input, args) {
55+
const [key, password, mdAlgo] = args;
56+
if (key.replace("-----BEGIN RSA PRIVATE KEY-----", "").length === 0) {
57+
throw new OperationError("Please enter a private key.");
58+
}
59+
try {
60+
const privateKey = forge.pki.decryptRsaPrivateKey(key, password);
61+
// Generate message hash
62+
const md = MD_ALGORITHMS[mdAlgo].create();
63+
md.update(input, "utf8");
64+
// Sign message hash
65+
const sig = privateKey.sign(md);
66+
return sig;
67+
} catch (err) {
68+
throw new OperationError(err);
69+
}
70+
}
71+
72+
}
73+
74+
export default RSASign;

0 commit comments

Comments
 (0)