Getting different encryption results - RSA - Java and Openssl - javascript

When I encrypt a string of text with Java and Openssl - I am unable to decrypt the cipher (using Javascript Crypto Subtle) got from Openssl, however Java cipher is decrypted (with Crypto Subtle Web API JS) succesfully. Could you please look into and advise where is the mistake?
Here are RSA Public and Private key examples in Base64 generated by JS Crypto Sublte API:
Public key (RSA-OAEP, monulus 2048, hash SHA-256):
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA8JhmO71HZ1vb8uxKhw6MM+ZvtTmc9Tw6AFpJVgXKiTjebj1SPdnxhdhJ5Bj15RN0rGNACXhAyUdn7zsp66/tmjNuC91L+9PvQBjDbXLsx7XUV9nIGJ3aYO5/qruVNXwyemf7wlwZVDF7oeZ8JUfjABTg7a7xui2WdXDHgvhTQdvQ9TS0NkX9xWAiDSn/HWfoEBC7TLeKeVjHsT7g1JnskGxfVhFrLfxQCxYZle4ebXP7dCPsff4WNNCxzBQtHHt8iEoZ0SVKBzS5zhdLHIdAIW4ELdnYsM7iTlWZO+kfWnlV2i13lAJobhxOAqwsg4OqkTsrx0KtWfZH40bNtFzx8wIDAQAB
Private Key (the same parameteres):
MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDwmGY7vUdnW9vy7EqHDowz5m+1OZz1PDoAWklWBcqJON5uPVI92fGF2EnkGPXlE3SsY0AJeEDJR2fvOynrr+2aM24L3Uv70+9AGMNtcuzHtdRX2cgYndpg7n+qu5U1fDJ6Z/vCXBlUMXuh5nwlR+MAFODtrvG6LZZ1cMeC+FNB29D1NLQ2Rf3FYCINKf8dZ+gQELtMt4p5WMexPuDUmeyQbF9WEWst/FALFhmV7h5tc/t0I+x9/hY00LHMFC0ce3yIShnRJUoHNLnOF0sch0AhbgQt2diwzuJOVZk76R9aeVXaLXeUAmhuHE4CrCyDg6qROyvHQq1Z9kfjRs20XPHzAgMBAAECggEAPtCjPGyeFFu00LclfB5tt20t9CQ/GP3o7MelxvpLF0mMNT74VmKs/rNqE037ARxzxUBCa1aEn6hvd9O8DadIgw5zaFCWMoDyQYtVlqE/NaMA9hDLf7XS2qTaGyLPgX/UFAZLAkMWb9ddfncVKYybtR0+Xn/i56dYVYAk7spTvmkai5Q4Li5eEJqrRNse5fg/wiwDda/cWlEvlvfziAjU51gS/Y9ItUT8Z1g8A2NYfqgFVgBVb0qrtbP76TWPbeLZr1FXTMDB0MO+zuC+1lq48+CYqSJqi9a4GlW2gi0wkaQe3VIOsHipv82kSh20LczXiSKqF58F8m4whWqpa02rrQKBgQD6tbuFiY8fVXi44m1mFO7KDIVXFjuMca0+qXisVaUhG4q42fnlHN0kyuH2IFXNxHD+gE4MnvAUkXDkG1znaz8vM2tfQn6vSpU7OytwOt6UEMWm+112083nGMUy1Me/6rAJbBWfTAl0KXY9/Y/SuEL8u+W6Eas+eASUIe1DooI4xwKBgQD1rAfUXjuRH6WFnbITdG8B3WVZhRCx3YuQVE1UnHR8N6qfx6qKIricvKMSvtWAj0UiIbSA8DENyfMiyAbGpZ0cILxtdXZOtZFW9hfiOeLptnYKTJXOiGoju1prxpUNUKu/1kd7e0clxN75mooge8yFFA6il43wYhhwO2si+FYJdQKBgQDL+1TpX3StX9NrSf8MkXd/uRQ8OQCWUl9MnoJqZPyHpWsG34Ms4IElUFTs9n4Zfv0YdLgMGLzpXzRkw8ahG2c7NjDkPqvoX1xv5sJ++8bg3YyTQe1XoxjiMAsyQmGLSp2T7PbitvDyLFHiOg3surL2AsL00y9rEidXhwsOfohJPQKBgQCqhw4sQHjShIgNlmfMj06amcZG/FGZVPISbiH7cFp++tjp7duX5QAGc/4x/dsPUOOpDIJR2egC7UJiyzvA2aaTprmEtTs46VmIZmwvsQSsO+X1wjFeWlxqjxr1orNFudBt6dxWfzzkn6Iy2i203Jobac+61r5EtKLIDMaSUJTQHQKBgQDaNTfgakpyZBSp/Ydzu76qDKpzGuDqrLBnR/lui4DrSyUoOjNwaGlkU/B6USdaC/BGO7lI08HZU7FzutvXb1mKS+nUI7rKvSTN9NKUJF4MaBv9q40RViKkF9vbq2f1wMs2KUNc0ex2ZL+5BOBICJNHyVulKSxY54Z55L/H4FZmWw==
Here is Java Code which receives Public key as Base64 string and create PublicKey instance from ite. Then I am encrypting a string of text with this key and encode the cipher to Base64 string.
String stringToEncrypt = "This is encrypted message!!!";
byte[] publicKeyBytes = Base64.getDecoder().decode("MIIBIjANBgkqhki....");
Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPPadding");
OAEPParameterSpec oaepParams = new OAEPParameterSpec("SHA-256", "MGF1", new
MGF1ParameterSpec("SHA-256"),PSource.PSpecified.DEFAULT);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PublicKey rsaPublicKey = keyFactory.generatePublic(new X509EncodedKeySpec(publicKeyBytes));
cipher.init(Cipher.ENCRYPT_MODE, rsaPublicKey,oaepParams);
String cipherBytes =
Base64.getEncoder().encodeToString(cipher.doFinal(stringToEncrypt.getBytes()));
The cipherBytes Base64 string then succsefully decrypted by JS Crypto Subtle code below:
First keypair generation code:
const keyPair = await window.crypto.subtle.generateKey(
{
name: "RSA-OAEP",
modulusLength: 2048,
publicExponent: new Uint8Array([1, 0, 1]),
hash: "SHA-256"
},
true,
["encrypt", "decrypt"]
)
Import Private key =
return window.crypto.subtle
.importKey("pkcs8", base64StringToArrayBuffer(privateKeyInPem), {
name: "RSA-OAEP",
hash: "SHA-256",
}, true, ["decrypt"]).then(key=>{
return key;
})
And then decrypt cipher
const dec = new TextDecoder();
function str2ab(str: any) {
const buf = new ArrayBuffer(str.length);
const bufView = new Uint8Array(buf);
for (let i = 0, strLen = str.length; i < strLen; i++) {
bufView[i] = str.charCodeAt(i);
}
return buf;
}
console.log('cipher: '+cipher)
return window.crypto.subtle.decrypt({
name: "RSA-OAEP",
},
privateKey,
str2ab(window.atob(cipher))
).then(result=>{
console.log('DECRYPTED MSG:'+dec.decode(result));
})
But the problem is when I try to decrypt the cipher from openssl - it doesn't work.
Here is the Openssl command which I use:
OpenSSL> rsautl -encrypt -pubin -keyform DER -inkey publicKey.der -oaep -in input.txt -out out.bin
I am using the same Public key, just converted from Base64 string to DER format (converted Base64 string key to DER with Java - 1. Decode base64 string -> byte[]->write to file each byte)
And finally last Openssl operation to convert binary data from out.bin to Base64 string
OpenSSL> enc -A -base64 -in out.bin -out base64.txt

