RSA Encryption with OAEP between Java and JavaScript - javascript

I am trying to encrypt a short string in JavaScript and decrypt it in Java. The decryption fails, and I think it's because of a difference in the block mode and/or padding between the two platforms. I tried encrypting the same string in both Java and JavaScript, and got different results, which indicates that there is indeed a difference. Here is the Java code that creates the key:
KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
kpg.initialize(1024);
KeyPair keyPair = kpg.generateKeyPair();
And here is the Java code that I used to test the encryption:
Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-256AndMGF1Padding");
cipher.init(Cipher.ENCRYPT_MODE, keyPair.getPublic());
byte[] bytes = cipher.doFinal("asdf".getBytes());
I send the public key across to the JavaScript process, and convert it to an ArrayBuffer, with the variable name publicKey. I have verified that the key on the JavaScript side matches the key on the Java side (by exporting it with crypto.subtle.exportKey and examining the bytes). Here is the JavaScript code that I used to test the encryption:
crypto.subtle.importKey('spki', publicKey,
{hash: 'SHA-256', name: 'RSA-OAEP'}, true,
['encrypt'])
.then((key) => {
crypto.subtle.encrypt({name: 'RSA-OAEP'}, key,
new TextEncoder().encode('asdf'))
.then((buffer) => {
});
});
The contents of the byte array in Java and the array buffer in JavaScript are not the same. The settings that I am unsure of are the parameter to Cipher#getInstance on the Java side and the parameters to importKey and encrypt on the JavaScript side. Are there any settings that will work between Java and JavaScript using the built-in classes? Or should I look at third-party libraries (e.g., Bouncy Castle)?

This is old, but here's an alternate solution if you want to use subtle crypto in javascript, and have control over the java decryption.
Here's how you decrypt in Java assuming you used the original JS code in the question to encrypt:
Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPPadding");
OAEPParameterSpec oaepParams = new OAEPParameterSpec("SHA-256", "MGF1", new MGF1ParameterSpec("SHA-256"), PSource.PSpecified.DEFAULT);
cipher.init(Cipher.DECRYPT_MODE, privKey, oaepParams);
byte[] decrypted = cipher.doFinal(encryptedBytes)
The issue with the Cipher RSA/ECB/OAEPWithSHA-256AndMGF1Padding is that it uses SHA-1 for the MGF1 Padding by default. Javascript uses SHA-256, which causes the mismatch. By specifying the MGF1ParamterSpec, we can force Java to use the same hashing algorithm as Javascript default.

In addition to #Chip's answer - which was really helpful - I would like to add the following case:
Assume you want to use the following for decryption in Javascript (webcrypto):
window.crypto.subtle.decrypt(
{
name: "RSA-OAEP",
hash: { name: "SHA-512" }
//label: Uint8Array([...]) //optional
},
privateRsaKey, //CryptoKey object containing private RSA key
encdata //ArrayBuffer containing to be decrypted data
)
.catch(function(err){
console.error(err);
})
Then you have to use the following OAEPParameterSpec for encryption in Java (and likely vice versa but I did not try that):
OAEPParameterSpec oaepParams = new OAEPParameterSpec("SHA-512", "MGF1",
new MGF1ParameterSpec("SHA-512"), PSource.PSpecified.DEFAULT);
Since #Chip referred to the MGF1 Padding I only, I assumed one would have to use
OAEPParameterSpec oaepParams = new OAEPParameterSpec("SHA-512", "MGF1",
new MGF1ParameterSpec("SHA-256"), PSource.PSpecified.DEFAULT);
but apparently one has to change both hash functions to SHA-512 as shown in my first OAEPParameterSpec code block.

It looks as though the built-in encryption/decryption in JavaScript and Java do not have compatible settings for RSA encryption. A viable solution appears to be the forge library from github (forge on github). The key settings are described on the github page as follows (RSA examples):
// encrypt data with a public key using RSAES-OAEP/SHA-256/MGF1-SHA-1
// compatible with Java's RSA/ECB/OAEPWithSHA-256AndMGF1Padding
var encrypted = publicKey.encrypt(bytes, 'RSA-OAEP', {
md: forge.md.sha256.create(),
mgf1: {
md: forge.md.sha1.create()
}
});

Related

Java Decrypt with NodeJS Encrypt <RSA_PKCS1_OAEP_PADDING> padding and <sha256> oaepHash

