I'm trying to replicate the same signature outcome as the Python code in Java.
Python Code Example:
secret = "1234abcd".encode()
encoded_payload = json.dumps(payload).encode()
b64 = base64.b64encode(encoded_payload)
signature = hmac.new(secret, b64, hashlib.sha384).hexdigest()
Current Java Code (I'm using Apache's Commons Codec for Hex encoding):
String secret = "1234abcd";
String b64 = Base64.getEncoder().encodeToString(payload.toString().getBytes());
Mac hasher = Mac.getInstance("HmacSHA384");
hasher.init(new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), "HmacSHA384"));
byte[] hash = hasher.doFinal(b64.getBytes(StandardCharsets.UTF_8));
String signature = Hex.encodeHexString(hash);
How should I go about doing this in Java?
Related
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.
I try to implement this Java method in Python, but it seems to hard to rewrite it in pure Python.
public static String CalculateHash(String input, String token) {
SecretKeySpec signingKey = new SecretKeySpec(token.getBytes(), "HmacSHA1");
Mac mac = null;
mac = Mac.getInstance("HmacSHA1");
mac.init(signingKey);
assert mac != null;
byte[] bytes = mac.doFinal(input.getBytes(Charset.forName("UTF-8")));
String form = "";
for (byte aByte : bytes) {
String str = Integer.toHexString(((int) aByte) & 0xff);
if (str.length() == 1) {
str = "0" + str;
}
form = form + str;
}
return form;
}
I tried this one, but it generates other hash.
def sign_request():
from hashlib import sha1
import hmac
# key = CONSUMER_SECRET& #If you dont have a token yet
key = "CONSUMER_SECRET&TOKEN_SECRET"
# The Base String as specified here:
raw = "BASE_STRING" # as specified by oauth
hashed = hmac.new(key, raw, sha1)
# The signature
return hashed.digest().encode("base64").rstrip('\n')
What and how should I use in standart Python library to rewrite it? Thank you
Your python code and java code don't match in the fact that the python code uses base 64, while the java code uses hexadecimal (base 16).
You should change the phyton code to use base16 for its output, this can be done with the hex() function, caring to correctly pad the number with the correct numbers of 0 characters the java code does.
I'm trying to implement digital signature in php as in java sample code below:
Signature rsaSig = Signature.getInstance("MD5withRSA");
RSAPrivateKey clientPrivateKey = readPrivateKeyFromFile(fileName);
rsaSig.initSign(clientPrivateKey);
String source = msg;
byte temp[] = source.getBytes();
rsaSig.update(temp);
byte sig[] = rsaSig.sign();
BASE64Encoder encoder = new BASE64Encoder();
return encoder.encode(sig);
My php code :
$rsa = new Crypt_RSA();
$rsa->loadKey('...'); // in xml format
$plaintext = '...';
$rsa->setSignatureMode(CRYPT_RSA_SIGNATURE_PKCS1);
$signature = $rsa->sign($plaintext);
But looks like some thing is missing. We should get same signature as java code returns.Can anybody guide me in this?
By default phpseclib uses sha1 as the hash. You probably need to do $rsa->setHash('md5').
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
I want tp encrypt and decrypt string, with defined salt. But the result must be same if the code run in java and adobe flex.
The main goal is: the app in adobe flex will be generate a string that can be decrypt in server using java.
I use this flex library
http://crypto.hurlant.com/demo/
Try to 'Secret Key' Tab. I want to use AES Encryption, 'CBC' or 'PKCS5'.
var k:String = "1234567890123456";
var kdata:ByteArray = Hex.toArray(k);
var txt:String = "hello";
var data:ByteArray = Hex.toArray(Hex.fromString(txt));;
var name:String = "simple-aes-cbc";
var pad:IPad =new PKCS5();
var mode:ICipher = Crypto.getCipher(name, kdata, pad);
pad.setBlockSize(mode.getBlockSize());
mode.encrypt(data);
encrypted.text=Hex.fromArray(data);
trace(Hex.fromArray(data));
And here is the code in java
String plaintext = "hello";
String key = "1234567890123456";
SecretKey keyspec = new SecretKeySpec(key.getBytes(), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE,keyspec);
byte[] encrypted = cipher.doFinal(plaintext.getBytes());
BASE64Encoder base64 = new BASE64Encoder();
String encodedString = base64.encode(encrypted);
System.out.println(encodedString);
Why the result is not same?
Can you guys provide the sample with the same result both of java and flex (encrypt and decrypt)? And if I want to change the paramater, for example, from cbc to ebc, which line that need to be changed?
Thanks!
"Simple" encryption mode (simple-aes-cbc) uses random initialization vector which is different each time you use it even if your secret key is the same.
If you wish to guarantee the same results when using the same key you should use "aes-cbc". Additionally you have to manually set the IV on the Cipher:
var ivmode:IVMode = mode as IVMode;
ivmode.IV = "some string guaranteed to be constant"
The IV can be made dependent on something like userId, which makes encryption repeatable for the same user.
You should consider how this affects your security scheme.
Seems like I do not convert into hex first in java when pass the key. And so on when get result byteArray at adobe flex, I do not cast again in java.
That's what I got when I see Arcadio code. Thanks.