rsautl unchangeably uses SHA1 for the OAEP digests, see here. Since you apply SHA256 in the WebCrypto code, you must use pkeyutl instead, which allows the specification of the digests:
openssl pkeyutl -in plaintext.txt -out ciphertext.bin -encrypt -keyform DER -pubin -inkey spki.der -pkeyopt rsa_padding_mode:oaep -pkeyopt rsa_oaep_md:sha256 -pkeyopt rsa_mgf1_md:sha256
A ciphertext generated in this way can be decrypted using the WebCrypto code:
(async () => {
// your private key
var privateKeyInPem = "MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDwmGY7vUdnW9vy7EqHDowz5m+1OZz1PDoAWklWBcqJON5uPVI92fGF2EnkGPXlE3SsY0AJeEDJR2fvOynrr+2aM24L3Uv70+9AGMNtcuzHtdRX2cgYndpg7n+qu5U1fDJ6Z/vCXBlUMXuh5nwlR+MAFODtrvG6LZZ1cMeC+FNB29D1NLQ2Rf3FYCINKf8dZ+gQELtMt4p5WMexPuDUmeyQbF9WEWst/FALFhmV7h5tc/t0I+x9/hY00LHMFC0ce3yIShnRJUoHNLnOF0sch0AhbgQt2diwzuJOVZk76R9aeVXaLXeUAmhuHE4CrCyDg6qROyvHQq1Z9kfjRs20XPHzAgMBAAECggEAPtCjPGyeFFu00LclfB5tt20t9CQ/GP3o7MelxvpLF0mMNT74VmKs/rNqE037ARxzxUBCa1aEn6hvd9O8DadIgw5zaFCWMoDyQYtVlqE/NaMA9hDLf7XS2qTaGyLPgX/UFAZLAkMWb9ddfncVKYybtR0+Xn/i56dYVYAk7spTvmkai5Q4Li5eEJqrRNse5fg/wiwDda/cWlEvlvfziAjU51gS/Y9ItUT8Z1g8A2NYfqgFVgBVb0qrtbP76TWPbeLZr1FXTMDB0MO+zuC+1lq48+CYqSJqi9a4GlW2gi0wkaQe3VIOsHipv82kSh20LczXiSKqF58F8m4whWqpa02rrQKBgQD6tbuFiY8fVXi44m1mFO7KDIVXFjuMca0+qXisVaUhG4q42fnlHN0kyuH2IFXNxHD+gE4MnvAUkXDkG1znaz8vM2tfQn6vSpU7OytwOt6UEMWm+112083nGMUy1Me/6rAJbBWfTAl0KXY9/Y/SuEL8u+W6Eas+eASUIe1DooI4xwKBgQD1rAfUXjuRH6WFnbITdG8B3WVZhRCx3YuQVE1UnHR8N6qfx6qKIricvKMSvtWAj0UiIbSA8DENyfMiyAbGpZ0cILxtdXZOtZFW9hfiOeLptnYKTJXOiGoju1prxpUNUKu/1kd7e0clxN75mooge8yFFA6il43wYhhwO2si+FYJdQKBgQDL+1TpX3StX9NrSf8MkXd/uRQ8OQCWUl9MnoJqZPyHpWsG34Ms4IElUFTs9n4Zfv0YdLgMGLzpXzRkw8ahG2c7NjDkPqvoX1xv5sJ++8bg3YyTQe1XoxjiMAsyQmGLSp2T7PbitvDyLFHiOg3surL2AsL00y9rEidXhwsOfohJPQKBgQCqhw4sQHjShIgNlmfMj06amcZG/FGZVPISbiH7cFp++tjp7duX5QAGc/4x/dsPUOOpDIJR2egC7UJiyzvA2aaTprmEtTs46VmIZmwvsQSsO+X1wjFeWlxqjxr1orNFudBt6dxWfzzkn6Iy2i203Jobac+61r5EtKLIDMaSUJTQHQKBgQDaNTfgakpyZBSp/Ydzu76qDKpzGuDqrLBnR/lui4DrSyUoOjNwaGlkU/B6USdaC/BGO7lI08HZU7FzutvXb1mKS+nUI7rKvSTN9NKUJF4MaBv9q40RViKkF9vbq2f1wMs2KUNc0ex2ZL+5BOBICJNHyVulKSxY54Z55L/H4FZmWw==";
var privateKey = await window.crypto.subtle.importKey("pkcs8", base64StringToArrayBuffer(privateKeyInPem), {name: "RSA-OAEP", hash: "SHA-256"}, true, ["decrypt"]);
console.log(privateKey);
// Base64 encoded ciphertext from openssl pkeyutl
var cipher = "V2QWzwoOHPAlKhGqv0fDSv+lSPytBW4tTxVVJgneyvfIqTDvllhbZJzUAszdAC0IEow+YgbIWHyIBCw9YVS+EDZ3jbuIU97nx5NxAimiUFKvUmHE8p3oP6AP/etJhGQGC+fMiTbhmGn5FQhMnH/2lVei3yJypXWbgI6ONRmncYalq73q7VGelFUSubuPWQA3bKzuIOSorpQFy9sGIMDvW+YOMLrClVmUujVrEXrdsIbvzSb6hooKHbwjOaAmN4XRw0sr+YaF3n2PwazpLSvJmuugF26GxhmJAMmNViUvvsN+ycpJZdyRKNehQGqmahpC0XXihZ9dsHEH7vIDDmPAZQ==";
console.log('cipher: '+cipher)
// successfull decryption
var decrypted = await window.crypto.subtle.decrypt({name: "RSA-OAEP"}, privateKey, str2ab(window.atob(cipher)));
const dec = new TextDecoder();
console.log('DECRYPTED MSG:' + dec.decode(decrypted));
})();
function base64StringToArrayBuffer(base64String){
return Uint8Array.from(window.atob(base64String), c => c.charCodeAt(0));
}
function str2ab(str) {
const buf = new ArrayBuffer(str.length);
const bufView = new Uint8Array(buf);
for (let i = 0, strLen = str.length; i < strLen; i++) {
bufView[i] = str.charCodeAt(i);
}
return buf;
}
For completeness: In the context of OAEP there are two digests to specify, the content digest and the MGF1 digest. pkeyutl allows to set both independently. If both digests are identical (which is usually the case), it is sufficient to set only the content digest via rsa_oaep_md, i.e. the MGF1 digest implicitly has the same value as long as no other digest is explicitly set via rsa_mgf1_md.

