Rewriting function to generate Java Hmac SHA512 in Node.js - javascript

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==

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

JS Bcrypt to c#

I'm trying to convert https://github.com/tutao/tutanota/blob/master/src/api/worker/crypto/Bcrypt.js to c#.
If I'm correct, the input of generateKeyFromPassphrase is as follow:
passphrase = SHA256 string
salt = Rand 16 byte string
Finally a string is returned with:
Bcrypt with passphrase+salt with 8 iterations
I have build the following functions in c#
public static string ComputeSha256Hash(string rawData)
{
// Create a SHA256
using (SHA256 sha256Hash = SHA256.Create())
{
// ComputeHash - returns byte array
byte[] bytes = sha256Hash.ComputeHash(Encoding.UTF8.GetBytes(rawData));
// Convert byte array to a string
StringBuilder builder = new StringBuilder();
for (int i = 0; i < bytes.Length; i++)
{
builder.Append(bytes[i].ToString("x2"));
}
return builder.ToString();
}
}
public static string CreateHash(string passphrase, string saltInput)
{
// Generate a salt
RNGCryptoServiceProvider provider = new RNGCryptoServiceProvider();
byte[] salt = StringToByteArray(saltInput);
provider.GetBytes(salt);
// Generate the hash
Rfc2898DeriveBytes pbkdf2 = new Rfc2898DeriveBytes(passphrase, salt, 8);
byte[] bytes = pbkdf2.GetBytes(256);
return Encoding.UTF8.GetString(bytes, 0, bytes.Length);
}
public static byte[] StringToByteArray(string str)
{
byte[] array = Convert.FromBase64String(str);
return array;
}
But my encoding isn't working, but I don't fully understand why (I'm new to c#).
Can someone point me in the right direction?
Thanks in advance.

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

What does CryptoJS.enc.Hex.parse(hash) do, and how to replicate it in Java?

Basically, I want to replicate the following javascript code in java:
let h = calculateHmac(CryptoJS.enc.Hex.parse(hash), key);
const calculateHmac = (seed, salt) => {
const hmac = CryptoJS.HmacSHA256(seed, salt);
return hmac;
}
Here's my Java code so far:
public static String encode(String key, String hash) throws Exception {
Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
SecretKeySpec secret_key = new SecretKeySpec(key.getBytes("UTF-8"), "HmacSHA256");
sha256_HMAC.init(secret_key);
return bytesToHex(sha256_HMAC.doFinal(hash.getBytes("UTF-8")));
}
My java code correctly gets the hmac, but my result is different from the js due to the CryptoJS.enc.Hex.parse(hash) part (I'm using just hash in java). What does CryptoJS.enc.Hex.parse(hash) actually do? When I print it, it seems to print the same as just hash. And how do I replicate that line in java?
For reference, I'm using the key 0000000000000000004d6ec16dafe9d8370958664c1dc422f452892264c59526 and hash 71b12b1ee6aad3763a3f844bec2512e6985f727de60224237552df77aa2d1d12. When I run it through an online hmac generator tool (for example https://www.freeformatter.com/hmac-generator.html#ad-output) I get 2d1044151d96fadd070261880e6564f7a52a33af293b57cc40fb75e099d1e232 as the output, which my java code correctly does, but is different from the js code because of the CryptoJS.enc.Hex.parse(hash) line. Please let me know what I'm missing.
EDIT: edited for clarity
Assuming that hash-value in JS is a String var we do need a way to get a byte[] out of it. The simple way of
byte[] hashValue = hash.getBytes("UTF-8");
does not work as we need the value and not the ASCII-code of the characters. Simply use the hexStringToByteArray method to
transform a hex-string representation to a byte array ("bytesToHex" is a service method to print out the byte array):
import java.io.UnsupportedEncodingException;
public class ParseHexstring {
public static void main(String[] args) throws UnsupportedEncodingException {
System.out.println("https://stackoverflow.com/questions/62497931/what-does-cryptojs-enc-hex-parsehash-do-and-how-to-replicate-it-in-java");
String hash = "124d45ff6789ac01234543789012";
byte[] hashGetBytes = hash.getBytes("UTF-8");
byte[] hashValue = hexStringToByteArray(hash);
System.out.println("hash String : " + hash);
System.out.println("hashGetBytes: " + bytesToHex(hashGetBytes));
System.out.println("hashValue : " + bytesToHex(hashValue));
}
public static byte[] hexStringToByteArray(String s) {
int len = s.length();
byte[] data = new byte[len / 2];
for (int i = 0; i < len; i += 2) {
data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
+ Character.digit(s.charAt(i + 1), 16));
}
return data;
}
private static String bytesToHex(byte[] bytes) {
StringBuffer result = new StringBuffer();
for (byte b : bytes) result.append(Integer.toString((b & 0xff) + 0x100, 16).substring(1));
return result.toString();
}
}
That the output:
https://stackoverflow.com/questions/62497931/what-does-cryptojs-enc-hex-parsehash-do-and-how-to-replicate-it-in-java
hash String : 124d45ff6789ac01234543789012
hashGetBytes: 31323464343566663637383961633031323334353433373839303132
hashValue : 124d45ff6789ac01234543789012

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

Categories