Converting PHP hmac signature to java - 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.

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? **

C# to Java Base64 and HmacSHA256 encryption

I have the following code in C# which generates a hash value from a Base64 encoded string.
var hmacSha256 = new System.Security.Cryptography.HMACSHA256 { Key = Convert.FromBase64String(key) };
string verb = "post";
string resourceType = "docs";
string resourceId = "dbs/ToDoList/colls/Items";
string date = DateTime.UtcNow.ToString("R");
string payLoad = string.Format(System.Globalization.CultureInfo.InvariantCulture, "{0}\n{1}\n{2}\n{3}\n{4}\n",
verb.ToLowerInvariant(),
resourceType.ToLowerInvariant(),
resourceId,
date.ToLowerInvariant(),
""
);
byte[] hashPayLoad = hmacSha256.ComputeHash(System.Text.Encoding.UTF8.GetBytes(payLoad));
string signature = Convert.ToBase64String(hashPayLoad);
string authToken = System.Web.HttpUtility.UrlEncode(String.Format(System.Globalization.CultureInfo.InvariantCulture, "type={0}&ver={1}&sig={2}",
keyType,
tokenVersion,
signature));
This works perfectly fine and I wanted to convert it to Java code for my Android app. I checked references from these sources-
C# vs Java HmacSHA1 and then base64
c# and java - difference between hmacsha256 hash
and wrote below code in Java-
String restServiceVersion = "2017-02-22";
String verb = "post";
String resourceType = "docs";
String resourceId = "dbs/ToDoList/colls/Items";
String dateString = org.apache.http.impl.cookie.DateUtils.formatDate(new Date(System.currentTimeMillis()));
String gmtIndex = "GMT";
int index = dateString.indexOf(gmtIndex);
String dateStringFinal = dateString.substring(0, index + 3).toLowerCase();
String payLoad = verb +"\n" + resourceType + "\n" + resourceId + "\n" + dateStringFinal + "\n\n";
System.out.println(payLoad);
String secretAccessKey = MASTER_KEY;
String data = payLoad;
byte[] secretKey = Base64.decode(secretAccessKey, Base64.DEFAULT);
SecretKeySpec signingKey = new SecretKeySpec(secretKey, "HmacSHA256");
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(signingKey);
byte[] bytes = data.getBytes("UTF-8");
byte[] rawHmac = mac.doFinal(bytes);
String authToken = "type=master&ver=1.0&sig=" + Base64.encodeToString(rawHmac, Base64.DEFAULT);
The authToken value generated in Java does not match with C#. Also the byte array generated from Base64 decoding differs.
I am not sure if this is the correct approach. Can someone please take a look? All I need is to convert the above working C# code to Java for my Android app.
Most likely explanation: you have invisible unicode characters (e.g. "zero-width non-joiner") embedded in your BASE64 strings. One platform is stripping these out when base-64 decoding, the other is not, resulting in different keys.

Alternative to PHP Encryption in Java