Related

CryptoJs decrypt function equivalent in Java

I'm trying to decrypt a token that the server I'm using brings in order to load correctly users access to the page.
I already achieved the goal using CryptoJs using JavaScript. However now I need to transfer that function to my java backend.
Look my code using CryptoJs which works correctly
const token = "U2FsdGVkX1+6YueVRKp6h0dZfk/a8AC9vyFfAjxD4nb7mXsKrM7rI7xZ0OgrF1sShHYNLMJglz4+67n/I7P+fg=="
const key = "p80a0811-47db-2c39-bcdd-4t3g5h2d5d1a"
// Decrypt
const iv = CryptoJS.enc.Utf8.parse(key);
const decryptedToken = CryptoJS.AES.decrypt(token, key, {
keySize: 16,
iv,
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7,
});
const stringToken = decryptedToken.toString(CryptoJS.enc.Utf8);
const dataToUse = JSON.parse(stringToken)
console.log("decryptedToken => ", stringToken);
console.log("data => ",dataToUse);
And this is my code in Java using javax.crypto
public String decrypt() {
String finalToken = null;
Cipher cipher;
try {
String token = "U2FsdGVkX1+6YueVRKp6h0dZfk/a8AC9vyFfAjxD4nb7mXsKrM7rI7xZ0OgrF1sShHYNLMJglz4+67n/I7P+fg==";
String key = "p80a0811-47db-2c39-bcdd-4t3g5h2d5d1a";
Key skeySpec = new SecretKeySpec(Arrays.copyOf(key.getBytes(), 16), "AES");
byte[] iv = Arrays.copyOf(key.getBytes(StandardCharsets.UTF_8), 16);
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
byte[] bytesToDecrypt = Base64.decodeBase64(token.getBytes());
cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
cipher.init(Cipher.DECRYPT_MODE, skeySpec, ivParameterSpec);
byte[] decrypted = cipher.doFinal(bytesToDecrypt);
finalToken = new String(decrypted);
log.info("Decrypt token succesfully {}", finalToken);
} catch (NoSuchAlgorithmException
| InvalidAlgorithmParameterException
| IllegalBlockSizeException
| NoSuchPaddingException
| BadPaddingException
| InvalidKeyException e) {
e.printStackTrace();
log.error("Error decrypting");
}
return finalToken;
}
I'm not sure I'm setting correctly the key and the iv. With the piece of code above I get this error:
javax.crypto.BadPaddingException: pad block corrupted
If I don't "cut" the key and the iv in order to have 16 bytes I get wrong length error.
Can someone help me to figure out what's wrong please!
The expected result is to get this info in java so then I can manipulate the object:
{name: "Burak", surName: "Bayraktaroglu"}
Decryption fails because CryptoJS interprets the key material as password when passed as string (as in the posted code).
In this case, the password and a salt are used to derive the key and IV applying the OpenSSL proprietary key derivation function EVP_BytesToKey().
The random 8 bytes salt is generated during encryption, which returns as result the Base64 encoding of the concatenation of a prefix, the salt and the actual ciphertext. The prefix consists of the ASCII encoding of Salted__.
Therefore, the following steps are necessary in the decryption process:
Determining salt and ciphertext from the CryptoJS return value
Deriving key and IV using password and salt
Decryption of the ciphertext with the derived key and IV
There are several ways to implement the last two steps in Java.
You can find various ports of EVP_BytesToKey() on the web which can be used in conjunction with the native JCA/JCE for decryption. These EVP_BytesToKey() ports are not always reliable, a solid port is this one.
A convenient (and reliable) alternative is BouncyCastle (with its own EVP_BytesToKey() port), described below:
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import org.bouncycastle.crypto.digests.MD5Digest;
import org.bouncycastle.crypto.engines.AESEngine;
import org.bouncycastle.crypto.generators.OpenSSLPBEParametersGenerator;
import org.bouncycastle.crypto.modes.CBCBlockCipher;
import org.bouncycastle.crypto.paddings.PaddedBufferedBlockCipher;
import org.bouncycastle.crypto.params.ParametersWithIV;
...
// Get salt and ciphertext
String saltCiphertextB64 = "U2FsdGVkX1+6YueVRKp6h0dZfk/a8AC9vyFfAjxD4nb7mXsKrM7rI7xZ0OgrF1sShHYNLMJglz4+67n/I7P+fg==";
byte[] saltCiphertext = Base64.getDecoder().decode(saltCiphertextB64);
ByteBuffer byteBuffer = ByteBuffer.wrap(saltCiphertext);
byte[] prefix = new byte[8];
byte[] salt = new byte[8];
byteBuffer.get(prefix);
byteBuffer.get(salt);
byte[] ciphertext = new byte[byteBuffer.remaining()];
byteBuffer.get(ciphertext);
// Key derivation via EVP_BytesToKey() (using BouncyCastle)
String passwordStr = "p80a0811-47db-2c39-bcdd-4t3g5h2d5d1a";
byte[] password = passwordStr.getBytes(StandardCharsets.UTF_8);
OpenSSLPBEParametersGenerator pbeGenerator = new OpenSSLPBEParametersGenerator(new MD5Digest());
pbeGenerator.init(password, salt);
ParametersWithIV parameters = (ParametersWithIV) pbeGenerator.generateDerivedParameters(256, 128); // keySize, ivSize in bits
// FYI: key: ((KeyParameter)parameters.getParameters()).getKey()
// IV : parameters.getIV()
// Decryption (using BouncyCastle)
PaddedBufferedBlockCipher cipher = new PaddedBufferedBlockCipher(new CBCBlockCipher(new AESEngine()));
cipher.init(false, parameters);
byte[] plaintext = new byte[cipher.getOutputSize(ciphertext.length)];
int length = cipher.processBytes(ciphertext, 0, ciphertext.length, plaintext, 0);
length += cipher.doFinal(plaintext, length);
String plaintextSr = new String(plaintext, 0, length, StandardCharsets.UTF_8);
System.out.println(plaintextSr); // {"name":"Burak","surName":"Bayraktaroglu"}

