SHA-512 with salt not matching between Java and PHP - java

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.

Related

CryptoJS AES encryption and Java AES decryption

I'm only asking this because I have read many posts for 2 days now about crypto AES encryption, and just when I thought I was getting it, I realized I wasn't getting it at all.
This post is the closest one to my issue, I have exactly the same problem but it is unanswered:
CryptoJS AES encryption and JAVA AES decryption value mismatch
I have tried doing it in many ways but I haven't gotten it right.
First Off
I'm getting the already encrypted string (I only got the code to see how they were doing it), so modifying the encryption way is not an option. That's why all the similar questions aren't that useful to me.
Second
I do have access to the secret key and I can modify it (so adjusting length is an option if neccessary).
The encryption is done on CryptoJS and they send the encrypted string as a GET parameter.
GetParamsForAppUrl.prototype.generateUrlParams = function() {
const self = this;
return new Promise((resolve, reject) => {
const currentDateInMilliseconds = new Date().getTime();
const secret = tokenSecret.secret;
var encrypted = CryptoJS.AES.encrypt(self.authorization, secret);
encrypted = encrypted.toString();
self.urlParams = {
token: encrypted,
time: currentDateInMilliseconds
};
resolve();
});
};
I can easily decrypt this on javascript using CryptoJS with:
var decrypted = CryptoJS.AES.decrypt(encrypted_string, secret);
console.log(decrypted.toString(CryptoJS.enc.Utf8));
But I don't want to do this on Javascript, for security reasons, so I'm trying to decrypt this on Java:
String secret = "secret";
byte[] cipherText = encrypted_string.getBytes("UTF8");
SecretKey secKey = new SecretKeySpec(secret.getBytes(), "AES");
Cipher aesCipher = Cipher.getInstance("AES");
aesCipher.init(Cipher.DECRYPT_MODE, secKey);
byte[] bytePlainText = aesCipher.doFinal(byteCipherText);
String myDecryptedText = = new String(bytePlainText);
Before I had any idea of what I was doing, I tried base64 decoding, adding some IV and a lot of stuff I read, of course none of it worked.
But after I started to understand, kinda, what I was doing, I wrote that simple script above, and got me the same error on the post: Invalid AES key length
I don't know where to go from here. After reading a lot about this, the solution seems to be hashing or padding, but I have no control on the encryption method, so I can't really hash the secret or pad it.
But as I said, I can change the secret key so it can match some specific length, and I have tried changing it, but as I'm shooting in the dark here, I don't really know if this is the solution.
So, my question basically is, If I got the encrypted string (in javascript like the first script) and the secret key, is there a way to decrypt it (in Java)? If so, how to do it?
Disclaimer: Do not use encryption unless you understand encryption concepts including chaining mode, key derivation functions, IV and block size. And don't roll your own security scheme but stick to an established one. Just throwing in encryption algorithms doesn't mean an application has become any more secure.
CryptoJS implements the same key derivation function as OpenSSL and the same format to put the IV into the encrypted data. So all Java code that deals with OpenSSL encoded data applies.
Given the following Javascript code:
var text = "The quick brown fox jumps over the lazy dog. 👻 👻";
var secret = "René Über";
var encrypted = CryptoJS.AES.encrypt(text, secret);
encrypted = encrypted.toString();
console.log("Cipher text: " + encrypted);
We get the cipher text:
U2FsdGVkX1+tsmZvCEFa/iGeSA0K7gvgs9KXeZKwbCDNCs2zPo+BXjvKYLrJutMK+hxTwl/hyaQLOaD7LLIRo2I5fyeRMPnroo6k8N9uwKk=
On the Java side, we have
String secret = "René Über";
String cipherText = "U2FsdGVkX1+tsmZvCEFa/iGeSA0K7gvgs9KXeZKwbCDNCs2zPo+BXjvKYLrJutMK+hxTwl/hyaQLOaD7LLIRo2I5fyeRMPnroo6k8N9uwKk=";
byte[] cipherData = Base64.getDecoder().decode(cipherText);
byte[] saltData = Arrays.copyOfRange(cipherData, 8, 16);
MessageDigest md5 = MessageDigest.getInstance("MD5");
final byte[][] keyAndIV = GenerateKeyAndIV(32, 16, 1, saltData, secret.getBytes(StandardCharsets.UTF_8), md5);
SecretKeySpec key = new SecretKeySpec(keyAndIV[0], "AES");
IvParameterSpec iv = new IvParameterSpec(keyAndIV[1]);
byte[] encrypted = Arrays.copyOfRange(cipherData, 16, cipherData.length);
Cipher aesCBC = Cipher.getInstance("AES/CBC/PKCS5Padding");
aesCBC.init(Cipher.DECRYPT_MODE, key, iv);
byte[] decryptedData = aesCBC.doFinal(encrypted);
String decryptedText = new String(decryptedData, StandardCharsets.UTF_8);
System.out.println(decryptedText);
The result is:
The quick brown fox jumps over the lazy dog. 👻 👻
That's the text we started with. And emojis, accents and umlauts work as well.
GenerateKeyAndIV is a helper function that reimplements OpenSSL's key derivation function EVP_BytesToKey (see https://github.com/openssl/openssl/blob/master/crypto/evp/evp_key.c).
/**
* Generates a key and an initialization vector (IV) with the given salt and password.
* <p>
* This method is equivalent to OpenSSL's EVP_BytesToKey function
* (see https://github.com/openssl/openssl/blob/master/crypto/evp/evp_key.c).
* By default, OpenSSL uses a single iteration, MD5 as the algorithm and UTF-8 encoded password data.
* </p>
* #param keyLength the length of the generated key (in bytes)
* #param ivLength the length of the generated IV (in bytes)
* #param iterations the number of digestion rounds
* #param salt the salt data (8 bytes of data or <code>null</code>)
* #param password the password data (optional)
* #param md the message digest algorithm to use
* #return an two-element array with the generated key and IV
*/
public static byte[][] GenerateKeyAndIV(int keyLength, int ivLength, int iterations, byte[] salt, byte[] password, MessageDigest md) {
int digestLength = md.getDigestLength();
int requiredLength = (keyLength + ivLength + digestLength - 1) / digestLength * digestLength;
byte[] generatedData = new byte[requiredLength];
int generatedLength = 0;
try {
md.reset();
// Repeat process until sufficient data has been generated
while (generatedLength < keyLength + ivLength) {
// Digest data (last digest if available, password data, salt if available)
if (generatedLength > 0)
md.update(generatedData, generatedLength - digestLength, digestLength);
md.update(password);
if (salt != null)
md.update(salt, 0, 8);
md.digest(generatedData, generatedLength, digestLength);
// additional rounds
for (int i = 1; i < iterations; i++) {
md.update(generatedData, generatedLength, digestLength);
md.digest(generatedData, generatedLength, digestLength);
}
generatedLength += digestLength;
}
// Copy key and IV into separate byte arrays
byte[][] result = new byte[2][];
result[0] = Arrays.copyOfRange(generatedData, 0, keyLength);
if (ivLength > 0)
result[1] = Arrays.copyOfRange(generatedData, keyLength, keyLength + ivLength);
return result;
} catch (DigestException e) {
throw new RuntimeException(e);
} finally {
// Clean out temporary data
Arrays.fill(generatedData, (byte)0);
}
}
Note that you have to install the Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy. Otherwise, AES with key size of 256 won't work and throw an exception:
java.security.InvalidKeyException: Illegal key size
Update
I have replaced Ola Bini's Java code of EVP_BytesToKey, which I used in the first version of my answer, with a more idiomatic and easier to understand Java code (see above).
Also see How to decrypt file in Java encrypted with openssl command using AES?.
When encrypting on one system and decrypting on another you are at the mercy of system defaults. If any system defaults do not match (and they often don't) then your decryption will fail.
Everything has to be byte for byte the same on both sides. Effectively that means specifying everything on both sides rather than relying on defaults. You can only use defaults if you are using the same system at both ends. Even then, it is better to specify exactly.
Key, IV, encryption mode, padding and string to bytes conversion all need to be the same at both ends. It is especially worth checking that the key bytes are the same. If you are using a Key Derivation Function (KDF) to generate your key, then all the parameters for that need to be the same, and hence specified exactly.
Your "Invalid AES key length" may well indicate a problem with generating your key. You use getBytes(). That is probably an error. You need to specify what sort of bytes you are getting: ANSI, UTF-8, EBCDIC, whatever. The default assumption for the string to byte conversion is the likely cause of this problem. Specify the conversion to be used explicitly at both ends. That way you can be sure that they match.
Crypto is designed to fail if the parameters do not match exactly for encryption and decryption. For example, even a one bit difference in the key will cause it to fail.

JAVA DES Encryption Output Encoding

I have DES Encryption Algorithm implementation in JAVA (javax.crypto.Cipher), it is successfully encoding and decoding (most) strings... the problem is that, sometimes, it message specific blocks (since DES uses 8-character blocks in block mode).
In my case, almost always the 3rd block is messed up and rest shows fine.
for example:
key: thisiskey
message to encrypt: Google is an American multinational technology company specializing in Internet-related services
encrypted message (in UTF-8):
mñqè•ÀPŒ�øf"
ߦ\±õ¤ù'È9¢ëyT ÍQEÁ|;ëâÉ÷JWú
Now, when i go and decrypt this, i get this:
Decrypted message:
Google i,í\O¯‹Ýbº-¸�¬ltinational technology company specializHôJ—=ÊÍnternet-related services
As far as i understand the issue, it is due to the fact that UTF-8 CANNOT show all characters and thus, while showing as well as copying for decryption, this problem occurs.
Can anyone suggest me a solution?
Preferably, either a character-set that can handle this, or, a way to convert Binary directly to HEX (that can be output to user) and then Vice Versa (decrypted, after copying/pasting) in JAVA.
EDIT
This is 'approximate' code, not exact (for example encrypted message is not properly paste-able and these are parts of the function, but it should give the idea). Even in base64 encoding , i am unable to get this decrypted properly.
Encrypt Function code:
boolean base64 = true;
key = "thisiskey";
plainText = "Google is an American multinational technology company specializing in Internet-related services";
SecretKeyFactory MyKeyFactory = SecretKeyFactory.getInstance("DES");
byte[] keyBytes = key.getBytes();
DESKeySpec generatedKeySpec = new DESKeySpec(keyBytes);
SecretKey generatedSecretKey = MyKeyFactory.generateSecret(generatedKeySpec);
Cipher generatedCipher = Cipher.getInstance("DES");
generatedCipher.init(Cipher.ENCRYPT_MODE, generatedSecretKey);
byte[] messsageStringBytes = plainText.getBytes();
byte[] encryptedMessage = generatedCipher.doFinal(messsageStringBytes);
String encryptedMessageString = new String(encryptedMessage);
if (base64) {
encryptedMessageString = Base64.getEncoder().encodeToString(encryptedMessageString.getBytes("utf-8"));
}
return encryptedMessageString;
Decrypt Function code:
boolean dbase64 = true;
dkey = "thisiskey";
messageToDecrypt = "mñqè•ÀPŒ�øf\"ߦ\±õ¤ù'È9¢ëyT ÍQEÁ|;ëâÉ÷JWú"; // Message from above code
SecretKeyFactory MyKeyFactory = SecretKeyFactory.getInstance("DES");
byte[] dkeyBytes = dkey.getBytes();
DESKeySpec generatedKeySpec = new DESKeySpec(dkeyBytes);
SecretKey generatedSecretKey = MyKeyFactory.generateSecret(generatedKeySpec);
Cipher generatedCipher = Cipher.getInstance("DES");
generatedCipher.init(Cipher.DECRYPT_MODE, generatedSecretKey);
if (dbase64) {
byte[] decodedBytes = Base64.getDecoder().decode(dencryptedText);
dencryptedText = new String(decodedBytes, "utf-8");
}
byte[] messsageStringBytes = dencryptedText.getBytes();
byte[] encryptedMessage = generatedCipher.doFinal(messsageStringBytes);
String decryptedMessageString = new String(encryptedMessage);
return decryptedMessageString;
"Encrypted message in UTF-8" makes no sense. The ciphertext is binary and not UTF-8. You need to put it into a byte[], not a String.
If you need a String, use Base64 or Hex encoding.
Even in base64 encoding , i am unable to get this decrypted properly.
String encryptedMessageString = new String(encryptedMessage);
if (base64) {
encryptedMessageString = Base64.getEncoder().encodeToString(encryptedMessageString.getBytes("utf-8"));
}
That does not work. You are encoding to Base64 after the data is already broken (by calling new String). Do not put it in a String at all. Go directly from encryptedMessage (the byte[]) to Base64.

Garbled output in AES/CBC/NoPadding Decryption

I'm trying to decrypt text in java that is encrypted using CryptoJS. I've read on other posts that they use different default modes and padding so I set them both(java/cryptojs) both to use aes/cbc/nopadding. I no longer get an exception in java, but I am getting a garbled output during decryption
Encryption(JS):
var parsedLogin = JSON.parse(login);
var publicKey = "abcdefghijklmnio";
var publiciv = "abcdefghijklmnio";
var key = CryptoJS.enc.Hex.parse(publicKey);
var iv = CryptoJS.enc.Hex.parse(publiciv);
var encrypted = CryptoJS.AES.encrypt(parsedLogin.password, publicKey, {iv: publiciv}, { padding: CryptoJS.pad.NoPadding, mode: CryptoJS.mode.CBC});
// send encrypted to POST request
DECRYPT (Java)
String PUBLIC_KEY = "abcdefghijklmnio";
String PUBLIC_IV = "abcdefghijklmnio";
byte[] byteArr = PUBLIC_KEY.getBytes();
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
final SecretKeySpec secretKey = new SecretKeySpec(byteArr, "AES");
cipher.init(Cipher.DECRYPT_MODE, secretKey, new IvParameterSpec(PUBLIC_IV.getBytes()));
byte[] parsed = Base64.decodeBase64(encrypted.getBytes());
//byte[] parsed = DatatypeConverter.parseBase64Binary(encrypted);
byte[] fin = cipher.doFinal(parsed);
String decryptedString = new String(fin);
The result that I'm getting is like this: Š²Û!aå{’`#"Ûîñ?Œr˜krÆ
I have already tried changing the CHARSET in the getBytes() to US-ASCII, UTF-8 and UTF-16 but all this does is change the garbled text
I have also tried using othe blocking modes and paddings but they failed at the js level. I just need a simple encryption method right now.
NOTE:
Ignore the security issues...like having the key exposed in js, etc. I'll be handling those later..
You shouldn't be able to use AES CBC without padding unless the password is always 16 bytes. It probably applies some sort of default padding that may or may not be a good idea.
Anyway: you need to pass your key and iv to CryptoJS as a WordArray; if you give it a string it will assume you're giving it a passphrase and derive a different key from that. As such, your Java decryption code will be using a different key/iv pair. You can create a WordArray from your strings using
var key = CryptoJS.enc.Utf8.parse("abcdefghijklmnio")
var iv = ...

How to decrypt Triple Des CryptoJS values in Java class

I was asked to encrypt some text from client side ( web ) before sending it to server side ( java )
So i try to use CryptoJS library for client side.
I encrypt it like this :
var key = "aaaaaaaaaaaaaaaaaaaaaaaa";
var value = "KF169841";
var encryptedString = CryptoJS.TripleDES.encrypt(value, key);
console.log(encryptedString.toString());
And i get something like this : U2FsdGVkX19eYFFHgYGCr3v9/skTOKVp0pLWRNK9JTg=
I use this encryptedString and key in other Decrypt tool online ( Which also use CryptoJS ) and got back exact value KF169841.
After sending this value and key to server ( well key isn't sending directly to server though but for test, it is ), i need to decrypt it using Java.
But i quite don't know how to decrypt it. I'm tried some code from google search but it end up wrong padding if use DESese or get wrong value if i use ECB/NoPadding.
I did try to something like setting sfg for CryptoJS side like:
mode: CryptoJS.mode.EBC,
padding: CryptoJS.pad.NoPadding
But they got javascript exception ( a is not define )
So any have any experience with CryptoJS can help me decrypt this one using java ?
=============================================================
UPDATE : Sorry here my server side code i'm using
/**
* Method To Decrypt An Ecrypted String
*/
public String decrypt(String encryptedString, String myEncryptionKey) {
String decryptedText = null;
try {
byte[] keyAsBytes = myEncryptionKey.getBytes("UTF8");
KeySpec myKeySpec = new DESedeKeySpec(keyAsBytes);
SecretKeyFactory mySecretKeyFactory =
SecretKeyFactory.getInstance("DESede");
Cipher cipher = Cipher.getInstance("DESede/ECB/NoPadding");
SecretKey key = mySecretKeyFactory.generateSecret(myKeySpec);
cipher.init(Cipher.DECRYPT_MODE, key);
// BASE64Decoder base64decoder = new BASE64Decoder();
// byte[] encryptedText = base64decoder.decodeBuffer(encryptedString);
byte[] encryptedText = org.apache.commons.codec.binary.Base64.decodeBase64(encryptedString);
byte[] plainText = cipher.doFinal(encryptedText);
decryptedText= bytes2String(plainText);
} catch (Exception e) {
e.printStackTrace();
}
return decryptedText;
}
According to the documentation, your encryptedString variable contains structured data that must be split apart to be sent to Java code. You will need to send encryptedString.iv and encryptedString.ciphertext to your Java code. If you continue to use passwords (see below), you will need to send encryptedString.salt as well.
If you pass your key as a string it will be interpreted as a password and a key will be derived from it. If you actually want to pass an explicit key, follow the documentation and specify the IV and key as suggested by the code snippet below. If you stick with supplying a password, then you must figure out the derivation scheme and use the same process in your Java code.
// Code snippet from http://code.google.com/p/crypto-js/#Custom_Key_and_IV
<script src="http://crypto-js.googlecode.com/svn/tags/3.1.2/build/rollups/aes.js"></script>
<script>
var key = CryptoJS.enc.Hex.parse('000102030405060708090a0b0c0d0e0f');
var iv = CryptoJS.enc.Hex.parse('101112131415161718191a1b1c1d1e1f');
var encrypted = CryptoJS.AES.encrypt("Message", key, { iv: iv });
</script>
Regarding your Java code, it looks mostly OK (although there is plenty of room for error with string conversions). However, you probably want to convert your key from hex to binary rather than grabbing the bytes:
byte[] keyAsBytes = DatatypeConverter.parseHexBinary(myEncryptionKey);
This assumes you alter your JavaScript code to pass the literal key value.
You will also need to switch to DESede/CBC/PKCS5Padding and pass an IVParameterSpec object to your Cipher.init call, specifying the IV value sent from your Java Script code.

Java equivalent of Fantom HMAC using SHA1

I'm having trouble doing the following in Java. Below is the Fantom code from the documentation for the the tool I am using.
// compute salted hmac
hmac := Buf().print("$username:$userSalt").hmac("SHA-1", password.toBuf).toBase64
// now compute login digest using nonce
digest := "${hmac}:${nonce}".toBuf.toDigest("SHA-1").toBase64
// our example variables
username: "jack"
password: "pass"
userSalt: "6s6Q5Rn0xZP0LPf89bNdv+65EmMUrTsey2fIhim/wKU="
nonce: "3da210bdb1163d0d41d3c516314cbd6e"
hmac: "IjJOApgvDoVDk9J6NiyWdktItl0="
digest: "t/nzXF3n0zzH4JhXtihT8FC1N3s="
I've been searching various examples through Google but none of them produce the results the documentation claims should be returned.
Can someone with Fantom knowledge verify if the example in the documentation is correct?
As for the Java side, here is my most recent attempt
public static String hmacSha1(String value, String key) {
try {
// Get an hmac_sha1 key from the raw key bytes
byte[] keyBytes = key.getBytes("UTF-8");
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("UTF-8"));
// Convert raw bytes to Hex
byte[] hexBytes = new Hex().encode(rawHmac);
// Covert array of Hex bytes to a String
return new String(hexBytes, "UTF-8");
} catch (Exception e) {
throw new RuntimeException(e);
}
}
However, when I call the method with the following parameters
jack:6s6Q5Rn0xZP0LPf89bNdv+65EmMUrTsey2fIhim/wKU=
pass
I get
22324e02982f0e854393d27a362c96764b48b65d
Not sure where the docs came from - but they could be out-of-date - or wrong. I would actually run the Fantom code to use as your reference to make sure you're testing the right stuff ;)
You can take a look at the Java source for sys::Buf.hmac: MemBuf.java
I would also recommend separating out the 3 transformations. Make sure your raw byte array matches in both Fantom and Java, then verify the digest matches, and finally the Base64 encoding. Be alot easier to verify each stage in your code.
Turns out it was just my own lack of knowledge and with enough trial and error I was able to figure it out by doing the following:
//username: "jack"
//password: "pass"
//userSalt: "6s6Q5Rn0xZP0LPf89bNdv+65EmMUrTsey2fIhim/wKU="
//nonce: "3da210bdb1163d0d41d3c516314cbd6e"
//hmac: "IjJOApgvDoVDk9J6NiyWdktItl0="
//digest: "t/nzXF3n0zzH4JhXtihT8FC1N3s="
...
// initialize a Mac instance using a signing key from the password
SecretKeySpec signingKey = new SecretKeySpec(password.getBytes(), "HmacSHA1");
Mac mac = Mac.getInstance("HmacSHA1");
mac.init(signingKey);
// compute salted hmac
byte[] hmacByteArray = mac.doFinal((username + ':' + userSalt).getBytes());
String hmacString = new String(Base64.encodeBase64(hmacByteArray));
// hmacString == hmac
// now compute login digest using nonce
MessageDigest md = MessageDigest.getInstance("SHA-1");
md.update((hmacString + ':' + nonce).getBytes());
byte[] digestByteArray = md.digest();
String digestString = new String(Base64.encodeBase64(digestByteArray));
// digestString == digest
Used org.apache.commons.codec.binary.Base64 to encode the byte arrays.

Categories

Resources