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

RSA Operations #652

Merged
merged 13 commits into from
Feb 1, 2021
10 changes: 5 additions & 5 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions src/core/config/Categories.json
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,11 @@
"Derive EVP key",
"Bcrypt",
"Scrypt",
"Generate RSA Key Pair",
"RSA Sign",
"RSA Verify",
"RSA Encrypt",
"RSA Decrypt",
"JWT Sign",
"JWT Verify",
"JWT Decode",
Expand Down
9 changes: 9 additions & 0 deletions src/core/lib/RSA.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import forge from "node-forge/dist/forge.min.js";

export const MD_ALGORITHMS = {
"SHA-1": forge.md.sha1,
"MD5": forge.md.md5,
"SHA-256": forge.md.sha256,
"SHA-384": forge.md.sha384,
"SHA-512": forge.md.sha512,
};
83 changes: 83 additions & 0 deletions src/core/operations/GenerateRSAKeyPair.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
/**
* @author Matt C [me@mitt.dev]
* @author gchq77703 []
* @copyright Crown Copyright 2018
* @license Apache-2.0
*/

import Operation from "../Operation";
import forge from "node-forge/dist/forge.min.js";

/**
* Generate RSA Key Pair operation
*/
class GenerateRSAKeyPair extends Operation {

/**
* GenerateRSAKeyPair constructor
*/
constructor() {
super();

this.name = "Generate RSA Key Pair";
this.module = "Ciphers";
this.description = "Generate an RSA key pair with a given number of bits";
this.infoURL = "https://wikipedia.org/wiki/RSA_(cryptosystem)";
this.inputType = "string";
this.outputType = "string";
this.args = [
{
name: "RSA Key Length",
type: "option",
value: [
"1024",
"2048",
"4096"
]
},
{
name: "Output Format",
type: "option",
value: [
"PEM",
"JSON",
"DER"
]
}
];
}

/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
async run(input, args) {
const [keyLength, outputFormat] = args;

return new Promise((resolve, reject) => {
forge.pki.rsa.generateKeyPair({ bits: Number(keyLength), workers: -1, workerScript: "assets/forge/prime.worker.min.js"}, (err, keypair) => {
if (err) return reject(err);

let result;

switch (outputFormat) {
case "PEM":
result = forge.pki.publicKeyToPem(keypair.publicKey) + "\n" + forge.pki.privateKeyToPem(keypair.privateKey);
break;
case "JSON":
result = JSON.stringify(keypair);
break;
case "DER":
result = forge.asn1.toDer(forge.pki.privateKeyToAsn1(keypair.privateKey)).getBytes();
break;
}

resolve(result);
});
});
}

}

export default GenerateRSAKeyPair;
87 changes: 87 additions & 0 deletions src/core/operations/RSADecrypt.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
/**
* @author Matt C [me@mitt.dev]
* @copyright Crown Copyright 2020
* @license Apache-2.0
*/

import Operation from "../Operation.mjs";
import OperationError from "../errors/OperationError.mjs";
import Utils from "../Utils.mjs";
import forge from "node-forge/dist/forge.min.js";
import { MD_ALGORITHMS } from "../lib/RSA.mjs";

/**
* RSA Decrypt operation
*/
class RSADecrypt extends Operation {

/**
* RSADecrypt constructor
*/
constructor() {
super();

this.name = "RSA Decrypt";
this.module = "Ciphers";
this.description = "Decrypt an RSA encrypted message with a PEM encoded private key.";
this.infoURL = "https://wikipedia.org/wiki/RSA_(cryptosystem)";
this.inputType = "ArrayBuffer";
this.outputType = "string";
this.args = [
{
name: "RSA Private Key (PEM)",
type: "text",
value: "-----BEGIN RSA PRIVATE KEY-----"
},
{
name: "Key Password",
type: "text",
value: ""
},
{
name: "Encryption Scheme",
type: "argSelector",
value: [
{
name: "RSA-OAEP",
on: [3]
},
{
name: "RSAES-PKCS1-V1_5",
off: [3]
},
{
name: "RAW",
off: [3]
}]
},
{
name: "Message Digest Algorithm",
type: "option",
value: Object.keys(MD_ALGORITHMS)
}
];
}

/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
const [pemKey, password, scheme, md] = args;
if (pemKey.replace("-----BEGIN RSA PRIVATE KEY-----", "").length === 0) {
throw new OperationError("Please enter a private key.");
}
try {
const privKey = forge.pki.decryptRsaPrivateKey(pemKey, password);
const dMsg = privKey.decrypt(Utils.arrayBufferToStr(input), scheme, {md: MD_ALGORITHMS[md].create()});
return dMsg;
} catch (err) {
throw new OperationError(err);
}
}

}

export default RSADecrypt;
88 changes: 88 additions & 0 deletions src/core/operations/RSAEncrypt.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
/**
* @author Matt C [me@mitt.dev]
* @copyright Crown Copyright 2020
* @license Apache-2.0
*/

import Operation from "../Operation.mjs";
import OperationError from "../errors/OperationError.mjs";
import Utils from "../Utils.mjs";
import forge from "node-forge/dist/forge.min.js";
import { MD_ALGORITHMS } from "../lib/RSA.mjs";

/**
* RSA Encrypt operation
*/
class RSAEncrypt extends Operation {

/**
* RSAEncrypt constructor
*/
constructor() {
super();

this.name = "RSA Encrypt";
this.module = "Ciphers";
this.description = "Encrypt a message with a PEM encoded RSA public key.";
this.infoURL = "https://wikipedia.org/wiki/RSA_(cryptosystem)";
this.inputType = "string";
this.outputType = "ArrayBuffer";
this.args = [
{
name: "RSA Public Key (PEM)",
type: "text",
value: "-----BEGIN RSA PUBLIC KEY-----"
},
{
name: "Encryption Scheme",
type: "argSelector",
value: [
{
name: "RSA-OAEP",
on: [2]
},
{
name: "RSAES-PKCS1-V1_5",
off: [2]
},
{
name: "RAW",
off: [2]
}]
},
{
name: "Message Digest Algorithm",
type: "option",
value: Object.keys(MD_ALGORITHMS)
}
];
}

/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
const [pemKey, scheme, md] = args;

if (pemKey.replace("-----BEGIN RSA PUBLIC KEY-----", "").length === 0) {
throw new OperationError("Please enter a public key.");
}
try {
// Load public key
const pubKey = forge.pki.publicKeyFromPem(pemKey);
// Encrypt message
const eMsg = pubKey.encrypt(input, scheme, {md: MD_ALGORITHMS[md].create()});
return Utils.strToArrayBuffer(eMsg);
} catch (err) {
if (err.message === "RSAES-OAEP input message length is too long.") {
throw new OperationError(`RSAES-OAEP input message length (${err.length}) is longer than the maximum allowed length (${err.maxLength}).`);
}
throw new OperationError(err);
}
}

}

export default RSAEncrypt;
Loading