how to properly decrypt text via Subtle Crypto which was encrypted via CryptoJS

I have code that encrypts user's data using CryptoJS.AES, stores key, iv and encrypted content in different places.
Also it decrypts encrypted content using stored key and iv by user demand.
I want to use Subtle Crypto browser API for encryption,which is done.
But I also want to have possibility to decrypt old data (that was ecnrypted using CryptoJS.AES) using Subtle Crypto.
old data was generated with following code
var CryptoJS = require("crypto-js/core");
CryptoJS.AES = require("crypto-js/aes");
let encKey = generateRandomString();
let aesEncrypted = CryptoJS.AES.encrypt(content, encKey);
let encrypted = {
key: aesEncrypted.key.toString(),
iv: aesEncrypted.iv.toString(),
content: aesEncrypted.toString()
};
and I've tried to decrypt it as following
let keyArrayBuffer = hexArrayToArrayBuffer(sliceArray(encrypted.key, 2));
let decKey = await importKey(keyArrayBuffer);
let decIv = hexArrayToArrayBuffer(sliceArray(encrypted.iv, 2));
let encContent = stringToArrayBuffer(encrypted.content);
let decryptedByteArray = await crypto.subtle.decrypt(
{ name: "AES-CBC", iv: decIv },
decKey,
encContent
);
let decrypted = new TextDecoder().decode(decrypted);
I receive DOMException error without backtrace on await crypto.subtle.decrypt
complete reproduction can be found at https://codesandbox.io/s/crypto-js-to-subtle-crypto-u0pgs?file=/src/index.js
In the CryptoJS code the key is passed as string. Therefore it is interpreted as a password, from which in combination with a randomly generated 8 bytes salt, a 32 bytes key and a 16 bytes IV are derived, see here. The proprietary (and relatively insecure) OpenSSL key derivation function EVP_BytesToKey is used for this.
CryptoJS.AES.encrypt() returns a CipherParams object that encapsulates various parameters, such as the generated key and IV as WordArray, see here. toString() applied to the key or IV WordArray, returns the data hex encoded. toString() applied to the CipherParams object, returns the ciphertext in OpenSSL format, i.e. the first block (= the first 16 bytes) consists of the ASCII encoding of Salted__, followed by the 8 bytes salt and the actual ciphertext, all together Base64 encoded, see here. This means that the actual ciphertext starts (after Base64 decoding) with the second block.
The following code illustrates how the ciphertext generated with CryptoJS can be decrypted with the WebCrypto API
//
// CryptoJS
//
const content = "The quick brown fox jumps over the lazy dog";
const encKey = "This is my passphrase";
const aesEncrypted = CryptoJS.AES.encrypt(content, encKey);
const encrypted = {
key: aesEncrypted.key.toString(),
iv: aesEncrypted.iv.toString(),
content: aesEncrypted.toString()
};
//
// WebCrypto API
//
// https://stackoverflow.com/a/50868276
const fromHex = hexString => new Uint8Array(hexString.match(/.{1,2}/g).map(byte => parseInt(byte, 16)));
// https://stackoverflow.com/a/41106346
const fromBase64 = base64String => Uint8Array.from(atob(base64String), c => c.charCodeAt(0));
async function decryptDemo(){
const rawKey = fromHex(encrypted.key);
const iv = fromHex(encrypted.iv);
const ciphertext = fromBase64(encrypted.content).slice(16);
const key = await window.crypto.subtle.importKey(
"raw",
rawKey,
"AES-CBC",
true,
["encrypt", "decrypt"]
);
const decrypted = await window.crypto.subtle.decrypt(
{
name: "AES-CBC",
iv: iv
},
key,
ciphertext
);
const decoder = new TextDecoder();
const plaintext = decoder.decode(decrypted);
console.log(plaintext);
}
decryptDemo();
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.0.0/crypto-js.min.js"></script>

