symmetric and asymmetric encryption between Angular 2 and Spring Boot - javascript

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

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"}

Rewriting function to generate Java Hmac SHA512 in Node.js

I have the following code in java for encrypting the plain text with key:
public static String calculateHMAC(String signatureKey, String textToHash) throws GeneralSecurityException {
// Convert Hex string to byte array
byte[] signatureKeyInByteArray = hexStringToByteArray(signatureKey);
SecretKeySpec secretKeySpec = new SecretKeySpec(signatureKeyInByteArray, "HmacSHA512");
Mac mac = Mac.getInstance("HmacSHA512");
mac.init(secretKeySpec);
return Base64.getEncoder().encodeToString(mac.doFinal(textToHash.getBytes()));
}
In node, what i am doing:
function getHmacKey(data) {
const hash = crypto.createHmac('sha512', secretKey);
hash.update(data, 'utf8');
return hash.digest('base64'); }
But getting different results, can't figure out what's wrong.
With string signatureKEy = "keyy" ,
textToHash = "text" ,
Outputs:
Java:
TKVB2boNHHwkvDtmjRw2p8DNe0XWsBvodWJZAmKK5ey2Xk0+u+q3imug25Dsai32SGy5bnIr+owkWPgby7csoQ==
Node:
IMbZJ6hcShYMLuNi9BYWY9PB3w8/d7tOneN+ZzeSPSozgVDQYrm1x6wbKIdZIomI7lhfU4/Y10yzZgxDfmTsFA==

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 }*/

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

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!

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