I need to decrypt some information coming from an external service built with NodeJS.
This service ask for a RSA (2048) public key in pem format in base64, in order to encrypt the information.
I am creating the key pair in Java:
KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
kpg.initialize(2048);
KeyPair kp = kpg.generateKeyPair();
PublicKey pubkey = kp.getPublic();
PrivateKey privkey = kp.getPrivate();
String pemPublicString = "-----BEGIN PUBLIC KEY-----\n";
pemPublicString = pemPublicString+Base64.getEncoder().encodeToString(pubkey.getEncoded())+"\n";
pemPublicString = pemPublicString+"-----END PUBLIC KEY-----\n";
String pemPrivateString = "-----BEGIN RSA PRIVATE KEY-----\n";
pemPrivateString = pemPrivateString+Base64.getEncoder().encodeToString(privkey.getEncoded())+"\n";
pemPrivateString = pemPrivateString+"-----END RSA PRIVATE KEY-----\n";
//Send to node js service
String base64publickey = Base64.getEncoder().encodeToString(pemPublicString.getBytes());
//Store for decrypting
String base64privatekey = Base64.getEncoder().encodeToString(pemPrivateString.getBytes());
The external service is encrypting the information as follows and is returning the bytes:
crypto.publicEncrypt(
{
key: publicKey,
padding: crypto.constants.RSA_PKCS1_OAEP_PADDING,
oaepHash: "sha256",
},
dataToEncrypt
);
I am trying to decrypt the information in Java as follows:
public String decrypt(String payload, String privateKey){
byte [] ciphertext = payload.getBytes(StandardCharsets.UTF_8);
Cipher oaepFromInit = Cipher.getInstance("RSA/ECB/OAEPPadding");
OAEPParameterSpec oaepParams = new OAEPParameterSpec("SHA-256", "MGF1", new
MGF1ParameterSpec("SHA-1"), PSource.PSpecified.DEFAULT);
oaepFromInit.init(Cipher.DECRYPT_MODE, getRSAPrivateKeyFrom(privateKey), oaepParams);
byte[] pt = oaepFromInit.doFinal(ciphertext);
return new String(pt, StandardCharsets.UTF_8);
}
private PrivateKey getRSAPrivateKeyFrom(String content) {
byte[] decodedBytes = Base64.getDecoder().decode(content);
String decodedString = new String(decodedBytes);
Security.addProvider(new BouncyCastleProvider());
PEMParser pemParser = new PEMParser(new StringReader(decodedString));
JcaPEMKeyConverter converter = new JcaPEMKeyConverter().setProvider("BC");
Object object = pemParser.readObject();
PrivateKey k = converter.getPrivateKey((PrivateKeyInfo) object);
return k;
}
Now I am getting a BadPadding Exception, any idea of what could be the problem?
Thanks in advance.
The posted code does not show how the NodeJS code exports the ciphertext. The following line in decrypt() of the Java code:
byte[] ciphertext = payload.getBytes(StandardCharsets.UTF_8);
implies that you have used a Utf-8 encoding. This is a common mistake that corrupts the ciphertext (see here). Instead of Utf-8, apply a binary-to-text enncoding, such as Base64.
The export of the ciphertext would then be done in NodeJS with:
var chiphertextBase64 = ciphertext.toString('base64');
and the import in Java with:
import java.util.Base64;
...
byte[] ciphertext = Base64.getDecoder().decode(payload);
In the NodeJS code, OAEP (RSAES-OAEP) is specified as padding. crypto.publicEncrypt() applies with the parameter oaepHash the same digest for both, the OAEP and MGF1 digest. oaepHash: "sha256" thus specifies SHA256 for both digests.
In contrast, Java allows separate (and different) specification of OAEP and MGF1 digests. While SHA256 is used for the OAEP digest in decrypt(), SHA1 is applied for the MGF1 digest. The latter is inconsistent with the NodeJS code and needs to be changed to SHA256:
OAEPParameterSpec oaepParams = new OAEPParameterSpec("SHA-256", "MGF1", new MGF1ParameterSpec("SHA-256"), PSource.PSpecified.DEFAULT);
This bug was already suspected in the 1st comment by Maarten Bodewes.
In the posted code, the PEM encoded pemPublicString and pemPrivateString keys are Base64 encoded as base64publickey and base64privatekey respectively. This is not necessary because the body of a PEM encoded key is already Base64 encoded and header and footer consist of ASCII characters. A second Base64 encoding thus brings no advantage, but the disadvantage that the key data is unnecessarily enlarged (Base64 overhead: 33%, see here). On the other hand, if the service expects a double Base64 encoded public key, you must comply.
When generating keys, the posted Java code implicitly uses the PKCS#8 format for the private key. You can verify this with privkey.getFormat(), which will output PKCS#8. The associated header for PKCS#8 is -----BEGIN PRIVATE KEY----- and Footer -----END PRIVATE KEY-----. Both the header and footer you are currently using belong to a PEM encoded PKCS#1 private key and are thus inconsistent with the key data. This issue has already been addressed in the 2nd comment by Maarten Bodewes.
By the way, a PKCS#8 key can easily be imported without BouncyCastle using PKCS8EncodedKeySpec. For this only header, footer and line breaks have to be removed and the rest has to be Base64 decoded (DER encoding), s. e.g. here.

How do I encrypt and decrypt a string using a function in Javascript [duplicate]