PBKDF2WithHmacSHA512 decryption in Javascript

I'm a Noob to Java and Javascript. I'm trying to decrypt a Java encrypted text using Javascript, tried with both CryptoJS and node-forge, but the decrypted text I'm getting is gibberish.
Java cipher creation (this works in JAVA where we pass in the ciphermode as Cipher.DECRYPT_MODE and is able to decrypt the line encrypted using the same cipher algo/spec)
Cipher c;
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA512");
SecretKey key = factory.generateSecret(new PBEKeySpec(password.toCharArray(), salt, iterationsCount, bitStrength));
SecretKeySpec keyspec = new SecretKeySpec(key.getEncoded(), "AES");
c = Cipher.getInstance("AES/ECB/PKCS5Padding");
c.init(cipherMode, keyspec);
return c;
Anyone know of a good Js library that I can use or provide some sample of how to decrypt the encrypted line using this Cipher and KeySpec?
Javascript Code
const bufferStr = Buffer.from(encryptedStr, "base64"); //encryptedStr is the encrypted string in base64 format
//generate key using node forge
//password is a String, salt is a hex, numIterations is an int
const AESkey = forge.pkcs5.pbkdf2(password, salt, numIterations, 32);
var decipher = forge.cipher.createDecipher("AES-ECB", AESkey);
decipher.start({});
//console.log(decipher);
decipher.update(forge.util.createBuffer(bufferStr));
var result = decipher.finish(); // check 'result' for true/false
// outputs decrypted hex
console.log(decipher.output);
Output
ByteStringBuffer {
data:
'ÏWʃÓYqŽé\u0017\u0000†\u0018ŸHª3•—²Ì[iòjóÑ8⌂\u0002k\rÒ\r­\u0018EvX!ØçQâ!1zåDÉ-<\u0012s)‰²\u001aò=k6<å`r’Ã×lCê)Xq×mÊ¥ ‚Ø\u0000D°l붳È\b>‘`eó¾Î<å\u001c•g|K\\Ù\u0014\u0010#œó\u0019±žk¯þM“‹¾tüzØçBïõîi¸\u0006ÙZ9Jsæ!kµ¸Ã.à-É‹Ï69',
read: 0,
_constructedStringLength: 208 }
Additional Details
On the Java Side
String password = <Password String>;
byte[] salt = { bytes array|| String };
int iterationCount = 65536;
int bitStrength = 256;
encryptedText = <base64 encoded string>'; //this is what I'm trying to decrypt
Node JS Code I'm using to decrypt this (and I'm doing something really wrong :( )
const forge = require('node-forge');
const password = <password as String>;
const salt = new Int8Array(new Buffer(<array of bytes>)); //converting from Java byte[] array
const numIterations = 65536;
const keyPhrase = forge.pkcs5.pbkdf2(password, salt, numIterations, 32, "SHA512");
const encryptedText =
"<base64 encoded String>";
let decipher = forge.cipher.createDecipher("AES-ECB", keyPhrase);
decipher.start({});
decipher.update(forge.util.createBuffer(encryptedText));
let result = decipher.finish(); // check 'result' for true/false
console.log(decipher.output);
//Outputs a ByteStringBuffer
/*ByteStringBuffer {
data:
'ÜB\rÌ9pÞ[\u000e²&øV$²ÝªüÚf\u0000ÀÅmè·èjÉû¤Á|ê¹õIpð\u0012ì\u001cª\f\u000f¦¹ñøÄ\u001b!ëáÕXô\u0007½¦{±\u0012\u001a®}#U6òöÞÊ\u0016n§#gf h£xû\u0016jyÕ5Õ3ÍJ&\n^Eå2õÉ\u000eÑßü!ªÎâÉ\u0010!eÑ.&·à\u0011¬\u0005\u0015/#&\u0019&Wà­ÃK\u000eç8|\'Ê­b°¸ó¹Ñ\bS\u0004lÑ´<\u0011O½\u0014q?uu$\u001c\b^»',
read: 0,
_constructedStringLength: 192 }*/

