From f81ca3ba605687ccb1b2b76d700a03f7dc4d370f Mon Sep 17 00:00:00 2001 From: GCHQ 77703 Date: Thu, 30 Aug 2018 22:38:01 +0100 Subject: [PATCH] Implement RSA generation and signing of messages --- src/core/config/Categories.json | 2 + src/core/operations/GenerateRSAKeyPair.mjs | 82 ++++++++++++++++++++++ src/core/operations/RSASign.mjs | 60 ++++++++++++++++ 3 files changed, 144 insertions(+) create mode 100644 src/core/operations/GenerateRSAKeyPair.mjs create mode 100644 src/core/operations/RSASign.mjs diff --git a/src/core/config/Categories.json b/src/core/config/Categories.json index ab3bc486..68e17294 100755 --- a/src/core/config/Categories.json +++ b/src/core/config/Categories.json @@ -89,6 +89,8 @@ "Derive EVP key", "Bcrypt", "Scrypt", + "Generate RSA Key Pair", + "RSA Sign", "Pseudo-Random Number Generator" ] }, diff --git a/src/core/operations/GenerateRSAKeyPair.mjs b/src/core/operations/GenerateRSAKeyPair.mjs new file mode 100644 index 00000000..9d7a88d8 --- /dev/null +++ b/src/core/operations/GenerateRSAKeyPair.mjs @@ -0,0 +1,82 @@ +/** + * @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: 2 }, (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; diff --git a/src/core/operations/RSASign.mjs b/src/core/operations/RSASign.mjs new file mode 100644 index 00000000..fb3e4bbd --- /dev/null +++ b/src/core/operations/RSASign.mjs @@ -0,0 +1,60 @@ +/** + * @author gchq77703 [] + * @copyright Crown Copyright 2018 + * @license Apache-2.0 + */ + +import Operation from "../Operation"; +import forge from "node-forge/dist/forge.min.js"; + +/** + * RSA Sign operation + */ +class RSASign extends Operation { + + /** + * RSASign constructor + */ + constructor() { + super(); + + this.name = "RSA Sign"; + this.module = "Ciphers"; + this.description = "Sign a plaintext message with a PEM encoded RSA key."; + this.infoURL = "https://wikipedia.org/wiki/RSA_(cryptosystem)"; + this.inputType = "string"; + this.outputType = "byteArray"; + this.args = [ + { + name: "RSA Private Key (PEM)", + type: "text", + value: "-----BEGIN RSA PRIVATE KEY-----" + }, + { + name: "Password", + type: "text", + value: "" + } + ]; + } + + /** + * @param {string} input + * @param {Object[]} args + * @returns {string} + */ + run(input, args) { + const [key, password] = args; + + const privateKey = forge.pki.decryptRsaPrivateKey(key, password); + + const md = forge.md.sha1.create(); + md.update(input, 'utf8'); + const signature = privateKey.sign(md); + + return signature.split('').map(char => char.charCodeAt()); + } + +} + +export default RSASign;