C# Android HMAC result - java

I am using the following code in c# for HMAC conversion:
string RawData = "data";
string sharedKey = "my-key";
byte[] signature = Encoding.UTF8.GetBytes(RawData);
var KeyByteArray = Encoding.UTF8.GetBytes(sharedKey);
using (HMACSHA256 hmac = new HMACSHA256(KeyByteArray))
{
byte[] signatureBytes =hmac.ComputeHash(signature);
var ContentBase64String =(Convert.ToBase64String(signatureBytes));
Console.WriteLine(ContentBase64String );
Console.ReadKey();
}
And the following in Java:
String RawData="data";
String Key="my-key";
byte[] KeyByteArray=Key.getBytes("UTF-8");
byte[] signature=RawData.getBytes("UTF-8");
Mac sha256_HMAC;
sha256_HMAC = Mac.getInstance("HmacSHA256");
SecretKeySpec secret_key = new SecretKeySpec(KeyByteArray, "HmacSHA256");
sha256_HMAC.init(secret_key);
String ContentBase64String = Base64.encodeToString(sha256_HMAC.doFinal(signature),Base64.URL_SAFE|Base64.NO_WRAP);
The output for C# is as follows:
The output for android is as follows:
I have been it at it for many days and can't figure out what I am doing wrong as the outputs differ though they should be the same.Also, when I remove the - in the key the results are the same. I know I might be missing something simple but thought another pair of eyes might see my mistake. Thanks.

Those answers are the same (as far as the bytes contained in the HMAC goes). For the difference in the + and - characters in the output, you specifically told it to do that by specifying the Base64.URL_SAFE flag.
For future readers of this question: When in doubt, consult the documentation for everything you don't perfectly understand.

Related

(Java to Javascript) javax.crypto.Cipher equivalent code in Nodejs Crypto Javascript

I'm trying to convert below java code into nodejs.
private static String TRANS_MODE = "Blowfish";
private static String BLOWFISH_KEY = "BLOWFISH_KEY";
public static String encrypt(String password) throws Exception {
SecretKeySpec keySpec = new SecretKeySpec(BLOWFISH_KEY.getBytes("Windows-31J"),TRANS_MODE);
Cipher cipher;
cipher = Cipher.getInstance(TRANS_MODE);
cipher.init(Cipher.ENCRYPT_MODE, keySpec);
byte[] passByte;
passByte = cipher.doFinal(password.getBytes("Windows-31J"));
return new String(Hex.encodeHex(passByte));
}
Here is what I was able to figure out-
const crypto = require('crypto');
function encrypt(password)
var fcKey = "BLOWFISH_KEY";
var cipher = crypto.createCipher('BF-CBC', fcKey, "");
var encrypted = cipher.update(password,'ascii','hex');
encrypted += cipher.final('hex');
return encrypted;
I'm not able to get same output. For example if
password= "password01"
Java Code output - fe0facbf8d458adaa47c5fe430cbc0ad
Nodejs Code output - ae5e8238c929b5716566e97fa35efb9b
Can someone help me figure out the problem ??
Notice that crypto.createCipher(algorithm, password[, options]) is deprecated and should not be used.
Where the SecretKeySpec(..) in java takes a binary key as input, the createCipher(..) in js takes a "password" as input, and behind the scene tries to derive a binary key using MD5. So your actually key used in the two programs ends up being different. The js methode
also tries to derive an IV from the password, which is bad practice and different from your java code.
In js you need to use the crypto.createCipheriv() instead. And when you are at it, you also need to consider if an iv is needed - both in Java and in js.

AES-192-ECB encrypted in node.js crypto and decrypt in java