symmetric and asymmetric encryption between Angular 2 and Spring Boot

I need send a password from Angular App to Spring boot backend and i need encrypt this password. I try to use AES to encrypt the password and RSA to encrypt the AES generated key, but i dont know how do this.
My code:
Angular 2 Side:
EncryptService:
public generateRandomKey( keyLength: number){
let chars =
`0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmn
opqrstuvwxyz*&-%/!?*+=()`;
let stringKey = "";
for (let i=0; i < keyLength; i++) {
var rnum = Math.floor(Math.random() * chars.length);
stringKey += chars.substring(rnum,rnum+1);
}
return stringKey;
}
public aesEncrypt( phrase: string ){
let key = this.generateRandomKey(50);
let aesEncrypt = cryptojs.AES.encrypt(phrase, key);
let aesKey = aesEncrypt.key
let aesIv = aesEncrypt.iv;
let encryptMessage = aesEncrypt.toString();
return [encryptMessage, aesKey, aesIv];
}
public buildPubkFromPem( pem: string ){
return forge.pki.publicKeyFromPem(pem);
}
Component:
let pubKey = this.encryptService.buildPubkFromPem(environment.publicKey);
let data = this.encryptService.aesEncrypt(map.passwd);
let passwdEncrypt = data[0];
let aesKey = data[1];
let encryptAesKey = pubKey.encrypt(aesKey.toString());
this.pendingRequestService
.send({'passwd': passwdEncrypt, 'encryptAesKey': encryptAesKey)
.finally(()=>{this.popupSignService.isLoading = false;})
.subscribe(
result => {
console.log("OK", result);
}
);
}
Backend Side:
Controller:
public ResponseEntity signDocs(
#RequestParam(value = "passwd") String passwd,
#RequestParam(value = "encryptAesKey") String encryptAesKey,
){
try{
signatureService.decryptMessage(passwd, encryptAesKey);
}catch( Exception ex ){
ex.printStackTrace();
}
Service:
public String decryptMessage( String encrypMsg, String encryptAesKey ){
// Load private key from P12
ClassPathResource pkcs12 = new ClassPathResource("ssl/file.p12");
KeyStore keystore = KeyStore.getInstance("PKCS12");
keystore.load(pkcs12.getInputStream(), p12Password.toCharArray());
String alias = (String)keystore.aliases().nextElement();
PrivateKey privateKey = (PrivateKey) keystore.getKey(alias, p12Password.toCharArray());
// Decrypt
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.PRIVATE_KEY, privateKey);
System.out.println(encryptAesKey);
byte[] doFinal = cipher.doFinal(encryptAesKey.getBytes());
String aesKey = new String(cipher.doFinal(Base64.decodeBase64(encryptAesKey)), "UTF-8");
return aesKey;
}
when i try to decrypt the AES encrypted key:
javax.crypto.IllegalBlockSizeException: Data must not be longer than 256 bytes
I think it's related when I encrypted the AES key.
Can somebody help me?
Thanks in advance.
EDIT
If i encrypt the string "Some text" with the public key using forge, and send the encrypted text to the backend does not work. If i ecrypt and decrypt using the same public and private key work from javascript to javascript and java to java.
Javascript code:
let encryptedText = pubKey.encrypt(this.forgeService.encodeUTF8("Some text"));//<-- Encrypt "Some text"
I dont know if something is loosing (changing) when i send the encrypted text in the network.
I believe you have basic problem with encoding data (along others)
let encryptAesKey = pubKey.encrypt(aesKey.toString());
You are encrypting string representation of the key (not the key itself) and I believe JS returns the toString() in the hex format (please correct me if I am wrong). The result would be twice as long as the original key.
while i Java you are trying to decrypt the encrypted hex string rssulting in a byte array, which doesn't fit into expected 256 bit size
edit:
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.PRIVATE_KEY, privateKey);
is wrong, you have to use Cipher. DECRYPT_MODE.
next :
String aesKey = new String(cipher.doFinal(.. DO NOT make a string from the key (encode when you want to print it), keep it as byte array. You are loosing nonprintable values.
you should be specific with the encryption mode
Cipher cipher = Cipher.getInstance("rsa/ecb/nopadding");

Encrypt in javascript and decrypt in C# with AES algorithm

I tried to encrypt in angular using AES library from AES.
I encrypted the string using the CryptoJS.AES.encrypt() method from AES.
Here is my code:
var txtloginKod = 'Some String...';
var key = CryptoJS.enc.Utf8.parse('8080808080808080');
var iv = CryptoJS.enc.Utf8.parse('8080808080808080');
var encryptedlogin = CryptoJS.AES.encrypt(CryptoJS.enc.Utf8.parse(txtloginKod), key,
{
keySize: 128 / 8,
iv: iv,
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7
});
The method CryptoJS.AES.encrypt() return a Object to my encryptedlogin variable.
I don't know how to send this object to my WCF Web server in C#
When I tried to send the whole object (and define the Web service method to expect get C# Object), I got the error below:
"Converting circular structure to JSON"
Using Dot net core and Type Script.
npm instal crypto-js
//Inside imports of your TS file include
import * as CryptoJS from 'crypto-js';
// Declare this key and iv values in declaration
private key = CryptoJS.enc.Utf8.parse('4512631236589784');
private iv = CryptoJS.enc.Utf8.parse('4512631236589784');
// Methods for the encrypt and decrypt Using AES
encryptUsingAES256() {
var encrypted = CryptoJS.AES.encrypt(CryptoJS.enc.Utf8.parse(JSON.stringify("Your Json Object data or string")), this.key, {
keySize: 128 / 8,
iv: this.iv,
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7
});
console.log('Encrypted :' + encrypted);
this.decryptUsingAES256(encrypted);
return encrypted;
}
decryptUsingAES256(decString) {
var decrypted = CryptoJS.AES.decrypt(decString, this.key, {
keySize: 128 / 8,
iv: this.iv,
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7
});
console.log('Decrypted : ' + decrypted);
console.log('utf8 = ' + decrypted.toString(CryptoJS.enc.Utf8));
}
The C# code to encode or decode is here.
public class encr {
public static string DecryptStringAES(string cipherText) {
var keybytes = Encoding.UTF8.GetBytes("4512631236589784");
var iv = Encoding.UTF8.GetBytes("4512631236589784");
var encrypted = Convert.FromBase64String(cipherText);
var decriptedFromJavascript = DecryptStringFromBytes(encrypted, keybytes, iv);
return decriptedFromJavascript;
}
private static string DecryptStringFromBytes(byte[] cipherText, byte[] key, byte[] iv) {
// Check arguments.
if (cipherText == null || cipherText.Length <= 0) {
throw new ArgumentNullException("cipherText");
}
if (key == null || key.Length <= 0) {
throw new ArgumentNullException("key");
}
if (iv == null || iv.Length <= 0) {
throw new ArgumentNullException("key");
}
// Declare the string used to hold
// the decrypted text.
string plaintext = null;
// Create an RijndaelManaged object
// with the specified key and IV.
using(var rijAlg = new RijndaelManaged()) {
//Settings
rijAlg.Mode = CipherMode.CBC;
rijAlg.Padding = PaddingMode.PKCS7;
rijAlg.FeedbackSize = 128;
rijAlg.Key = key;
rijAlg.IV = iv;
// Create a decrytor to perform the stream transform.
var decryptor = rijAlg.CreateDecryptor(rijAlg.Key, rijAlg.IV);
try {
// Create the streams used for decryption.
using(var msDecrypt = new MemoryStream(cipherText)) {
using(var csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read)) {
using(var srDecrypt = new StreamReader(csDecrypt)) {
// Read the decrypted bytes from the decrypting stream
// and place them in a string.
plaintext = srDecrypt.ReadToEnd();
}
}
}
} catch {
plaintext = "keyError";
}
}
return plaintext;
}
public static string EncryptStringAES(string plainText) {
var keybytes = Encoding.UTF8.GetBytes("4512631236589784");
var iv = Encoding.UTF8.GetBytes("4512631236589784");
var encryoFromJavascript = EncryptStringToBytes(plainText, keybytes, iv);
return Convert.ToBase64String(encryoFromJavascript);
}
private static byte[] EncryptStringToBytes(string plainText, byte[] key, byte[] iv) {
// Check arguments.
if (plainText == null || plainText.Length <= 0) {
throw new ArgumentNullException("plainText");
}
if (key == null || key.Length <= 0) {
throw new ArgumentNullException("key");
}
if (iv == null || iv.Length <= 0) {
throw new ArgumentNullException("key");
}
byte[] encrypted;
// Create a RijndaelManaged object
// with the specified key and IV.
using(var rijAlg = new RijndaelManaged()) {
rijAlg.Mode = CipherMode.CBC;
rijAlg.Padding = PaddingMode.PKCS7;
rijAlg.FeedbackSize = 128;
rijAlg.Key = key;
rijAlg.IV = iv;
// Create a decrytor to perform the stream transform.
var encryptor = rijAlg.CreateEncryptor(rijAlg.Key, rijAlg.IV);
// Create the streams used for encryption.
using(var msEncrypt = new MemoryStream()) {
using(var csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write)) {
using(var swEncrypt = new StreamWriter(csEncrypt)) {
//Write all data to the stream.
swEncrypt.Write(plainText);
}
encrypted = msEncrypt.ToArray();
}
}
}
// Return the encrypted bytes from the memory stream.
return encrypted;
}
}
I too had the requirement to send encrypted JSON object to .Net CORE API 2.0 and searched on internet to find if there is any help. I know by now you would have solved this issue but to help anyone interested I am providing my solution to help them.
With the sample codes I found here on stackoverflow, I managed to implement the solution. The tricky bit is the key and IV has to be 16 in length for the code to work.
public static encrypt(model: any) {
const key = CryptoJS.enc.Utf8.parse('TestMyOwnKeyForI');
const iv = CryptoJS.enc.Utf8.parse('TestMyOwnIV1ForI');
// padding and truncating
const encryptedMessage = CryptoJS.AES.encrypt(JSON.stringify(model), key, {
keySize: 128 / 8,
iv: iv,
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7
}).toString();
return encryptedMessage;
}
In CORE I've implemented my customer model binding provider as follows:
public class DecryptModelBinderProvider : IModelBinderProvider
{
public IModelBinder GetBinder(ModelBinderProviderContext context)
{
if (context.Metadata.ModelType == typeof(MyModel))
return new DecryptModelBinder();
return null;
}
}
Then my own decryptmodelbinder as follows:
public class DecryptModelBinder : IModelBinder
{
public Task BindModelAsync(ModelBindingContext bindingContext)
{
if (bindingContext == null)
throw new ArgumentNullException(nameof(bindingContext));
using (var sr = new StreamReader(bindingContext.HttpContext.Request.Body))
{
string valueFromBody = sr.ReadToEnd();
if (valueFromBody != null && valueFromBody.Length > 0)
{
var decrypted = Encryption.DecryptString(valueFromBody, "TestMyOwnKeyForI");
var model = JsonConvert.DeserializeObject(decrypted, bindingContext.ModelType);
bindingContext.Result = ModelBindingResult.Success(model);
bindingContext.Model = model;
}
}
return Task.CompletedTask;
}
}
You would need to serialize the ciphertext yourself. There are many ways to do it.
If the key is not a string, but a WordArray (as in your case), then a simple encryptedlogin.toString() would produce a Base64 encoded string only containing the ciphertext. Remember that you need to include the enc-base64.js source file.
If the "key" (actually password) is a string, then an OpenSSL-compatible key derivation is applied. In that case encryptedlogin.toString() would be a Base64 encoded string which contains the "Salted__" string, 8 byte random salt and the ciphertext.
If you only want to get the ciphertext then encryptedlogin.ciphertext.toString() will give you a Hex-encoded string which contains only the ciphertext and encryptedlogin.iv.toString() will give you a Hex-encoded IV. You can produce a Base64-encoded string in this way encryptedlogin.ciphertext.toString(CryptoJS.enc.Base64).
Remember that the IV must be randomly chosen for every encryption in order to provide semantic security. It doesn't have to be secret, so you can send it along with the ciphertext.
On the server side, you would decode the values (Base64 or Hex depending on what you used during encryption) and use them with the AesCryptoServiceProvider class (or similar) to decrypt the ciphertext.
Remember that you additionally need to authenticate your ciphertext in order to detect (malicious) manipulation. This can be done with an encrypt-then-MAC scheme with a strong MAC like HMAC-SHA256.
Also, if the key is transmitted along with the ciphertext or over an insecure channel, then this is basically just data obfuscation and doesn't provide real security. See more: Javascript Cryptography Considered Harmful
Use encryptedLogin.cipherText from the returned object
Check pls https://social.msdn.microsoft.com/Forums/vstudio/en-US/47800a60-4461-4f8e-a8d1-751fa62c7884/aes-encrypt-in-javascript-and-decrypt-in-c?forum=csharpgeneral
Also, you should use CryptoJS v3.1.2 AES realization to use it. You can download it here : https://storage.googleapis.com/google-code-archive-downloads/v2/code.google.com/crypto-js/CryptoJS%20v3.1.2.zip
Js side code you can find here : jsfiddle.net/aEKw5/22/
So using info above you can encode at C# side, and decode it on js. or vice versa.

Categories