Skip to content

Instantly share code, notes, and snippets.

@milaabl
Created August 5, 2023 11:19
Show Gist options
  • Save milaabl/268b678c7b9793a11e9f636d3ee3ba70 to your computer and use it in GitHub Desktop.
Save milaabl/268b678c7b9793a11e9f636d3ee3ba70 to your computer and use it in GitHub Desktop.
E2E RSA + AES encryption system (symmetric+asymmetric encryption for sensitive data transfers) | React, CryptoJS & Shell .sh (OpenSSL)
import CryptoJS from 'crypto-js';
import './app.css';
import FileInput from "./components/FileInput/FileInput";
import { FC, useEffect, useState } from "react";
const getAESEncrypted = (secret: string, password: string) => {
const encrypted = CryptoJS.AES.encrypt(secret, password);
return encrypted.toString();
}
const fromBase64 = (base64String : string) => Uint8Array.from(atob(base64String), c => c.charCodeAt(0));
const getPkciDer = (pkciPem : string) => {
console.log(pkciPem);
const pkciPemHeader = "-----BEGIN PUBLIC KEY-----";
const pkciPemFooter = "-----END PUBLIC KEY-----";
console.log(pkciPem);
pkciPem = pkciPem.substring(pkciPemHeader.length, pkciPem.length - pkciPemFooter.length);
console.log(pkciPem);
return fromBase64(pkciPem);
}
const importPublicKey = async (pkciPem : string) => {
return await window.crypto.subtle.importKey(
"spki",
getPkciDer(pkciPem),
{
name: "RSA-OAEP",
hash: "SHA-1",
},
true,
["encrypt"]
);
}
const encryptRSA = async (key : CryptoKey, text : string) => {
const enc = new TextEncoder();
const encrypted = await window.crypto.subtle.encrypt(
{
name: "RSA-OAEP"
},
key,
enc.encode(text)
);
return encrypted;
}
const ab2str = (buf : ArrayBuffer) => {
//@ts-ignore
return String.fromCharCode.apply(null, new Uint8Array(buf));
}
const getRSAEncrypted = async (text : string) => {
if (!process.env.REACT_APP_PUBLIC_RSA_KEY) {
throw new Error('No public key provided in the .env');
}
const key =
await importPublicKey(process.env.REACT_APP_PUBLIC_RSA_KEY);
const encrypted = await encryptRSA(key, text);
download(createTextFile(window.btoa(ab2str(encrypted))), 'cipher_to_retrieve_aes_password.txt');
}
const dec2hex = (dec: number): string => {
return dec.toString(16).padStart(2, "0");
};
const generateId = (len?: number): string => {
let arr = new Uint8Array((len || 32) / 2);
window.crypto.getRandomValues(arr);
return Array.from(arr, dec2hex).join("");
};
const download = (url: string, filename: string): void => {
let elm = document.createElement("a");
elm.href = url;
elm.setAttribute("download", filename);
elm.click();
document.body.append(elm);
};
const encrypt = async (fileBase64: string): Promise<string> => {
const key = generateId();
await getRSAEncrypted(key);
const aesEncryptedPayload = await getAESEncrypted(fileBase64, key);
download(createTextFile(aesEncryptedPayload), 'ciphered_file_contents.txt');
return aesEncryptedPayload;
};
const createTextFile = (text : string) : string => {
const data = new Blob([text], {type: 'text/plain'});
const textFile = window.URL.createObjectURL(data);
return textFile;
};
const App: FC = () => {
const [uploadedFileName, setUploadedFileName] = useState<string>();
const [uploadedFileBase64, setUploadedFileBase64] = useState<string>();
useEffect(() => {
if (!(uploadedFileBase64 && uploadedFileName)) return;
(async function () {
await encrypt(uploadedFileBase64.split(",")[1]);
})();
}, [uploadedFileName, uploadedFileBase64]);
return (
<main>
<FileInput
setUploadedFileName={setUploadedFileName}
setUploadedFileBase64={setUploadedFileBase64}
/>
</main>
);
};
export default App;
base64 --decode cipher_to_retrieve_aes_password.txt > cipher_to_retrieve_aes_password_binary.txt
openssl rsautl -decrypt -oaep -inkey private_key.pem -in cipher_to_retrieve_aes_password_binary.txt -out key_to_decrypt_aes.txt
RANDOM_KEY=$(cat key_to_decrypt_aes.txt)
base64 --decode ciphered_file_contents.txt > ciphered_file_contents_base64_decoded.txt
openssl enc -d -aes-256-cbc -in ciphered_file_contents_base64_decoded.txt -k ${RANDOM_KEY} -md MD5 -out original_file_contents.txt
base64 --decode original_file_contents.txt > original_file_contents_base64_decoded.txt
read
Display the source blob
Display the rendered blob
Raw
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
import { FileInputProps } from "./FileInput.types";
import { useRef, FC } from "react";
const fileToBase64 = (file: File): Promise<string> => {
return new Promise<string>((resolve, reject) => {
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = () => resolve(reader.result?.toString() || "");
reader.onerror = (error) => reject(error);
});
};
const FileInput: FC<FileInputProps> = ({
setUploadedFileName,
setUploadedFileBase64,
}) => {
const inputElement = useRef<HTMLInputElement>(null);
return (
<section>
<input
ref={inputElement}
onChange={async (e) => {
if (!e.target?.files?.[0]) {
return;
}
const _fileSizeInMb = (
e.target.files[0].size /
(1024 * 1024)
).toFixed(2);
if (+_fileSizeInMb > 10) {
alert("File is larger than 10 MB");
e.target.value = "";
if (!/safari/i.test(navigator.userAgent)) {
e.target.type = "";
e.target.type = "file";
}
return;
}
setUploadedFileName(e.target.files[0].name);
setUploadedFileBase64(await fileToBase64(e.target.files[0]));
}}
type="file"
/>
</section>
);
};
export default FileInput;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment