Node.js Hmac SHA256 base64 of string - java

I'm making an app in java and a server with node and as an authentication method I would like to compare two strings.
In java i'm doing this:
try {
String secret = "secret";
String message = "Message";
Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
SecretKeySpec secret_key = new SecretKeySpec(secret.getBytes(), "HmacSHA256");
sha256_HMAC.init(secret_key);
String hash = Base64.encodeBase64String(sha256_HMAC.doFinal(message.getBytes()));
System.out.println(hash);
} catch (Exception e){
System.out.println("Error");
}
But I'm still pretty new to node.js and I'm trying to figure out how to do the same there. This is what I've got:
var crypto = require('crypto');
var sha256 = crypto.createHash('HMAC-SHA256').update('Message').digest("base64");
How can I make them do the same? I'm still missing the salt in node.js.
Suggestions?
EDIT:
The answer below helped me find the solution. If other android users has this problem then this code worked for me:
try {
String secret = "secret";
String message = "Message";
Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
SecretKeySpec secret_key = new SecretKeySpec(secret.getBytes(), "HmacSHA256");
sha256_HMAC.init(secret_key);
byte[] s53 = sha256_HMAC.doFinal(message.getBytes());
String hash = Base64.encodeToString(s53, Base64.DEFAULT);
Log.e("beadict", hash);
} catch (Exception e){
System.out.println("Error");
}
And this in node:
var crypto = require('crypto');
var hash = crypto.createHmac('SHA256', "secret").update("Message").digest('base64');

You can use this line:
let test = crypto.createHmac('sha256', "key").update("message").digest("base64");
Convert to base64 last.

If you want to use a HMAC then you need to use the method crypto.createHmac(algorithm, key).
I'm still missing the salt in node.js
It seems that you do not use the salt in your Java code...

Related

Java equivalent of CryptoJS.enc.Base64 for encryption

've been trying to port some NodeJS code to Java in order to make HMAC signed requests to an API. I've been able to simplify the code to a point were encoding is clearly affecting the output.
Here's my code in JavaScript:
var APIKey = "secret";
var secretByteArray = CryptoJS.enc.Base64.parse(APIKey);
var hash = CryptoJS.HmacSHA256("Message", secretByteArray);
var hashInBase64 = CryptoJS.enc.Base64.stringify(hash);
//alert(hashInBase64);
document.write(hashInBase64);
Here's the Java Code:
try {
String secret = "secret";
String message = "Message";
byte[] secretByteArray = Base64.getEncoder().encode(secret.getBytes());
//byte[] secretByteArray = Base64.encodeBase64(secret.getBytes("utf-8"), true);
Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
SecretKeySpec secret_key = new SecretKeySpec(secretByteArray, "HmacSHA256");
sha256_HMAC.init(secret_key);
String hash = Base64.getEncoder().encodeToString(sha256_HMAC.doFinal(message.getBytes()));
System.out.println(hash);
}
catch (Exception e){
System.out.println("Error");
}
**What to use instead of Base64.getEncoder().encode? **

How can I get C# AES encryption method to return the same results as a given java method?

I was given following java code:
private static String key = "0123456789ABCDEF0123456789ABCDEF"; // Not real key
public static String Encrypt(String text)
{
byte[] encrypted, bytekey = hexStringToByteArray(key);
SecretKeySpec sks = new SecretKeySpec(bytekey, "AES");
try
{
Cipher cipher = Cipher.getInstance("AES");
cipher.init(1, sks, cipher.getParameters());
encrypted = cipher.doFinal(text.getBytes());
}
catch (Exception e)
{
System.out.println("Error using AES encryption with this Java instance");
e.printStackTrace();
System.exit(1);
return null;
}
String encryptedText = byteArrayToHexString(encrypted);
return encryptedText;
}
Passing Password123 into this returns 6836A38816248A0C7DD89400A997251A. I'm not looking for comments on the security of this. I'm aware. I didn't write it, I just need to duplicate it.
I have to create C# code that has the same functionality. I have tried many code snippets from all over SO and other web sites. None of them produce the same output when given a specific input.
I added some debug statements to the java code to get the following information about the algorithm:
sks.getAlgorithm(): AES (duh)
sks.getFormat(): RAW
cipher.getAlgorithm(): AES (again, duh)
cipher.getBlockSize(): 16
cipher.getParameters(): null
cipher.getIV(): null (I think this might be my primary issue)
Here is one of the C# methods I found that looked promising:
private const string key = "0123456789ABCDEF0123456789ABCDEF"; // Not real key
private static byte[] encryptionKey= new byte[16];
static void SetupKey()
{
var secretKeyBytes = Encoding.UTF8.GetBytes(key);
Array.Copy(secretKeyBytes, encryptionKey, Math.Min(encryptionKey.Length, secretKeyBytes.Length));
}
public static String Encrypt3(String secret)
{
SetupKey();
byte[] inputBytes = UTF8Encoding.UTF8.GetBytes(secret);
using (MemoryStream ms = new MemoryStream())
{
using (AesManaged cryptor = new AesManaged())
{
cryptor.Mode = CipherMode.CBC;
cryptor.Padding = PaddingMode.PKCS7;
cryptor.KeySize = 128;
cryptor.BlockSize = 128;
using (CryptoStream cs = new CryptoStream(ms, cryptor.CreateEncryptor(encryptionKey, null), CryptoStreamMode.Write))
{
cs.Write(inputBytes, 0, inputBytes.Length);
}
byte[] encryptedContent = ms.ToArray();
byte[] result = new byte[encryptedContent.Length];
System.Buffer.BlockCopy(encryptedContent, 0, result, 0, encryptedContent.Length);
return ByteArrayToHexString(result);
}
}
}
Every time I run this code, I get a different result, even though I'm passing null into the Initialization Vector(IV) parameter of cryptor.CreateEncryptor(). Is the AesManaged object using an internal IV even though I told it to use null? If I try to set the IV to null, I get an error.
What do I need to do to get the C# code to consistently return the same result as the java code?
NOTE: Both methods use HexStringToByteArray and ByteArrayToHexString. The original author of the java code, for some reason, wrote his own byte/hex converters. I recreated them in C#, but they work just like the build in functions.