I give up in trying to research for solutions regarding my problem. I've done my part and search about this problem and encountered solutions (like this https://stackoverflow.com/a/21252990/5328303) which was really the same problem as mine but he is using aes-128-ecb.
I cannot get the solution to work for aes-192-ecb mode.
Here's the node.js part (take note I cannot change this part of the code since this is a third party provider and I'm very limited.)
console.log(encrypt("hello world"))
function encrypt(data) {
const aesKey = '4327601417486622'
const algorithm = 'aes-192-ecb'
const cipher = crypto.createCipher(algorithm, aesKey)
const crypted = cipher.update(data, 'utf-8', "hex") + cipher.final("hex")
return crypted
}
// expected: 066c47b162cd5c464ea9805742c1af9b
And here's my Java function:
public static String decrypt(String seed, String encrypted) throws Exception {
byte[] keyb = seed.getBytes("UTF-8");
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] thedigest = md.digest(keyb);
SecretKeySpec skey = new SecretKeySpec(thedigest, "AES");
Cipher dcipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
dcipher.init(Cipher.DECRYPT_MODE, skey);
byte[] clearbyte = dcipher.doFinal(toByte(encrypted));
return new String(clearbyte);
}
The java code above works well if I use aes-128-ecb on my node code but it cannot decode when I'm using aes-192-ecb or even aes-256-ecb.
Maybe I just don't quite understand openssl EVP_BytesToKey function since I read that crypto.createCipher() uses it when encrypting. It also said that it is hashing the key with MD5 which I'm currently doing with the java code.
Also I was thinking that the aesKey that I have is only 16 bytes and maybe that's why it won't work with AES-192 and only works with AES-128. I want to understand how openssl/crypto does it when I'm only passing a 16 byte key with the required 24 bytes key for AES-192 since I cannot change the node.js code.
Am I on the right track? Can anyone guide me?
Thank you!

How to change the Node JS HMAC signature creation to Java

I am very rarely use Java and I have an requirement of converting a function in Node.js which builds the HMAC signature for REST POST call to Java
Node JS function :
function buildSignature(buf, secret) {
const hmac = crypto.createHmac('sha256', Buffer.from(secret, 'utf8'));
hmac.update(buf);
return hmac.digest('hex');
}
What I have currently done is:
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);
This is not working out to be the same.
It's my suspicion that hmac.digest('hex'); isn't base 64 encoded. I'd try converting the response of sha256_HMAC.doFinal(message.getBytes()) to hex instead.
How to convert a byte array to a hex string in Java? 's
The answer I would prefer there (since I use Guava in many projects) is
final String hex = BaseEncoding.base16().lowerCase().encode(bytes);
Adapted to your question would be
final String hash = BaseEncoding.base16().lowerCase().encode(sha256_HMAC.doFinal(message.getBytes()));
Mac sha256_HMAC;
sha256_HMAC = Mac.getInstance("HmacSHA256");
SecretKeySpec secret_key = new SecretKeySpec(secret.getBytes("UTF-8"), "HmacSHA256");
sha256_HMAC.init(secret_key);
byte[] hash = sha256_HMAC.doFinal(payload.getBytes());
return new String(Hex.encodeHex(hash));

How to replicate Java encryption in PHP?