I'm interested in building a small app for personal use that will encrypt and decrypt information on the client side using JavaScript. The encrypted information will be stored in a database on a server, but never the decrypted version.
It doesn't have to be super duper secure, but I would like to use a currently unbroken algorithm.
Ideally I'd be able to do something like
var gibberish = encrypt(string, salt, key);
to generate the encoded string, and something like
var sensical = decrypt(gibberish, key);
to decode it later.
So far I've seen this:
http://bitwiseshiftleft.github.io/sjcl/
Any other libraries I should look at?
var encrypted = CryptoJS.AES.encrypt("Message", "Secret Passphrase");
//U2FsdGVkX18ZUVvShFSES21qHsQEqZXMxQ9zgHy+bu0=
var decrypted = CryptoJS.AES.decrypt(encrypted, "Secret Passphrase");
//4d657373616765
document.getElementById("demo1").innerHTML = encrypted;
document.getElementById("demo2").innerHTML = decrypted;
document.getElementById("demo3").innerHTML = decrypted.toString(CryptoJS.enc.Utf8);
Full working sample actually is:
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.2/rollups/aes.js" integrity="sha256-/H4YS+7aYb9kJ5OKhFYPUjSJdrtV6AeyJOtTkw6X72o=" crossorigin="anonymous"></script>
<br><br>
<label>encrypted</label>
<div id="demo1"></div>
<br>
<label>decrypted</label>
<div id="demo2"></div>
<br>
<label>Actual Message</label>
<div id="demo3"></div>
How about CryptoJS?
It's a solid crypto library, with a lot of functionality. It implements hashers, HMAC, PBKDF2 and ciphers. In this case ciphers is what you need. Check out the quick-start quide on the project's homepage.
You could do something like with the AES:
<script src="http://crypto-js.googlecode.com/svn/tags/3.1.2/build/rollups/aes.js"></script>
<script>
var encryptedAES = CryptoJS.AES.encrypt("Message", "My Secret Passphrase");
var decryptedBytes = CryptoJS.AES.decrypt(encryptedAES, "My Secret Passphrase");
var plaintext = decryptedBytes.toString(CryptoJS.enc.Utf8);
</script>
As for security, at the moment of my writing AES algorithm is thought to be unbroken
Edit :
Seems online URL is down & you can use the downloaded files for encryption from below given link & place the respective files in your root folder of the application.
https://code.google.com/archive/p/crypto-js/downloads
or used other CDN like https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.2/components/aes-min.js
I created an insecure but simple text cipher/decipher utility. No dependencies with any external library.
These are the functions:
const cipher = salt => {
const textToChars = text => text.split('').map(c => c.charCodeAt(0));
const byteHex = n => ("0" + Number(n).toString(16)).substr(-2);
const applySaltToChar = code => textToChars(salt).reduce((a,b) => a ^ b, code);
return text => text.split('')
.map(textToChars)
.map(applySaltToChar)
.map(byteHex)
.join('');
}
const decipher = salt => {
const textToChars = text => text.split('').map(c => c.charCodeAt(0));
const applySaltToChar = code => textToChars(salt).reduce((a,b) => a ^ b, code);
return encoded => encoded.match(/.{1,2}/g)
.map(hex => parseInt(hex, 16))
.map(applySaltToChar)
.map(charCode => String.fromCharCode(charCode))
.join('');
}
// To create a cipher
const myCipher = cipher('mySecretSalt')
//Then cipher any text:
console.log(myCipher('the secret string'))
//To decipher, you need to create a decipher and use it:
const myDecipher = decipher('mySecretSalt')
console.log(myDecipher("7c606d287b6d6b7a6d7c287b7c7a61666f"))
This code is based on #Jorgeblom's answer above.
#Jorgeblom my man, that's fantastic small crypto lib :D
I touch it a bit because I didn't like that I have to assign the salt and to call it again but in general, for my needs is absolutely perfect.
const crypt = (salt, text) => {
const textToChars = (text) => text.split("").map((c) => c.charCodeAt(0));
const byteHex = (n) => ("0" + Number(n).toString(16)).substr(-2);
const applySaltToChar = (code) => textToChars(salt).reduce((a, b) => a ^ b, code);
return text
.split("")
.map(textToChars)
.map(applySaltToChar)
.map(byteHex)
.join("");
};
const decrypt = (salt, encoded) => {
const textToChars = (text) => text.split("").map((c) => c.charCodeAt(0));
const applySaltToChar = (code) => textToChars(salt).reduce((a, b) => a ^ b, code);
return encoded
.match(/.{1,2}/g)
.map((hex) => parseInt(hex, 16))
.map(applySaltToChar)
.map((charCode) => String.fromCharCode(charCode))
.join("");
};
And you use it
// encrypting
const encrypted_text = crypt("salt", "Hello"); // -> 426f666665
// decrypting
const decrypted_string = decrypt("salt", "426f666665"); // -> Hello
The existing answers which leverage SJCL, CryptoJS, and/or WebCrypto aren't necessarily wrong but they're not as safe as you might initially suspect. Generally you want to use libsodium. First I'll explain why, then how.
Why Not SJCL, CryptoJS, WebCrypto, etc.?
Short answer: In order for your encryption to actually be secure, these libraries expect you to make too many choices e.g. the block cipher mode (CBC, CTR, GCM; if you can't tell which of the three I just listed is secure to use and under what constraints, you shouldn't be burdened with this sort of choice at all).
Unless your job title is cryptography engineer, the odds are stacked against you implementing it securely.
Why to Avoid CryptoJS?
CryptoJS offers a handful of building blocks and expects you to know how to use them securely. It even defaults to CBC mode (archived).
Why is CBC mode bad?
Read this write-up on AES-CBC vulnerabilities.
Why to Avoid WebCrypto?
WebCrypto is a potluck standard, designed by committee, for purposes that are orthogonal to cryptography engineering. Specifically, WebCrypto was meant to replace Flash, not provide security.
Why to Avoid SJCL?
SJCL's public API and documentation begs users to encrypt data with a human-remembered password. This is rarely, if ever, what you want to do in the real world.
Additionally: Its default PBKDF2 round count is roughly 86 times as small as you want it to be. AES-128-CCM is probably fine.
Out of the three options above, SJCL is the least likely to end in tears. But there are better options available.
Why is Libsodium Better?
You don't need to choose between a menu of cipher modes, hash functions, and other needless options. You'll never risk screwing up your parameters and removing all security from your protocol.
Instead, libsodium just gives you simple options tuned for maximum security and minimalistic APIs.
crypto_box() / crypto_box_open() offer authenticated public-key encryption.
The algorithm in question combines X25519 (ECDH over Curve25519) and XSalsa20-Poly1305, but you don't need to know (or even care) about that to use it securely
crypto_secretbox() / crypto_secretbox_open() offer shared-key authenticated encryption.
The algorithm in question is XSalsa20-Poly1305, but you don't need to know/care
Additionally, libsodium has bindings in dozens of popular programming languages, so it's very likely that libsodium will just work when trying to interoperate with another programming stack. Also, libsodium tends to be very fast without sacrificing security.
How to Use Libsodium in JavaScript?
First, you need to decide one thing:
Do you just want to encrypt/decrypt data (and maybe still somehow use the plaintext in database queries securely) and not worry about the details? Or...
Do you need to implement a specific protocol?
If you selected the first option, get CipherSweet.js.
The documentation is available online. EncryptedField is sufficient for most use cases, but the EncryptedRow and EncryptedMultiRows APIs may be easier if you have a lot of distinct fields you want to encrypt.
With CipherSweet, you don't need to even know what a nonce/IV is to use it securely.
Additionally, this handles int/float encryption without leaking facts about the contents through ciphertext size.
Otherwise, you'll want sodium-plus, which is a user-friendly frontend to various libsodium wrappers. Sodium-Plus allows you to write performant, asynchronous, cross-platform code that's easy to audit and reason about.
To install sodium-plus, simply run...
npm install sodium-plus
There is currently no public CDN for browser support. This will change soon. However, you can grab sodium-plus.min.js from the latest Github release if you need it.
const { SodiumPlus } = require('sodium-plus');
let sodium;
(async function () {
if (!sodium) sodium = await SodiumPlus.auto();
let plaintext = 'Your message goes here';
let key = await sodium.crypto_secretbox_keygen();
let nonce = await sodium.randombytes_buf(24);
let ciphertext = await sodium.crypto_secretbox(
plaintext,
nonce,
key
);
console.log(ciphertext.toString('hex'));
let decrypted = await sodium.crypto_secretbox_open(
ciphertext,
nonce,
key
);
console.log(decrypted.toString());
})();
The documentation for sodium-plus is available on Github.
If you'd like a step-by-step tutorial, this dev.to article has what you're looking for.
Modern browsers now support the crypto.subtle API, which provides native encryption and decryption functions (async no less!) using one of these method: AES-CBC, AES-CTR, AES-GCM, or RSA-OAEP.
https://www.w3.org/TR/WebCryptoAPI/#dfn-Crypto
Before implementying any of this, please see Scott Arciszewski's answer.
I want you to be very careful with what I'm about to share as I have little to no security knowledge (There's a high chance that I'm misusing the API below), so I'd be more than welcome to update this answer with the help of the community.
As #richardtallent mentioned in his answer, there's support for the Web Crypto API, so this example uses the standard. As of this writing, there's a 95.88% of global browser support.
I'm going to be sharing an example using the Web Crypto API
Before we proceed, please note (Quoting from MDN):
This API provides a number of low-level cryptographic primitives. It's very easy to misuse them, and the pitfalls involved can be very subtle.
Even assuming you use the basic cryptographic functions correctly, secure key management and overall security system design are extremely hard to get right and are generally the domain of specialist security experts.
Errors in security system design and implementation can make the security of the system completely ineffective.
If you're not sure you know what you are doing, you probably shouldn't be using this API.
I respect security a lot, and I even bolded additional parts from MDN... You've been warned
Now, to the actual example...
JSFiddle:
Found here: https://jsfiddle.net/superjose/rm4e0gqa/5/
Note:
Note the use of await keywords. Use it inside an async function or use .then() and .catch().
Generate the key:
// https://developer.mozilla.org/en-US/docs/Web/API/CryptoKey
// https://developer.mozilla.org/en-US/docs/Web/API/RsaHashedKeyGenParams
// https://github.com/diafygi/webcrypto-examples#rsa-oaep---generatekey
const stringToEncrypt = 'https://localhost:3001';
// https://github.com/diafygi/webcrypto-examples#rsa-oaep---generatekey
// The resultant publicKey will be used to encrypt
// and the privateKey will be used to decrypt.
// Note: This will generate new keys each time, you must store both of them in order for
// you to keep encrypting and decrypting.
//
// I warn you that storing them in the localStorage may be a bad idea, and it gets out of the scope
// of this post.
const key = await crypto.subtle.generateKey({
name: 'RSA-OAEP',
modulusLength: 4096,
publicExponent: new Uint8Array([0x01, 0x00, 0x01]),
hash: {name: 'SHA-512'},
}, true,
// This depends a lot on the algorithm used
// Go to https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto
// and scroll down to see the table. Since we're using RSA-OAEP we have encrypt and decrypt available
['encrypt', 'decrypt']);
// key will yield a key.publicKey and key.privateKey property.
Encrypt:
const encryptedUri = await crypto.subtle.encrypt({
name: 'RSA-OAEP'
}, key.publicKey, stringToArrayBuffer(stringToEncrypt))
console.log('The encrypted string is', encryptedUri);
Decrypt
const msg = await crypto.subtle.decrypt({
name: 'RSA-OAEP',
}, key.privateKey, encryptedUri);
console.log(`Derypted Uri is ${arrayBufferToString(msg)}`)
Converting ArrayBuffer back and forth from String (Done in TypeScript):
private arrayBufferToString(buff: ArrayBuffer) {
return String.fromCharCode.apply(null, new Uint16Array(buff) as unknown as number[]);
}
private stringToArrayBuffer(str: string) {
const buff = new ArrayBuffer(str.length*2) // Because there are 2 bytes for each char.
const buffView = new Uint16Array(buff);
for(let i = 0, strLen = str.length; i < strLen; i++) {
buffView[i] = str.charCodeAt(i);
}
return buff;
}
You can find more examples here (I'm not the owner):
// https://github.com/diafygi/webcrypto-examples
crypt.subtle AES-GCM, self-contained, tested:
async function aesGcmEncrypt(plaintext, password)
async function aesGcmDecrypt(ciphertext, password)
https://gist.github.com/chrisveness/43bcda93af9f646d083fad678071b90a
you can use those function it's so easy the First one for encryption so you just call the function and send the text you wanna encrypt it and take the result from encryptWithAES function and send it to decrypt Function like this:
const CryptoJS = require("crypto-js");
//The Function Below To Encrypt Text
const encryptWithAES = (text) => {
const passphrase = "My Secret Passphrase";
return CryptoJS.AES.encrypt(text, passphrase).toString();
};
//The Function Below To Decrypt Text
const decryptWithAES = (ciphertext) => {
const passphrase = "My Secret Passphrase";
const bytes = CryptoJS.AES.decrypt(ciphertext, passphrase);
const originalText = bytes.toString(CryptoJS.enc.Utf8);
return originalText;
};
let encryptText = encryptWithAES("YAZAN");
//EncryptedText==> //U2FsdGVkX19GgWeS66m0xxRUVxfpI60uVkWRedyU15I=
let decryptText = decryptWithAES(encryptText);
//decryptText==> //YAZAN
CryptoJS is no longer supported. If you want to continue using it, you may switch to this url:
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.2/rollups/aes.js"></script>
UPDATE December 2021
use crypto api provided by MDN Web Docs:
https://developer.mozilla.org/en-US/docs/Web/API/Crypto
Old answer
Use SimpleCrypto
Using encrypt() and decrypt()
To use SimpleCrypto, first create a SimpleCrypto instance with a
secret key (password). Secret key parameter MUST be defined when
creating a SimpleCrypto instance.
To encrypt and decrypt data, simply use encrypt() and decrypt()
function from an instance. This will use AES-CBC encryption algorithm.
var _secretKey = "some-unique-key";
var simpleCrypto = new SimpleCrypto(_secretKey);
var plainText = "Hello World!";
var chiperText = simpleCrypto.encrypt(plainText);
console.log("Encryption process...");
console.log("Plain Text : " + plainText);
console.log("Cipher Text : " + cipherText);
var decipherText = simpleCrypto.decrypt(cipherText);
console.log("... and then decryption...");
console.log("Decipher Text : " + decipherText);
console.log("... done.");
Simple functions:
function Encrypt(value) 
{
var result="";
  for(i=0;i<value.length;i++)
  {
if(i<value.length-1)
{
result+=value.charCodeAt(i)+10;
result+="-";
}
else
{
result+=value.charCodeAt(i)+10;
}
  }
  return result;
}
function Decrypt(value)
{
var result="";
var array = value.split("-");
  for(i=0;i<array.length;i++)
  {
    result+=String.fromCharCode(array[i]-10);
  }
  return result;
}

AES Encryption/Decryption in Node JS similar to Java

I am trying to replicate the Java code for AES Encryption and Decryption in Node JS.
Java Code
SecretKeySpec skeySpec;
String key = "a4e1112f45e84f785358bb86ba750f48";
public void encryptString(String key) throws Exception {
try {
skeySpec = new SecretKeySpec(key.getBytes(), "AES");
cipher = Cipher.getInstance("AES");
cipher.init(1, skeySpec);
byte encstr[] = cipher.doFinal(message.getBytes());
String encData = new String(encstr, "UTF-8");
System.out.println(encData);
} catch (NoSuchAlgorithmException nsae) {
throw new Exception("Invalid Java Version");
} catch (NoSuchPaddingException nse) {
throw new Exception("Invalid Key");
}
}
Node JS
var encryptKey = function (text) {
var cipher = crypto.createCipher('aes256', 'a4e1112f45e84f785358bb86ba750f48');
var crypted = cipher.update(text,'utf8', 'hex')
crypted += cipher.final('hex');
console.log(crypted);
return crypted;
}
I am unable to get the exact cipher-text in Node JS, which i am getting in Java.
Your code actually uses different encryption parameters in the 2 cases. AES, being a block cipher, takes: the plain text to encrypt, the initialization vector, also called IV (which is used in conjunction with the plaintext), and the encryption key.
In Java, the IV is, apparently, generated automatically on init() - from the Java SE platform docs for Cipher.init:
The generated parameters can be retrieved using getParameters or getIV
(if the parameter is an IV).
In Node.js, if using the deprecated createCipher function, the IV is generated automatically based on the provided key, probably in a different way than in Java, so you will get a different cipher text. However, you should be using the non-deprecated variant crypto.createCipheriv: https://nodejs.org/docs/latest-v12.x/api/crypto.html#crypto_crypto_createcipheriv_algorithm_key_iv_options
In order to exactly reproduce the cipher text, you should:
Use the same encryption algorithm on both sides - it's best to specify this exactly, for example aes-256-cbc, or an authenticated encryption scheme such as aes-256-gcm, which is harder to use but provides message authentication.
Use the same IV on both sides, by providing it in the initialization params in Java, and by using createCipheriv in Node; though beware, you should always randomize it in production! See https://stackoverflow.com/a/20888967/6098312
As a closing remark, when using block encryption, you'll usually be generating securely-random IVs, which means the ciphertexts will always differ from one another, even for the same plaintext. This is a good thing! It protects your payload from an attacker who observes the encrypted data and makes conclusions based on message repetitions.
Finally after reviewing Java Docs and Node JS Crypto Docs managed to get the result.
We have to use crypto.createCipheriv() instead of crypto.createCipher with a iv.
Here iv will be null.
Code :
let crypto = require('crypto');
var iv = new Buffer.from(''); //(null) iv
var algorithm = 'aes-256-ecb';
var password = 'a4e1112f45e84f785358bb86ba750f48'; //key password for cryptography
function encrypt(buffer){
var cipher = crypto.createCipheriv(algorithm,new Buffer(password),iv)
var crypted = Buffer.concat([cipher.update(buffer),cipher.final()]);
return crypted;
}
console.log(encrypt(new Buffer('TextToEncrypt')).toString())

Javascript generate AES using cap and small cap letters [duplicate]

I'm interested in building a small app for personal use that will encrypt and decrypt information on the client side using JavaScript. The encrypted information will be stored in a database on a server, but never the decrypted version.
It doesn't have to be super duper secure, but I would like to use a currently unbroken algorithm.
Ideally I'd be able to do something like
var gibberish = encrypt(string, salt, key);
to generate the encoded string, and something like
var sensical = decrypt(gibberish, key);
to decode it later.
So far I've seen this:
http://bitwiseshiftleft.github.io/sjcl/
Any other libraries I should look at?
var encrypted = CryptoJS.AES.encrypt("Message", "Secret Passphrase");
//U2FsdGVkX18ZUVvShFSES21qHsQEqZXMxQ9zgHy+bu0=
var decrypted = CryptoJS.AES.decrypt(encrypted, "Secret Passphrase");
//4d657373616765
document.getElementById("demo1").innerHTML = encrypted;
document.getElementById("demo2").innerHTML = decrypted;
document.getElementById("demo3").innerHTML = decrypted.toString(CryptoJS.enc.Utf8);
Full working sample actually is:
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.2/rollups/aes.js" integrity="sha256-/H4YS+7aYb9kJ5OKhFYPUjSJdrtV6AeyJOtTkw6X72o=" crossorigin="anonymous"></script>
<br><br>
<label>encrypted</label>
<div id="demo1"></div>
<br>
<label>decrypted</label>
<div id="demo2"></div>
<br>
<label>Actual Message</label>
<div id="demo3"></div>
How about CryptoJS?
It's a solid crypto library, with a lot of functionality. It implements hashers, HMAC, PBKDF2 and ciphers. In this case ciphers is what you need. Check out the quick-start quide on the project's homepage.
You could do something like with the AES:
<script src="http://crypto-js.googlecode.com/svn/tags/3.1.2/build/rollups/aes.js"></script>
<script>
var encryptedAES = CryptoJS.AES.encrypt("Message", "My Secret Passphrase");
var decryptedBytes = CryptoJS.AES.decrypt(encryptedAES, "My Secret Passphrase");
var plaintext = decryptedBytes.toString(CryptoJS.enc.Utf8);
</script>
As for security, at the moment of my writing AES algorithm is thought to be unbroken
Edit :
Seems online URL is down & you can use the downloaded files for encryption from below given link & place the respective files in your root folder of the application.
https://code.google.com/archive/p/crypto-js/downloads
or used other CDN like https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.2/components/aes-min.js
I created an insecure but simple text cipher/decipher utility. No dependencies with any external library.
These are the functions:
const cipher = salt => {
const textToChars = text => text.split('').map(c => c.charCodeAt(0));
const byteHex = n => ("0" + Number(n).toString(16)).substr(-2);
const applySaltToChar = code => textToChars(salt).reduce((a,b) => a ^ b, code);
return text => text.split('')
.map(textToChars)
.map(applySaltToChar)
.map(byteHex)
.join('');
}
const decipher = salt => {
const textToChars = text => text.split('').map(c => c.charCodeAt(0));
const applySaltToChar = code => textToChars(salt).reduce((a,b) => a ^ b, code);
return encoded => encoded.match(/.{1,2}/g)
.map(hex => parseInt(hex, 16))
.map(applySaltToChar)
.map(charCode => String.fromCharCode(charCode))
.join('');
}
// To create a cipher
const myCipher = cipher('mySecretSalt')
//Then cipher any text:
console.log(myCipher('the secret string'))
//To decipher, you need to create a decipher and use it:
const myDecipher = decipher('mySecretSalt')
console.log(myDecipher("7c606d287b6d6b7a6d7c287b7c7a61666f"))
This code is based on #Jorgeblom's answer above.
#Jorgeblom my man, that's fantastic small crypto lib :D
I touch it a bit because I didn't like that I have to assign the salt and to call it again but in general, for my needs is absolutely perfect.
const crypt = (salt, text) => {
const textToChars = (text) => text.split("").map((c) => c.charCodeAt(0));
const byteHex = (n) => ("0" + Number(n).toString(16)).substr(-2);
const applySaltToChar = (code) => textToChars(salt).reduce((a, b) => a ^ b, code);
return text
.split("")
.map(textToChars)
.map(applySaltToChar)
.map(byteHex)
.join("");
};
const decrypt = (salt, encoded) => {
const textToChars = (text) => text.split("").map((c) => c.charCodeAt(0));
const applySaltToChar = (code) => textToChars(salt).reduce((a, b) => a ^ b, code);
return encoded
.match(/.{1,2}/g)
.map((hex) => parseInt(hex, 16))
.map(applySaltToChar)
.map((charCode) => String.fromCharCode(charCode))
.join("");
};
And you use it
// encrypting
const encrypted_text = crypt("salt", "Hello"); // -> 426f666665
// decrypting
const decrypted_string = decrypt("salt", "426f666665"); // -> Hello
The existing answers which leverage SJCL, CryptoJS, and/or WebCrypto aren't necessarily wrong but they're not as safe as you might initially suspect. Generally you want to use libsodium. First I'll explain why, then how.
Why Not SJCL, CryptoJS, WebCrypto, etc.?
Short answer: In order for your encryption to actually be secure, these libraries expect you to make too many choices e.g. the block cipher mode (CBC, CTR, GCM; if you can't tell which of the three I just listed is secure to use and under what constraints, you shouldn't be burdened with this sort of choice at all).
Unless your job title is cryptography engineer, the odds are stacked against you implementing it securely.
Why to Avoid CryptoJS?
CryptoJS offers a handful of building blocks and expects you to know how to use them securely. It even defaults to CBC mode (archived).
Why is CBC mode bad?
Read this write-up on AES-CBC vulnerabilities.
Why to Avoid WebCrypto?
WebCrypto is a potluck standard, designed by committee, for purposes that are orthogonal to cryptography engineering. Specifically, WebCrypto was meant to replace Flash, not provide security.
Why to Avoid SJCL?
SJCL's public API and documentation begs users to encrypt data with a human-remembered password. This is rarely, if ever, what you want to do in the real world.
Additionally: Its default PBKDF2 round count is roughly 86 times as small as you want it to be. AES-128-CCM is probably fine.
Out of the three options above, SJCL is the least likely to end in tears. But there are better options available.
Why is Libsodium Better?
You don't need to choose between a menu of cipher modes, hash functions, and other needless options. You'll never risk screwing up your parameters and removing all security from your protocol.
Instead, libsodium just gives you simple options tuned for maximum security and minimalistic APIs.
crypto_box() / crypto_box_open() offer authenticated public-key encryption.
The algorithm in question combines X25519 (ECDH over Curve25519) and XSalsa20-Poly1305, but you don't need to know (or even care) about that to use it securely
crypto_secretbox() / crypto_secretbox_open() offer shared-key authenticated encryption.
The algorithm in question is XSalsa20-Poly1305, but you don't need to know/care
Additionally, libsodium has bindings in dozens of popular programming languages, so it's very likely that libsodium will just work when trying to interoperate with another programming stack. Also, libsodium tends to be very fast without sacrificing security.
How to Use Libsodium in JavaScript?
First, you need to decide one thing:
Do you just want to encrypt/decrypt data (and maybe still somehow use the plaintext in database queries securely) and not worry about the details? Or...
Do you need to implement a specific protocol?
If you selected the first option, get CipherSweet.js.
The documentation is available online. EncryptedField is sufficient for most use cases, but the EncryptedRow and EncryptedMultiRows APIs may be easier if you have a lot of distinct fields you want to encrypt.
With CipherSweet, you don't need to even know what a nonce/IV is to use it securely.
Additionally, this handles int/float encryption without leaking facts about the contents through ciphertext size.
Otherwise, you'll want sodium-plus, which is a user-friendly frontend to various libsodium wrappers. Sodium-Plus allows you to write performant, asynchronous, cross-platform code that's easy to audit and reason about.
To install sodium-plus, simply run...
npm install sodium-plus
There is currently no public CDN for browser support. This will change soon. However, you can grab sodium-plus.min.js from the latest Github release if you need it.
const { SodiumPlus } = require('sodium-plus');
let sodium;
(async function () {
if (!sodium) sodium = await SodiumPlus.auto();
let plaintext = 'Your message goes here';
let key = await sodium.crypto_secretbox_keygen();
let nonce = await sodium.randombytes_buf(24);
let ciphertext = await sodium.crypto_secretbox(
plaintext,
nonce,
key
);
console.log(ciphertext.toString('hex'));
let decrypted = await sodium.crypto_secretbox_open(
ciphertext,
nonce,
key
);
console.log(decrypted.toString());
})();
The documentation for sodium-plus is available on Github.
If you'd like a step-by-step tutorial, this dev.to article has what you're looking for.
Modern browsers now support the crypto.subtle API, which provides native encryption and decryption functions (async no less!) using one of these method: AES-CBC, AES-CTR, AES-GCM, or RSA-OAEP.
https://www.w3.org/TR/WebCryptoAPI/#dfn-Crypto
Before implementying any of this, please see Scott Arciszewski's answer.
I want you to be very careful with what I'm about to share as I have little to no security knowledge (There's a high chance that I'm misusing the API below), so I'd be more than welcome to update this answer with the help of the community.
As #richardtallent mentioned in his answer, there's support for the Web Crypto API, so this example uses the standard. As of this writing, there's a 95.88% of global browser support.
I'm going to be sharing an example using the Web Crypto API
Before we proceed, please note (Quoting from MDN):
This API provides a number of low-level cryptographic primitives. It's very easy to misuse them, and the pitfalls involved can be very subtle.
Even assuming you use the basic cryptographic functions correctly, secure key management and overall security system design are extremely hard to get right and are generally the domain of specialist security experts.
Errors in security system design and implementation can make the security of the system completely ineffective.
If you're not sure you know what you are doing, you probably shouldn't be using this API.
I respect security a lot, and I even bolded additional parts from MDN... You've been warned
Now, to the actual example...
JSFiddle:
Found here: https://jsfiddle.net/superjose/rm4e0gqa/5/
Note:
Note the use of await keywords. Use it inside an async function or use .then() and .catch().
Generate the key:
// https://developer.mozilla.org/en-US/docs/Web/API/CryptoKey
// https://developer.mozilla.org/en-US/docs/Web/API/RsaHashedKeyGenParams
// https://github.com/diafygi/webcrypto-examples#rsa-oaep---generatekey
const stringToEncrypt = 'https://localhost:3001';
// https://github.com/diafygi/webcrypto-examples#rsa-oaep---generatekey
// The resultant publicKey will be used to encrypt
// and the privateKey will be used to decrypt.
// Note: This will generate new keys each time, you must store both of them in order for
// you to keep encrypting and decrypting.
//
// I warn you that storing them in the localStorage may be a bad idea, and it gets out of the scope
// of this post.
const key = await crypto.subtle.generateKey({
name: 'RSA-OAEP',
modulusLength: 4096,
publicExponent: new Uint8Array([0x01, 0x00, 0x01]),
hash: {name: 'SHA-512'},
}, true,
// This depends a lot on the algorithm used
// Go to https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto
// and scroll down to see the table. Since we're using RSA-OAEP we have encrypt and decrypt available
['encrypt', 'decrypt']);
// key will yield a key.publicKey and key.privateKey property.
Encrypt:
const encryptedUri = await crypto.subtle.encrypt({
name: 'RSA-OAEP'
}, key.publicKey, stringToArrayBuffer(stringToEncrypt))
console.log('The encrypted string is', encryptedUri);
Decrypt
const msg = await crypto.subtle.decrypt({
name: 'RSA-OAEP',
}, key.privateKey, encryptedUri);
console.log(`Derypted Uri is ${arrayBufferToString(msg)}`)
Converting ArrayBuffer back and forth from String (Done in TypeScript):
private arrayBufferToString(buff: ArrayBuffer) {
return String.fromCharCode.apply(null, new Uint16Array(buff) as unknown as number[]);
}
private stringToArrayBuffer(str: string) {
const buff = new ArrayBuffer(str.length*2) // Because there are 2 bytes for each char.
const buffView = new Uint16Array(buff);
for(let i = 0, strLen = str.length; i < strLen; i++) {
buffView[i] = str.charCodeAt(i);
}
return buff;
}
You can find more examples here (I'm not the owner):
// https://github.com/diafygi/webcrypto-examples
crypt.subtle AES-GCM, self-contained, tested:
async function aesGcmEncrypt(plaintext, password)
async function aesGcmDecrypt(ciphertext, password)
https://gist.github.com/chrisveness/43bcda93af9f646d083fad678071b90a
you can use those function it's so easy the First one for encryption so you just call the function and send the text you wanna encrypt it and take the result from encryptWithAES function and send it to decrypt Function like this:
const CryptoJS = require("crypto-js");
//The Function Below To Encrypt Text
const encryptWithAES = (text) => {
const passphrase = "My Secret Passphrase";
return CryptoJS.AES.encrypt(text, passphrase).toString();
};
//The Function Below To Decrypt Text
const decryptWithAES = (ciphertext) => {
const passphrase = "My Secret Passphrase";
const bytes = CryptoJS.AES.decrypt(ciphertext, passphrase);
const originalText = bytes.toString(CryptoJS.enc.Utf8);
return originalText;
};
let encryptText = encryptWithAES("YAZAN");
//EncryptedText==> //U2FsdGVkX19GgWeS66m0xxRUVxfpI60uVkWRedyU15I=
let decryptText = decryptWithAES(encryptText);
//decryptText==> //YAZAN
CryptoJS is no longer supported. If you want to continue using it, you may switch to this url:
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.2/rollups/aes.js"></script>
UPDATE December 2021
use crypto api provided by MDN Web Docs:
https://developer.mozilla.org/en-US/docs/Web/API/Crypto
Old answer
Use SimpleCrypto
Using encrypt() and decrypt()
To use SimpleCrypto, first create a SimpleCrypto instance with a
secret key (password). Secret key parameter MUST be defined when
creating a SimpleCrypto instance.
To encrypt and decrypt data, simply use encrypt() and decrypt()
function from an instance. This will use AES-CBC encryption algorithm.
var _secretKey = "some-unique-key";
var simpleCrypto = new SimpleCrypto(_secretKey);
var plainText = "Hello World!";
var chiperText = simpleCrypto.encrypt(plainText);
console.log("Encryption process...");
console.log("Plain Text : " + plainText);
console.log("Cipher Text : " + cipherText);
var decipherText = simpleCrypto.decrypt(cipherText);
console.log("... and then decryption...");
console.log("Decipher Text : " + decipherText);
console.log("... done.");
Simple functions:
function Encrypt(value) 
{
var result="";
  for(i=0;i<value.length;i++)
  {
if(i<value.length-1)
{
result+=value.charCodeAt(i)+10;
result+="-";
}
else
{
result+=value.charCodeAt(i)+10;
}
  }
  return result;
}
function Decrypt(value)
{
var result="";
var array = value.split("-");
  for(i=0;i<array.length;i++)
  {
    result+=String.fromCharCode(array[i]-10);
  }
  return result;
}

nodejs crypto module privateEncrypt() always return same result

I am using RSA encryption with nodejs crypto module.
I want encrypt message with PRIVATE KEY and decrypt with PUBLIC KEY.
also always make different result with same message using padding scheme like encryption using public key.
So I used basic crypto module like below
var crypto = require('crypto');
var fs = require('fs');
const path = require('path');
var PRIVKEY = fs.readFileSync(path.join(__dirname, 'private.key'), 'utf8');
var PUBKEY = fs.readFileSync(path.join(__dirname, 'pub.key'), 'utf8');
// RSA PRIVATE ENCRYPT -> PUBLIC DECRYPT //
myMSG = "apple";
console.log('myMSG SIZE:', myMSG.length);
function privENC_pubDEC(originMSG){
encmsg = crypto.privateEncrypt(PRIVKEY, Buffer.from(originMSG, 'utf8') ).toString('base64');
msg = crypto.publicDecrypt(PUBKEY, Buffer.from(encmsg, 'base64'));
console.log("Encrypted with private key : "+encmsg);
console.log(msg.toString());
}
// RSA PUBLIC ENCRYPT -> PRVATE DECRYPT //
function pubENC_privDEC(originMSG){
encmsg = crypto.publicEncrypt({key:PUBKEY, padding:crypto.constants.RSA_PKCS1_PADDING}, Buffer.from(originMSG, 'utf8') ).toString('base64');
msg = crypto.privateDecrypt({key:PRIVKEY, padding:crypto.constants.RSA_PKCS1_PADDING}, Buffer.from(encmsg, 'base64'));
console.log("\nEncrypted with public key : "+encmsg);
console.log(msg.toString());
}
privENC_pubDEC(myMSG);
pubENC_privDEC(myMSG);
Result
C:\Users\LSW>node crypto.js
myMSG SIZE: 5
Encrypted with private key : fbUZwj+UZP92HQYRc+EJTqSztJTY/Sit5axPZ0NVBuDAC8ZwvvC96pxxDGpra4Yg8MjcXyjvnT8rrrgHu0T0wA==
apple
Encrypted with public key : ze+5TdWtR8hkpNPIVa5HSasOxs3Pr8FA/1/zUGqDUQmIhs/miWt5pgU9kIAiryKfgGa0+p9RfHPMwZ1VMSA7Bw==
apple
C:\Users\LSW>node crypto.js
myMSG SIZE: 5
Encrypted with private key : fbUZwj+UZP92HQYRc+EJTqSztJTY/Sit5axPZ0NVBuDAC8ZwvvC96pxxDGpra4Yg8MjcXyjvnT8rrrgHu0T0wA==
apple
Encrypted with public key : OdEpjloUDWI8+YjWkE5cmBC/fJL2QnRLKBXfjaP5h5qyB1OMcm9JGGNSTiAAL2u8O5jjdQAavB9Rn+cdRDjLyA==
apple
C:\Users\LSW>node crypto.js
myMSG SIZE: 5
Encrypted with private key : fbUZwj+UZP92HQYRc+EJTqSztJTY/Sit5axPZ0NVBuDAC8ZwvvC96pxxDGpra4Yg8MjcXyjvnT8rrrgHu0T0wA==
apple
Encrypted with public key : INspxkyFu2AWGVYwSvOGOPH1fhE3qVVxiqz+SmyHU8wTDNKHj4gVVHqO+8AZOJvi4NfyekI2MMwpFDU4mUjEXA==
apple
PUBLIC ENCRYPT -> PRVATE DECRYPT is operated well I expected. it always return different result because of padding scheme.
But PRIVATE ENCRYPT -> PUBLIC DECRYPT is always return same message although used padding scheme.
Is there any solution make it different message with Nodejs crypto module???
This is expected behavior according to the padding schemes for RSA signing and encryption as implemented by OpenSSL, which crypto leverages.
I am not sure what you want to use the functions privateEncrypt() and publicDecrypt() for. If your intent is to sign data, then see my update below. Anyway, for these functions, the crypto documentation explains that it only exposes RSA_PKCS1_PADDING which OpenSSL maps to the deterministic RSASSA-PKCS1-v1_5 padding scheme under the hood. This means that for the same key and the same data, the resulting data will be the same.
For encryption and decryption, with publicEncrypt() and privateDecrypt(), you have selected the RSA_PKCS1_PADDING mode. This translates to the RSAES-PKCS1-v1_5, a scheme that includes random elements, which cause the different outputs that you observe in your repeated runs. According to the documentation, crypto uses RSA_PKCS1_OAEP_PADDING padding by default. This stands for Optimal asymmetric encryption padding, which is non-deterministic as well.
For a summary of the PKCS#1-defined schemes, see PKCS#1 Schemes.
Update: You may want to use the Sign class instead of the privateEncrypt() and publicDecrypt() functions. Its sign() function does support a probabilistic padding mode, which OpenSSL supports via RSASSA-PSS. Using your example code as a starting point, it would look something like this:
const sign = crypto.createSign('SHA256')
sign.update(Buffer.from(originMSG, 'utf8'))
signature = sign.sign({key:PRIVKEY, padding:crypto.constants.RSA_PKCS1_PSS_PADDING}).toString('base64')
The signature will be different every time. Note that you can not "decrypt" it, it is a one-way operation. You can only verify it, using the public key with the Verify class:
const verify = crypto.createVerify('SHA256')
verify.update(Buffer.from(originMSG, 'utf8'))
verifyRes = verify.verify({key:PUBKEY, padding:crypto.constants.RSA_PKCS1_PSS_PADDING}, Buffer.from(signature, 'base64'))

Categories

Resources