Decrypting strings from node.js in Java?

I have the following simple encryption code running in node.js:
var crypto = require('crypto');
var encKey = "FOO"; // Not the real key. Assume it works though.
var encrypt = function(str) {
var cipher = crypto.createCipher('aes-256-cbc', encKey);
var crypted = cipher.update(str, 'utf-8', 'hex');
crypted += cipher.final('hex');
return crypted;
};
Which I can also decrypt as below:
var crypto = require('crypto');
var encKey = "FOO"; // Not the real key. Assume it works though.
var decrypt = function(str) {
var decipher = crypto.createDecipher('aes-256-cbc', encKey);
var decrypted = decipher.update(str, 'hex', 'utf-8');
decrypted += decipher.final('utf-8');
return decrypted;
};
This all works fine. Strings are encrypting and decrypting as expected. But now I am faced with task of decrypting encrypted strings from this node.js code, in Java. And that is where things are going wrong and I am not sure why.
For decryption, My Java code looks like this:
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.security.MessageDigest;
import java.util.Arrays;
private static final String encKey = "FOO";
private static SecretKeySpec secretKey;
private static byte[] key;
public static String decrypt(String str) throws Exception {
String hexDecodedStr = new String(Hex.decodeHex(str.toCharArray()));
setKey(encKey);
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, secretKey);
return new String(cipher.doFinal(hexDecodedStr.getBytes()));
}
private static void setKey(String myKey) throws Exception {
MessageDigest sha = null;
try {
key = myKey.getBytes("UTF-8");
sha = MessageDigest.getInstance("SHA-1");
key = sha.digest(key);
key = Arrays.copyOf(key, 16);
secretKey = new SecretKeySpec(key, "AES");
}
catch (Exception e) {
throw e;
}
}
And it doesn't work. It seems like no matter what I try, I end up with some exception on the cipher.doFinal() call, or the String I get back is totally wrong. I know the node.js code is using aes-256-cbc, while the Java code is using AES/ECB/PKCS5Padding instead of AES/CBC/PKCS5Padding, but when I tried to use AES/CBC/PKCS5Padding, it was requiring an InitVector which I didn't have in node.js so I was unsure of how to proceed. Is node making an InitVector under the hood if not provided with one? Am I missing something totally obvious?
You seems to have the same issue as others OpenSSL encryption failing to decrypt C#
As far I understood the docs, the crypto libeary uses openssl. The openssl creates IV and key from the password using its EVP_BytesToKey function and random salt (not just hash). As dave pointed out, the crypto library uses no salt.
the output of openssl is Salted_{8 bytes salt}{ciphertext} so check what is output of the cipher ( I am unable to do it now)
I wrote a small article how to encrypt properly in Java

Converting PHP hmac signature to java