Some background of what I'm trying to accomplish.
Part 1.
PHP server communicates with a Java-based device. PHP uses OpenSSL to generate a public/private keypair, then sends the public key to the device which in turn gives back an encrypted macKey (generated using the public key), encoded in base64. PHP now needs to base64-decode and decrypt the macKey using the private key.
What is the equivalent of the below Java code snippet in PHP?
String base64EncodedMacKey = "LkvTT9LFj5lcxRRB8KrwwN906fSIDDcJvQK3E7a5PbR+Ox9WnslOs32jSCC9FkE8ouvr2MfWwtppuZmoPjaxwg3yAQI4UN3T1loISuF2VwKWfJ45fywbK9bNnD5Cw7336mjoGctv77Tg3JXPrsRwgMGIlBsNwdt1B0wgT4MMMAjl32TnBI3iwQ94VTMHffrK+QToddTahRHHoVsr3FVrETdiqKXdkiX1jES53im5lrXYIsY89UFkGzPo+3u4ijKIQWSLvYnA5wXI128gFHKxKYS82MbJDUn9i1RVFsGaP6T3nQRSX5SZNpSe5yGFWwMgYOx0KXMgET82FeaL2hfWuw==";
byte[] base64DecodedMacKey = DatatypeConverter.parseBase64Binary(base64EncodedMacKey);
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.DECRYPT_MODE, keypair.getPrivate());
byte[] macKey = cipher.doFinal(base64DecodedMacKey);
Here's what I attempted in PHP, however I'm confused about using byte array versus string when decrypting the macKey
$macKey = 'LkvTT9LFj5lcxRRB8KrwwN906fSIDDcJvQK3E7a5PbR+Ox9WnslOs32jSCC9FkE8ouvr2MfWwtppuZmoPjaxwg3yAQI4UN3T1loISuF2VwKWfJ45fywbK9bNnD5Cw7336mjoGctv77Tg3JXPrsRwgMGIlBsNwdt1B0wgT4MMMAjl32TnBI3iwQ94VTMHffrK+QToddTahRHHoVsr3FVrETdiqKXdkiX1jES53im5lrXYIsY89UFkGzPo+3u4ijKIQWSLvYnA5wXI128gFHKxKYS82MbJDUn9i1RVFsGaP6T3nQRSX5SZNpSe5yGFWwMgYOx0KXMgET82FeaL2hfWuw==';
$base64DecodedMacKey = base64_decode($macKey);
openssl_private_decrypt($base64DecodedMacKey, $decrypted, $privateKey);
The $decrypted above holds some binary data as it appears, so I'm unsure whether I need to convert it into a byte array or treat it as a string...
Part 2.
Each request has a counter. The macKey in Java code above is used to create a MAC value out of the counter.
What is the equivalent of the below Java code snippet in PHP?
int counter = 0;
String nextCounter = String.valueOf(++counter);
SecretKeySpec signingKey = new SecretKeySpec(macKey, "AES");
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(signingKey);
byte[] counterMac = mac.doFinal(nextCounter.getBytes("UTF-8"));
String base64EncodedMac = DatatypeConverter.printBase64Binary(counterMac);
The base64EncodedMac above is finally sent to the device to validate communication.
I've tried googling different solutions, however I've not been successful in generating a valid base64EncodedMac string in PHP for the device to approve it.
Found the solution myself. For Part 1, I chose to use phpseclib to generate the public/private keys and to specify the encryption algorithm. Decrypting macKey:
$rsa = new Crypt_RSA();
$keys = $rsa->createKey(2048);
// [...]
$macKey = base64_decode($base64EncodedMacKey);
$rsa->setEncryptionMode(CRYPT_RSA_ENCRYPTION_PKCS1);
$rsa->loadKey($keys['privatekey']);
$decryptedMac = $rsa->decrypt($macKey);
Followed by Part 2:
$counter = 0;
$hmac = hash_hmac('sha256', ++$counter, $decryptedMac, true);
$counterMac = base64_encode($hmac);
The main confusing part was that in Java, HMAC was done out of byte array, while in PHP the hash_hmac function expects a String as its 2nd parameter, so using unpack() was not sufficient. However, it seems to have worked with passing the $counter directly. It was also important to use the 4th parameter as TRUE to return raw data.

Java method which can provide the same output as Python method for HMAC-SHA256 in Hex

I am now trying to encode the string using HMAC-SHA256 using Java. The encoded string required to match another set of encoded string generated by Python using hmac.new(mySecret, myPolicy, hashlib.sha256).hexdigest(). I have tried
Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
SecretKeySpec secretKey = new SecretKeySpec(secret.getBytes(), "HmacSHA256");
sha256_HMAC.init(secretKey);
byte[] hash = sha256_HMAC.doFinal(policy.getBytes());
byte[] hexB = new Hex().encode(hash);
String check = Hex.encodeHexString(hash);
String sha256 = DigestUtils.sha256Hex(secret.getBytes());
after I print them out, hash, hexB, check and sha256 didn't provide the same result as the following Python encryption method
hmac.new(mySecret, myPolicy, hashlib.sha256).hexdigest()
So, I have try to looking for the library or something that work similar to the above Python function. Can anybody help me out?
Are you sure your key and input are identical and correctly encoded in both java and python?
HMAC-SHA256 works the same on both platforms.
Java
Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
SecretKeySpec secretKey = new SecretKeySpec("1234".getBytes(), "HmacSHA256");
sha256_HMAC.init(secretKey);
byte[] hash = sha256_HMAC.doFinal("test".getBytes());
String check = Hex.encodeHexString(hash);
System.out.println(new String(check));
Output
24c4f0295e1bea74f9a5cb5bc40525c8889d11c78c4255808be00defe666671f
Python
print hmac.new("1234", "test", hashlib.sha256).hexdigest();
Output
24c4f0295e1bea74f9a5cb5bc40525c8889d11c78c4255808be00defe666671f

Categories