I am having a PHP which is working fine and I want to replicate the same and want to do it using java.
My PHP code uses MCRYPT_RIJNDAEL_256 :
$iv = "42309842389462374623784692423642";
$key = "asfuyjkdhfabcdef";
$text = "0007";
$block = mcrypt_get_block_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_CBC);
$padding = $block - (strlen($text) % $block);
$text .= str_repeat(chr($padding), $padding);
$crypttext = mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $key, $text, MCRYPT_MODE_CBC, $iv);
$crypttext64=base64_encode($crypttext);
echo $crypttext64;
which returns: KdL2lWUGqy+UqLxbe9VTS6OCgvnJFn1jtMCgkj1434A=
But while encrypting using java :
byte[] sessionKey = "asfuyjkdhfabcdef".getBytes(Charset.forName("ASCII"));
byte[] iv = "42309842389462374623784692423642".getBytes(Charset.forName("ASCII"));
String plaintext="0007";
String data = new String(new char[28]).replace("\0", Character.toString ((char) 28));
String newdata = plaintext+data;
newdata = newdata.substring(0,32);
byte [] dataBytes = newdata.getBytes(Charset.forName("ASCII"));
System.out.println(dataBytes);
PaddedBufferedBlockCipher cipher = new PaddedBufferedBlockCipher(
new CBCBlockCipher(new RijndaelEngine(256)),new PKCS7Padding());
cipher.init(true, new ParametersWithIV(new KeyParameter(sessionKey), iv));
byte[] encrypted = new byte[cipher.getOutputSize(dataBytes.length)];
int oLen = cipher.processBytes(dataBytes, 0, dataBytes.length, encrypted, 0);
cipher.doFinal(encrypted, oLen);
String s = new String(encrypted);
System.out.println(Base64.getEncoder().encodeToString(s.getBytes()));
I am getting below output:
KdL2lWUGqy+UqLxbe9VTS6OCgvnJFn1jtMCgkj1434DMpLehN7ve0AhnMOQtT1fLcZIUfM9rF/iVOKj6UtgwyA==
Both the outputs are different.
Please suggest the way so that I can get the same output in JAVA also.
You seem to try and perform the padding twice:
String data = new String(new char[28]).replace("\0", Character.toString ((char) 28));
String newdata = plaintext+data;
newdata = newdata.substring(0,32);
byte [] dataBytes = newdata.getBytes(Charset.forName("ASCII"));
System.out.println(dataBytes);
seems to be an ill written attempt to perform the PKCS#7 padding, which you already do in your code (new PKCS7Padding()).
Just replace it by:
plaintext.getBytes(StandardCharsets.US_ASCII)
As you can see, the first part of the base 64 is identical in both PHP and Java. Note that PHP doesn't contain PKCS#7 padding, which is why this was coded using str_repeat.
Furthermore the encrypted array should be directly put into the encodeToString method, first converting to String and then back to byte[] again can only loose data. So strip out s in the following code:
String s = new String(encrypted);
System.out.println(Base64.getEncoder().encodeToString(s.getBytes()));

Java Digital Signature does not match C# (SHA1withRSA)

So I am not the Crypto wizard by any means but here is some code I have that works in C# but does not return the same b64 string in Java.
c#
string _Cert = "long b64 string here";
string _Pass = "my password";
string lvreturn = "Test";
byte[] lvCertBytes = Convert.FromBase64String(_Cert);
X509Certificate2 lvCertFromBytes = new X509Certificate2(lvCertBytes, _Pass);
SHA1Managed lvSHA1 = new SHA1Managed();
byte[] lvData = Encoding.Unicode.GetBytes(lvReturn);
byte[] lvHash = lvSHA1.ComputeHash(lvData);
RSACryptoServiceProvider lvCryptoProvider = (RSACryptoServiceProvider)lvCertFromBytes.PrivateKey;
byte[] lvSignedBytes = lvCryptoProvider.SignHash(lvHash, CryptoConfig.MapNameToOID("SHA1"));
string lvToken = Convert.ToBase64String(lvSignedBytes);
Java
String certB64 = "long b64 string here";
char[] Pass = "text password".toCharArray();
String alias = "guid looking ID here";
String plaintext = "Test";
byte[] certbytes = Base64.getDecoder().decode(certB64);
InputStream in = new ByteArrayInputStream(certbytes);
KeyStore keystore = KeyStore.getInstance("PKCS12");
keystore.load(in,Pass);
KeyStore.PrivateKeyEntry pvk = (KeyStore.PrivateKeyEntry)keystore.getEntry(alias, new KeyStore.PasswordProtection(Pass));
PrivateKey pkey = (PrivateKey)pvk.getPrivateKey();
Signature rsa = Signature.getInstance("SHA1withRSA");
rsa.initSign(pkey);
rsa.update(plaintext.getBytes());
System.out.println("Hash: " + Base64.getEncoder().encodeToString(rsa.sign()));
I have Cert.pfx file that I want to use to use the privatekey to encrypt a https auth segment. I am just ripping the file to a base64 string and stuffing it into the "_Cert" var in C#. I do the same in Java. I want to sign the plaintext message using the private key of the cert and SHA1. The C# code below works and the https server provides a response. Java however is not spitting out the same base64 encoded string. Thanks for some help!
Update: I found a link to another post that is the same as mine but with a couple small diffs, and I didn't want to necro post on it. I followed it exactly removing the messagedigest piece of my original code. I tried reading directly from the pfx file or using the b64 string directly in the code. I am still not getting the same between Java and C#. At this point it has to be something small I am missing with encoding in Java because the C# is basically identical to mine.
Java Digital Signature different to C#

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