I'm converting a php script to java (for android) but find myself stuck converting the hmac signature process.
PHP which gives correct sign:
$secret = "lT4fhviR7ILvwGeiBJgolfYji1uz/f7B6HQWaWQWVl/sWEz3Kwt4QjzCHWE+MBENOmtgBS6PlN87s+1d7/8bRw==";
$nonce = "1388256620813308";
$postdata = "nonce=1388256620813308";
$path = "/0/private/Balance";
$sign = hash_hmac('sha512', $path . hash('sha256', $nonce . $postdata, true), base64_decode($this->secret), true);
echo $sign;
Hmac = 2IVoBCoadCEivxKVRB/4quJET4DoZV4JdY6bMC2oEYJZuygF5JiAhGrxVMyw2yPhz+KdiwvbzV43cicGamzr2A==
Which is correct and accepted signature
Java (with invalid sign):
String secret = "lT4fhviR7ILvwGeiBJgolfYji1uz/f7B6HQWaWQWVl/sWEz3Kwt4QjzCHWE+MBENOmtgBS6PlN87s+1d7/8bRw==";
String nonce = "1388256620813308";
String postdata = "nonce=1388256620813308";
String path = "/0/private/Balance";
// hash nonce + data
MessageDigest md = MessageDigest.getInstance("SHA-256");
md.update((nonce+postdata).getBytes());
byte[] digest = md.digest();
String baseString = path + new String(digest); //this is probably root of evil
// HMAC
Mac mac = Mac.getInstance("HmacSHA512");
SecretKey secretKey = new SecretKeySpec(Base64.decode(secret, Base64.DEFAULT), "HmacSHA512");
mac.init(secretKey);
String sign = new String(Base64.encodeToString(mac.doFinal(baseString.getBytes()), Base64.DEFAULT)).trim();
Log.d(TAG, sign);
Hmac = 7ZQfn+fqMpMEFN5Z/T5UwcqP1uo0JOyAVSn4HEBeE/KotnEf4a5bPOWriiC//gdoEg2kOe60EIr3Lv7irXuejw==
The problem is in the java string conversion of the bytes (even if I add "UTF-8" as characted encoding in getBytes). I know this because if I don add path to the hmac, and just feed it with digest without the string conversion the signature matches.
After posting question I did a quick and dirty test to add bytes from path manually to a new bytes array
byte[] digest = md.digest();
byte[] pBytes = path.getBytes();
int L = digest.length + pBytes.length;
byte[] message = new byte[L];
for (int i=0;i<pBytes.length;i++) {
message[i] = pBytes[i];
}
for (int i=pBytes.length,n=0; n<digest.length; n++) {
message[i+n] = digest[n];
}
String sign = new String(Base64.encodeToString(mac.doFinal(message), Base64.NO_WRAP));
And voilĂ ; the hmac sign matches!
I have solved my problem but keeping question unanswered for some day to say if a better answer is provided that sheds light on this.

Implementing AES Encryption/Decryption around existing PHP System on Android

I'm expanding an iOS project over to Android. My existing application communicates with a server via PHP using an AES encryption system.
Here are the functions that I am using on the PHP side:
Encrypt
function cryptAESEncrypt($string,$key) {
$key = md5($key);
$iv = "1234567890123436"; //IV isn't needed if MCRYPT_MODE is ECB (What we are using)
$data = $data = base64_encode($string);
$algorythm = MCRYPT_RIJNDAEL_128;
$mode = MCRYPT_MODE_ECB;
$encrypted = mcrypt_encrypt(MCRYPT_RIJNDAEL_128,$key,$data,MCRYPT_MODE_ECB,$iv);
return base64_encode($encrypted);
}
Decrypt
function cryptAESDecrypt($string,$key) {
$key = md5($key);
$iv = "1234567890123436"; //IV isn't needed if MCRYPT_MODE is ECB (What we are using)
$data = base64_decode($string);
$algorythm = MCRYPT_RIJNDAEL_128;
$mode = MCRYPT_MODE_ECB;
$decrypted = mcrypt_decrypt(MCRYPT_RIJNDAEL_128,$key,$data,MCRYPT_MODE_ECB,$iv);
return base64_decode($decrypted);
}
The general flow of the process is:
md5 hash the $key (brings it down to 16 characters regardless)
Base64 Encode the $string
Encrypt the Base64'ed using 128Bit AES/RIJNDAEL in ECB mode (no IV)
Base64 the encrypted data and returns it as a string.
The decryption works the same but in reverse.
Now I'm just playing with samples but don't seem to be having much luck. I've encrypted the string "test" in PHP using that function ("test" was the key too - MD5'ed to 098f6bcd4621d373cade4e832627b4f6) and I am given the output of "ijzLe/2WgbaP+n3YScQSgQ==".
Now what I tried in Java didn't work as I get an incorrect key length error but I had more luck with a previous snippet earlier. Here's what I had anyway:
String key = "test";
String in = "ijzLe/2WgbaP+n3YScQSgQ==";
SecretKeySpec skeySpec = new SecretKeySpec(md5(key).getBytes(), "AES");
Cipher cipher = Cipher.getInstance("AES/ECB/NoPadding");
cipher.init(Cipher.DECRYPT_MODE, skeySpec);
byte[] encryptedByteArray = Base64.decode(in.getBytes(),0);
byte[] decryptedByteArray = cipher.doFinal(encryptedByteArray);
String decryptedData = new String(Base64.decode(decryptedByteArray, 0));
Log.v("NOTE","Data: "+decryptedData);
As I said though, that doesn't work. Now my question is, is there anybody that can help me make my Java code work with the supplied PHP code as I can't change that (had other code working using different PHP snippets).
Thanks to Duncan in the comments I found out the issue was with my MD5 hash function..
Found a working version for reference:
public String md5(String s) {
if (s != null)
{
try { // Create MD5 Hash
MessageDigest digest = java.security.MessageDigest .getInstance("MD5");
digest.update(s.getBytes());
byte messageDigest[] = digest.digest();
// Create Hex String
StringBuffer hexString = new StringBuffer();
for (int i = 0; i < messageDigest.length; i++) {
String h = Integer.toHexString(0xFF & messageDigest[i]);
while (h.length() < 2)
h = "0" + h;
hexString.append(h);
}
return hexString.toString();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return "";
}
return "";
}

Categories

Resources