I'm trying to integrate a PHP backend with a Java backend. The Java backend expects some credentials which must be correctly encoded and hashed to match at both ends.
The java system converts a string to a sequence of bytes (1), then generates a HMAC/SHA256 keyed hash of that sequence (2) and then base 64 encodes the hash (3).
private static final String HMAC_ALGORITHM = "HmacSHA256";
final SecretKey key = new SecretKeySpec('mysecret', HMAC_ALGORITHM);
final Mac hmac = Mac.getInstance(key.getAlgorithm());
hmac.init(key);
final String stringToHash = "blablabla";
final byte[] bytesToHash = stringToHash.getBytes("UTF8");
final byte[] hash = hmac.doFinal(bytesToHash);
return Base64.encodeBytes(hash);
I can do (1) and convert the string in PHP to a sequence of bytes using
$stringArray = unpack('C*', $string);
// which works and is exactly the same as the Java system at this point
however the second part (2) doesn't seem to match, is there a way to pass a byte array to hmac in PHP because in Java the HMAC method accepts a byte array whereas the PHP one expects a string e.g.
hash_hmac('sha256', $stringArray, $secret);
or do I need to loop through the array and do some magic?
Thanks!
I dont think you need to unpack in php
Just try
hash_hmac('sha256', $string, $secret, TRUE);
Note: I am passing the actual string to the function and the last param raw_output is set to TRUE
Update
Your java code seems to be wrong docs
final SecretKey key = new SecretKeySpec('mysecret', HMAC_ALGORITHM);
This should be like the following
byte[] keyBytes = mysecret.getBytes(); // say mysecret is a String var
final SecretKey key = new SecretKeySpec(keyBytes, HMAC_ALGORITHM);
I have to generate the same Java values on PHP. The only way that it works was this
<?php
$verb = hash_hmac("sha256", "POST" , "9mO6oBVSmb2QTSeN73mEGHHnD", FALSE);
$path = hash_hmac("sha256", '/1.0/benefits/registerBenefit', pack("H*", $verb), FALSE);
$query = hash_hmac("sha256", "" , pack("H*", $path), FALSE);
$body = hash_hmac("sha256", '{"id":"a7d23226-bdfc-4d85-a30a-b2d7eccc36e6","phone_number":"5511973512530","sku":"com.movile.cubes.br.biweekly.homolog","origin":"trade_up_group","application_id":437}' , pack("H*", $query), FALSE);
$rawSignature = hash_hmac("sha256", "x-kiwi-signature", pack("H*", $body), FALSE);
$signature = base64_encode( pack("H*", $rawSignature));
var_dump($signature);
So..I had to use pack("H*", $str) to get the same values that Java was generating.
Related
hi i'm trying to decode two strings then use the return result (byte[]) then put it in a Biginteger constructor Like this :
BigInteger bigInteger1 = new BigInteger(1, Base64.decode(myString1,0));
BigInteger bigInteger2 = new BigInteger(1, Base64.decode(myString2,0));
then put this BigIntegers on a java.security.KeyFactory class to create a RSAPublicKey like This :
KeyFactory.getInstance(ALG).generatePublic(new RSAPublicKeySpec(bigInteger1,bigInteger2));
then use my public key to encode a string Like this :
public static String encrypt(PublicKey publicKey, String str) {
Cipher instance = Cipher.getInstance(ALG);
instance.init(1, publicKey);
Base64.encodeToString(instance.doFinal(str.getBytes()), 2);
}
on PHP. I achieve this goal with android but when I want to do it with PHP I have a lot of problems even on the start when I want to decode my string in PHP with this code :
$encoded = base64_decode($base,true);
$decoded = utf8_decode(base64_decode($encoded));
I will get this string:??2?]????5N?[??S
but in android, the decoded string is totally different and always stay the same result
I tried to do this job on JSP but it's really hard to learn a new language and I don't have the time.
Can I do this project in spring boot? I have the codes for java
please, somebody, help me.
You're decoding string twice. You should try :
$encoded = base64_encode($base); // and not base64_decode
$decoded = base64_decode($encoded); // will be $base
utf8_decode is converting a string with ISO-8859-1 characters encoded with UTF-8 to single-byte ISO-8859-1. Are you sure you're needing it ?
In PHP, you can use intval to performs BigInteger() but I'm not sure you won't be facing an integer overflow.
Finally, OpenSSL library will certainly do the job for key generation and encryption.
My application uses salted hash in Java. First a random salt is generated. Then this salt is prefixed to the SHA-512 of the input password and the combined string is SHA-512 again.It is implemented as follows:-
String password = testpwd.getText().toString();
SecureRandom rand = new SecureRandom();
byte[] randbytes = new byte[16];
rand.nextBytes(randbytes);
String encodedSalt = Base64.encodeToString(randbytes, Base64.DEFAULT);
MessageDigest digest = MessageDigest.getInstance("SHA-512");
digest.reset();
digest.update(password.getBytes("utf-8"));
byte[] pwdhash = digest.digest();
String encodedPwd = Base64.encodeToString(pwdhash, Base64.DEFAULT);
String saltedPassword = encodedSalt+encodedPwd ;
digest.reset();
digest.update(saltedPassword.getBytes("utf-8"));
byte[] pwdhash1 = digest.digest();
String encodedPwdSalt = Base64.encodeToString(pwdhash1, Base64.DEFAULT);
Then the strings encodedSalt and encodedPwdSalt are sent to web server for authentication. The PHP encryption is as follows:
$postpassword = $_POST['password'];
$postsalt = $_POST['salt'];
$salt = base64_decode($postsalt);
$password = base64_decode('postpassword');
The SHA-512 hashs of the password "Ditglt#785" is stored in the database. It is retrieved and processed as follows:-
$getsaltpwd = $salt.$dbpassword ;
$dbsaltpwd = hash('sha512', $getsaltpwd);
if($dbpassword == $postpassword) {}
The condition always fails and so does the authentication. What should I do ?
The PHP version hashes raw bytes while the Java version hashes base64-encoded strings.
Here's a Java version that matches what your PHP code does:
digest.reset();
digest.update(randbytes);
digest.update(pwdhash);
byte[] pwdhash1 = digest.digest();
String encodedPwdSalt = Base64.encodeToString(pwdhash1, Base64.DEFAULT);
Having said that, it would be more secure to store the salt and the salted password in the database, and to use at least some key derivation function (iterate the hash function many times) in order to counteract any potential brute-forcing of the stored hashes.
Since your Java code correctly follows what you describe in your specification, the problem lies on the PHP side.
With your Java code as-is, it generates the following values when encoding the string "password" with a random salt:
encodedSalt: ww0g+f77ygKD7Iww1GTYtg==
encodedPwd: sQnzu7wkTrgkQZF+0G1hi5AI3Qmzvv0bXgc5THBqi7mAsdd4Xll27ASbRt9fEyavWi6m0QP9B8lThf+rDKy8hg==
encodedPwdSalt: YAGG7GcpUxIZzBnHuaezPf5BWFhFalBPgvue/0wFoRLu+JsKslG8wPCv6dPubIBk1aFIJ8spK8S17347aDBAYA==
In PHP, what you would need to do is the following:
$postpassword = 'YAGG7GcpUxIZzBnHuaezPf5BWFhFalBPgvue/0wFoRLu+JsKslG8wPCv6dPubIBk1aFIJ8spK8S17347aDBAYA==';
$postsalt = 'ww0g+f77ygKD7Iww1GTYtg==';
$dbpassword = 'sQnzu7wkTrgkQZF+0G1hi5AI3Qmzvv0bXgc5THBqi7mAsdd4Xll27ASbRt9fEyavWi6m0QP9B8lThf+rDKy8hg==';
if($postpassword == base64_encode(hash('sha512', $postsalt.$dbpassword, true))) {
echo 'OK';
}
Check if the padding matches. I experienced the same problem with encryption where the padding in PHP was different from the padding in JAVA. Luckily I was able to set the padding in JAVA to the one that PHP uses. But I had to look at PHP source code to figure out how. As far as I remember it was not possible to change the padding in PHP back then.
Here is the question I posted back then: decrypting php encrypted data on android
[...] you need to set Base64.decode with the parameter Base64.NO_WRAPas PHP will just put out the base64 delimited by \0.
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 trying to take some working python code and convert it to java for my usage. The python code below produces the correct signature. The java code using the same key, salt, produces something different and I am at a loss for why. In the Java code I am using the key generated in python (_key) to create the signature.
What I don't understand is, if I print the value of _key in python I get "34ee7983-5ee6-4147-aa86-443ea062abf774493d6a-2a15-43fe-aace-e78566927585". Now if I take that and place it directly into the hmac(new) call I get a different result than if I just leave the _key variable. I assume this has something to do with encoding of some type but I am at a loss.
_s1 = base64.b64decode('VzeC4H4h+T2f0VI180nVX8x+Mb5HiTtGnKgH52Otj8ZCGDz9jRW'
'yHb6QXK0JskSiOgzQfwTY5xgLLSdUSreaLVMsVVWfxfa8Rw==')
_s2 = base64.b64decode('ZAPnhUkYwQ6y5DdQxWThbvhJHN8msQ1rqJw0ggKdufQjelrKuiG'
'GJI30aswkgCWTDyHkTGK9ynlqTkJ5L4CiGGUabGeo8M6JTQ==')
# bitwise and of _s1 and _s2 ascii, converted to string
_key = ''.join([chr(ord(c1) ^ ord(c2)) for (c1, c2) in zip(_s1, _s2)])
#classmethod
def get_signature(cls, song_id, salt=None):
"""Return a (sig, salt) pair for url signing."""
if salt is None:
salt = str(int(time.time() * 1000))
mac = hmac.new(cls._key, song_id, sha1)
mac.update(salt)
sig = base64.urlsafe_b64encode(mac.digest())[:-1]
return sig, salt
This is my Java code. I think ultimately my issue is how I am handling or encoding the AA_KEY but I cannot figure it out.
private static final String AA_KEY = "34ee7983-5ee6-4147-aa86-443ea062abf774493d6a-2a15-43fe-aace-e78566927585";
public void someFunc(String songId) {
salt = "1431875768596"
String sig = hmacSha1(songId + salt, AA_KEY);
sig = StringUtils.replaceChars(sig, "+/=", "-_.");
}
static String hmacSha1(String value, String key) {
try {
// Get an hmac_sha1 key from the raw key bytes
byte[] keyBytes = key.getBytes();
SecretKeySpec signingKey = new SecretKeySpec(keyBytes, "HmacSHA1");
// Get an hmac_sha1 Mac instance and initialize with the signing key
Mac mac = Mac.getInstance("HmacSHA1");
mac.init(signingKey);
// Compute the hmac on input data bytes
byte[] rawHmac = mac.doFinal(value.getBytes());
return Base64.encodeBytes(rawHmac);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
I found a couple of similar questions but they didn't help me figure it out sadly. Thanks!
Python HMAC-SHA256 signature differs from PHP signature
Python HMAC-SHA1 vs Java HMAC-SHA1 different results
I'd like to sign a file by using a RSA keypair. For this purpose I have this Perl script:
#!/usr/bin/perl
use Crypt::RSA;
my $data = ... # File contents
my $rsa = new Crypt::RSA;
my $key = new Crypt::RSA::Key::Private(Filename => "stackoverflow.priv", Password => "*****");
my $signature = $rsa->sign(Message => $data, Key => $key, Armour => 0);
# Write signature to file
On the client side, I'd like to use the following Java function to verify the file:
private static final String PUBLICKEY_MOD = "190343051422614110006523776876348493...";
private static String PUBLICKEY_EXP = "65537";
public boolean check() {
byte[] data = ... // Data
byte[] dataSignature = ... // Signature (as calculated in the Perl script)
Signature signature = Signature.getInstance("SHA256withRSA");
signature.initVerify(getPublicKey());
signature.update(data);
return signature.verify(dataSignature);
}
private PublicKey getPublicKey() {
RSAPublicKeySpec spec = new RSAPublicKeySpec(new BigInteger(PUBLICKEY_MOD), new BigInteger(PUBLICKEY_EXP));
KeyFactory factory = KeyFactory.getInstance("RSA");
return factory.generatePublic(spec);
}
However, check() always reports false. These things I already checked:
data and dataSignature are correctly read
PUBLICKEY_MOD and PUBLICKEY_EXP are correct
getPublicKey() returns a PublicKey which has the correct attributes
the private key and the public key are part of the same pair
Does anyone know how to verify the file correctly? Is signature correctly instanced?
Your first clue that something might be wrong is that you never tell Perl what hash function to use, but you tell Java to use SHA256. You have a lot of work to do on the Perl side. Also, the default padding scheme for Crypt::RSA seems to be PSS, whereas for Java it is PKCSv1.5