How to Decrypt RSA encrypted string from JSEncrypt using C# BouncyCastle - javascript

I know there are a few posts regarding this, but none of them solve my issue so i will cut to the chase.
I have encrypted a string using JSEncrypt (javascript) with the following code:
var rsa = new JSEncrypt();
rsa.setPublicKey(pubKey);
var encrypted = rsa.encrypt($(this).text());
The public key is stored in a var like so:
var pubKey = "-----BEGIN PUBLIC KEY-----\
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCkskeHcz6zTvnXKGHbCkfeEnKF\
38j1hEgWw9UAZlg5a7m/1AaHkqkvKUOfoX+zSYH4CJr/MEVmrEa2ucPjKljXK14M\
74NzSwYuPxaikpfwxe7qvn1/0TIqahVHEiSmqieLIl+gHIyAc32e/wUyjhGFjbwL\
eJ/hBDTkt6O/QajWKwIDAQAB\
-----END PUBLIC KEY-----";
I then use the generated cipher text and send that to c# where it needs to decrypt it. I am using Bouncy Castle and have written code as such:
public string Decrypt(string cipherText)
{
var bytesToDecrypt = Convert.FromBase64String(cipherText);
AsymmetricCipherKeyPair keyPair;
using (var reader = File.OpenText(AppDomain.CurrentDomain.BaseDirectory + #"/scripts/private.pem"))
{
keyPair = (AsymmetricCipherKeyPair)new PemReader(reader).ReadObject();
}
var engine = new RsaEngine();
engine.Init(false, keyPair.Private);
var decrypted = engine.ProcessBlock(bytesToDecrypt, 0, bytesToDecrypt.Length);
var decryptedMessage = Encoding.UTF8.GetString(decrypted);
return decryptedMessage;
}
It does the decryption without error, but the output looks like this:
"\u0002��lT�J;�ol�\u008fY�L:GK=\u0002�(�����\\\u0003ܢ�fMT�\u001eoqR�\\\u000f�s�A��+ښ�|����\u0001J�s�w[\u001egſ\u0012^7�\u000f44Qy\a*�T�~Tu�Ei�O/<�\u000f!S:�]\u0018V<7S\n:x#���GȀ\u0016.w���Lb�\0"
I have tried to specifically use PKSC#1 by having this:
public string Decrypt(string cipherText)
{
var bytesToDecrypt = Convert.FromBase64String(cipherText);
AsymmetricCipherKeyPair keyPair;
using (var reader = File.OpenText(AppDomain.CurrentDomain.BaseDirectory + #"/scripts/private.pem"))
{
keyPair = (AsymmetricCipherKeyPair)new PemReader(reader).ReadObject();
}
Pkcs1Encoding engine = new Pkcs1Encoding(new RsaEngine());
engine.Init(false, keyPair.Private);
var decrypted = engine.ProcessBlock(bytesToDecrypt, 0, bytesToDecrypt.Length);
var decryptedMessage = Encoding.UTF8.GetString(decrypted);
return decryptedMessage;
}
I clearly have something wrong here as the decryptedMessage = "" and the decrypted bytes = bytes[0]
How do i fix this so that it decrypts the cipherText correctly.
As far i know JSEncrypt encrypts using PKSC1 encoding as default?
Any help is appreciated.
Please avoid rude comments, as learning to do it correctly is why I am posting here.
Thank you for your help in advance!

Related

How to implement this encryption related code in JavaScript?

I have following code in Java.
String secretString = 'AAABBBCCC'
KeyGenerator kgen = KeyGenerator.getInstance("AES");
SecureRandom securerandom = SecureRandom.getInstance("SHA1PRNG");
securerandom.setSeed(secretString.getBytes());
kgen.init(256, securerandom);
SecretKey secretKey = kgen.generateKey();
byte[] enCodeFormat = secretKey.getEncoded();
SecretKeySpec key = new SecretKeySpec(enCodeFormat, "AES");
Security.addProvider(new BouncyCastleProvider());
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] byteContent = content.getBytes("utf-8");
byte[] cryptograph = cipher.doFinal(byteContent);
String enc1 = Base64.getEncoder().encodeToString(cryptograph);
return enc1;
I need to implement it in JavaScript/Node.js, however I can only figure out the last half in js like below
'use strict';
const crypto = require('crypto');
const ALGORITHM = 'AES-256-ECB';
const secretString = 'AAABBBCCC'
// missing part in JS (how to convert secretString to key)
function encrypt(plaintext, key) {
const cipher = crypto.createCipheriv(ALGORITHM, key, Buffer.alloc(0));
return cipher.update(plaintext, 'utf8', 'base64') + cipher.final('base64');
}
For the previous Java part( from secretString to key generated by KeyGenerator), I don't know how to implement it in JavaScript, neither do I know whether there is a thing like KeyGenerator in JavaScript world could help me to do the heavy lifting work.
I think this is what you're after:
const crypto = require("crypto-js");
// Encrypt
const ciphertext = crypto.AES.encrypt('SOMETHING SECRET', 'secret key 123');
// Decrypt
const bytes = crypto.AES.decrypt(ciphertext.toString(), 'secret key 123');
const decryptedData = bytes.toString(crypto.enc.Utf8);
console.log(decryptedData);
https://runkit.com/mswilson4040/5b74f914d4998d0012cccdc0
UPDATE
JavaScript does not have a native equivalent for key generation. The answer is to create your own or use a third party module. I would recommend something like uuid for starters.
You can use crypto.randomBytes().
As per its documentation:
Generates cryptographically strong pseudo-random data. The size argument is a number indicating the number of bytes to generate.
Also, it uses openssl's RAND_bytes API behind scenes

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");

Replace Java code with javascript to encrypt in AES/ECB/Pkcs5

I am coding in node.js where i want to use AES/ECB/Pkcs5 for encrypting my string with a particular key. Now i have java code which is given below but as i tried to code the same thing in javascript things became really messy, Firstly i saw a simple solution of using crypto whose code is given below and i tried it but the output i got was different from the one i got from java, then i switched from crypto to Crypto-js which is another library for encryption now i gain tried the same but i again got different output and then finally when i saw plethora of posts where one said to declare iv and the other one to use key and input_string as a buffer and the other one to use chunks to encrypt the data.
Now as you can imagine i have tried for two days straight to code all these possibilities but none of them worked out, now can anyone please enlighten me and tell me which is the right approach or where i am missing something.
Thanks
JAVA CODE
SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes(), "AES");
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(1, secretKeySpec);
byte[] aBytes = cipher.doFinal(inputString.getBytes());
BASE64Encoder encoder = new BASE64Encoder();
String base64 = encoder.encode(aBytes).toString();
base64 = URLEncoder.encode(base64, "UTF-8");
return base64;
CRYPTO-JS CODE
var encrypted = CryptoJS.DES.encrypt(input_string, key, {
mode: CryptoJS.mode.ECB,
padding: CryptoJS.pad.Pkcs7
});
var base64 = CryptoJS.enc.Base64.toString(encrypted.ciphertext);
// var base64 = CryptoJS.enc.Base64.stringify(encrypted);
var parsedStr = base64.toString(CryptoJS.enc.Utf8);
console.log('parsedStr: ', parsedStr);
Crypto CODE
var cipher = crypto.createCipher('aes-128-ecb', key);
var crypted = cipher.update(input_string,'utf8','base64')
crypted += cipher.final('base64');
Chunks Implementation
var crypto = require('crypto'),
iv = new Buffer(''),
key = new Buffer('abcgthdbgfhyjhuy', 'hex'),
cipher = cypto.createCipheriv('aes-128-ecb', key, iv),
chunks = [];
chunks.push(cipher.update(
new Buffer(JSON.stringify({key: "abcgthdbgfhyjhuy"}), 'utf8'),
'buffer', 'base64'));
chunks.push(cipher.final('base64'));
var encryptedString = chunks.join('');
console.log('encryptedString: ', encryptedString);
ONE WHERE I USED input_string AND key AS BUFFER
var key = new Buffer(key, "utf8");
var input_string = new Buffer(input_string, "utf8");
IV USAGE
var key = new Buffer(key, "utf8");
var input_string = new Buffer(input_string, "utf8");
var iv = new Buffer(16); // 16 byte buffer with random data
iv.fill(0); // fill with zeros
var cipher = crypto.createCipher('aes-128-ecb', key, iv);
var crypted = cipher.update(input_string,'utf8','base64')
crypted += cipher.final('base64');

different output for JAVA vs javascript AES 256 cbc

Im trying to create an AES 256 cbc encryption using java and I need to emulate EXACTLY this javascript code (I know the iv is the same as the key (turnicated to 16 bytes), that's how it is from the site i'm trying to log into using java)
var recievedStr = "MDk4NTY1MDAyMjg2MTU1OA=="; //some
var key = CryptoJS.enc.Base64.parse(recievedStr);
var iv = CryptoJS.enc.Base64.parse(recievedStr);
var pw = "PASSWORD";
var encres = CryptoJS.AES.encrypt(pw, key, {iv:iv, keySize: 256, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7});
var finalStr = encres.toString();
finalStr will be : Su92ZXLm/MdOyruRnWDRqQ==
I need to make a java code that will output exactly the same output as finalStr from the javascript.
Im using bouncy castle for that.
String recievedStr = "MDk4NTY1MDAyMjg2MTU1OA==";
String pw = "PASSWORD";
AESEngine blockCipher = new AESEngine();
CBCBlockCipher cbcCipher = new CBCBlockCipher(blockCipher);
BufferedBlockCipher cipher = new PaddedBufferedBlockCipher (cbcCipher);
byte[] key = encodeBase64(recievedStr);
byte [] iv = java.util.Arrays.copyOf(key,16);
byte[] input = pw.getBytes();
ParametersWithIV pwIV= new ParametersWithIV(new KeyParameter(key),iv);
cipher.init(true, pwIV);
byte[] cipherText = new byte[cipher.getOutputSize(input.length)];
int outputLen = cipher.processBytes(input, 0, input.length, cipherText, 0);
try
{
cipher.doFinal(cipherText, outputLen);
}
catch (CryptoException ce)
{
System.err.println(ce);
}
System.out.println(new String(Base64.encodeBase64(cipherText)));
this will output : qEGQ1PC/QKxfAxGBIbLKpQ==
while I can decrypt it to the original input, that is not what i want.
I need my java code to output exactly what the javascript did.
I have 0 ideas left on how to approach this.
Thanks.
EDIT: problem was solved, I had to decode the received string to base64 instead of encoding it.
I think you are on the right track. But I think you are running with AES-128 instead of AES-256. If you have a look at Java 256-bit AES Password-Based Encryption I think maybe you can find something useful.

Encrypt text using AES in Javascript then Decrypt in C# WCF Service

I am trying to Encrypt a string using AES 128bit encryption. I have code for both Javascript and C#. The main objective is to encrypt the string using Javascript CryptoJS and then take the resultant cipher text and Decrypt it using C# AES AesCryptoServiceProvider.
Javascript Code:
function EncryptText()
{
var text = document.getElementById('textbox').value;
var Key = CryptoJS.enc.Hex.parse("PSVJQRk9QTEpNVU1DWUZCRVFGV1VVT0=");
var IV = CryptoJS.enc.Hex.parse("YWlFLVEZZUFNaWl=");
var encryptedText = CryptoJS.AES.encrypt(text, Key, {iv: IV, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7});
//var decrypted = CryptoJS.AES.decrypt(encrypted, "Secret Passphrase");
var encrypted = document.getElementById('encrypted');
encrypted.value = encryptedText;
}
C# Code:
private String AES_decrypt(string encrypted)
{
byte[] encryptedBytes = Convert.FromBase64String(encrypted);
AesCryptoServiceProvider aes = new AesCryptoServiceProvider();
aes.BlockSize = 128;
aes.KeySize = 256;
aes.Mode = CipherMode.CBC;
aes.Padding = PaddingMode.Pkcs7;
aes.Key = Key;
aes.IV = IV;
ICryptoTransform crypto = aes.CreateDecryptor(aes.Key, aes.IV);
byte[] secret = crypto.TransformFinalBlock(encryptedBytes, 0, encryptedBytes.Length);
crypto.Dispose();
return System.Text.ASCIIEncoding.ASCII.GetString(secret);
}
When using "hello" as the plain text for javascript i get this ciphertext:
uqhe5ya+mISuK4uc1WxxeQ==
When passing that into the C# application, upon running the Decrypt method i recieve:
Padding is invalid and cannot be removed.
I am stumped here and have tried many solutions resulting in the same error.
When encrypting hello through the C# encryption AES method I receive:
Y9nb8DrV73+rmmYRUcJiOg==
I thank you for your help in advance!
javascript code :
function EncryptText()
{
var text = CryptoJS.enc.Utf8.parse(document.getElementById('textbox').value);
var Key = CryptoJS.enc.Utf8.parse("PSVJQRk9QTEpNVU1DWUZCRVFGV1VVT0="); //secret key
var IV = CryptoJS.enc.Utf8.parse("2314345645678765"); //16 digit
var encryptedText = CryptoJS.AES.encrypt(text, Key, {keySize: 128 / 8,iv: IV, mode: CryptoJS.mode.CBC, padding:CryptoJS.pad.Pkcs7});
var encrypted = document.getElementById('encrypted');
encrypted.value = encryptedText;
//Pass encryptedText through service
}
C# code :
private String AES_decrypt(string encrypted,String secretKey,String initVec)
{
byte[] encryptedBytes = Convert.FromBase64String(encrypted);
AesCryptoServiceProvider aes = new AesCryptoServiceProvider();
//aes.BlockSize = 128; Not Required
//aes.KeySize = 256; Not Required
aes.Mode = CipherMode.CBC;
aes.Padding = PaddingMode.Pkcs7;
aes.Key = Encoding.UTF8.GetBytes(secretKey);PSVJQRk9QTEpNVU1DWUZCRVFGV1VVT0=
aes.IV = Encoding.UTF8.GetBytes(initVec); //2314345645678765
ICryptoTransform crypto = aes.CreateDecryptor(aes.Key, aes.IV);
byte[] secret = crypto.TransformFinalBlock(encryptedBytes, 0, encryptedBytes.Length);
crypto.Dispose();
return System.Text.ASCIIEncoding.ASCII.GetString(secret);
}
Used above code working fine !!!
Try using var Key = CryptoJS.enc.Utf8.parse("PSVJQRk9QTEpNVU1DWUZCRVFGV1VVT0="); instead of HEX.
Because actually the string you are putting in your key (and IV) and parsing is not a hex string. hex is 0 to F.
First, your Key variable in JS contains a string with 32 characters (after the odd-looking parse call). Although this might be interpreted as a 128-bit key, there is a certain chance that CryptoJS takes it as a pass phrase instead (and generates a key from it using some algorithm). So your actual key looks quite different. The string also looks suspiciously like hex-encoded, so there might be some additional confusion about its C# value. You have to make sure that you are using the same key in JS and C#.
Second, the IV variable also, after parsing, looks like a hex-encoded value. So you have to be careful what value you are using on the C# side as well.
FYI, here are the values for Key and IV after parsing:
Key = 00000000000e00000d000c0000010000,
IV = 0000000e000f0a00
Thank you "Uwe" parsing with UTF8 solved everything.
What happens if you use: var Key = CryptoJS.enc.Utf8.parse("PSVJQRk9QTEpNVU1DWUZCRVFGV1VVT0="); instead >of HEX? And what is your Key and IV in C#? Because actually the string you are putting in your key and >parsing is not a hex string. hex is 0 to F
Thank you so much!

Categories