I am trying to convert Java's DESede decryption to PHP's version. However with the same input, PHP cannot provide the identical output.
Java:
public class ThreeDES {
private KeySpec keySpec;
private SecretKeyFactory keyFactory;
private Cipher cipher;
public ThreeDES( String encryptionScheme, String encryptionKey )
throws EncryptionException {
try {
byte[] keyAsBytes = encryptionKey.getBytes("UTF-8");
keySpec = new DESedeKeySpec(keyAsBytes);
keyFactory = SecretKeyFactory.getInstance(encryptionScheme);
cipher = Cipher.getInstance(encryptionScheme);
} catch (InvalidKeyException e)
{
throw new EncryptionException( e );
}
catch (UnsupportedEncodingException e)
{
throw new EncryptionException( e );
}
catch (NoSuchAlgorithmException e)
{
throw new EncryptionException( e );
}
catch (NoSuchPaddingException e)
{
throw new EncryptionException( e );
}
}
public String decrypt( String encryptedString ) throws EncryptionException {
try {
SecretKey key = keyFactory.generateSecret( keySpec );
cipher.init( Cipher.DECRYPT_MODE, key );
BASE64Decoder base64decoder = new BASE64Decoder();
byte[] cleartext = base64decoder.decodeBuffer(encryptedString);
byte[] ciphertext = cipher.doFinal(cleartext);
return bytes2String( ciphertext );
}
catch (Exception e)
{
throw new EncryptionException( e );
}
}
private static String bytes2String( byte[] bytes )
{
StringBuffer stringBuffer = new StringBuffer();
for (int i = 0; i < bytes.length; i++)
{
stringBuffer.append( (char) bytes[i] );
}
return stringBuffer.toString();
}
}
PHP:
function decrypt($key, $data) {
$mcrypt_module = mcrypt_module_open(MCRYPT_TRIPLEDES, '', MCRYPT_MODE_ECB, '');
$mcrypt_iv = mcrypt_create_iv(mcrypt_enc_get_iv_size($mcrypt_module), MCRYPT_RAND);
$decrypted = mcrypt_decrypt(MCRYPT_TRIPLEDES, $key, base64_encode($data), MCRYPT_MODE_ECB, $mcrypt_iv);
mcrypt_module_close($mcrypt_module);
return pkcs5_unpad($decrypted);
}
function pkcs5_unpad($text) {
$pad = ord($text{strlen($text)-1});
if ($pad > strlen($text)) return false;
if (strspn($text, chr($pad), strlen($text) - $pad) != $pad) return false;
return substr($text, 0, -1 * $pad);
}
Given the following input parameters, PHP is unable to provide identical output:
$key = 'ASDFasdf12348983jklasdfJ2Jaf8';
$encrypted_data = 'cPap7+JIPS4=';
which should decrypt to:
coRef=3
Test codes for Java are as follow:
try {
String encryptedStr = encrypted_data; // same value as PHP's $encrypted_data
String decryptedString = "";
ThreeDES desedeEncrypter = new ThreeDES("DSEede", key); // same value as PHP's $key
decryptedString = desedeEncrypter.decrypt(encryptedStr);
System.out.println(decryptedString);
} catch (ThreeDES.EncryptionException e) {
e.printStackTrace();
}
which outputs: coRef=3. However, the following PHP code raises a warning about key length.
echo decrypt($key, $encrypted_data);
Key of size 29 not supported by this algorithm. Only keys of size 24 supported in...
How do I modify my code to use a key longer than 24 characters?
well, this is weird ,
Triple Des only accepts 24 bytes as it's key
Each DES key is nominally stored or transmitted as 8 bytes, each of
odd parity,[12] so a key bundle requires 24 bytes for option 1, 16 for
option 2, or 8 for option 3.
so i think that the problem is in here
DESedeKeySpec object :
/**
* Uses the first 24 bytes in <code>key</code> as the DES-EDE key.
* <p>
* The bytes that constitute the DES-EDE key are those between
* <code>key[0]</code> and <code>key[23]</code> inclusive
*
* #param key the buffer with the DES-EDE key material.
* #exception InvalidKeyException if the given key material is shorter
* than 24 bytes.
*/
so i think that DESedeKeySpec is kind of trimming your 29 length key to 24 to fit it with the tribledes requirements .
EDIT another important note that mcrypt_* extension has been deprecated .
This function has been DEPRECATED as of PHP 7.1.0. Relying on this
function is highly discouraged.
Related
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 3 years ago.
Improve this question
I have a legacy script that has already been saving encrypted data into a database.
The encrypt/decrypt was being done with Java the following code.
public class StringEncrypter {
Cipher ecipher;
Cipher dcipher;
/**
* Constructor used to create this object. Responsible for setting and
* initializing this object's encrypter and decrypter Chipher instances
* given a Pass Phrase and algorithm.
*
* #param passPhrase
* Pass Phrase used to initialize both the encrypter and
* decrypter instances.
*/
public StringEncrypter(String passPhrase) {
// 8-bytes Salt
byte[] salt = { (byte) 0xA9, (byte) 0x9B, (byte) 0xC8, (byte) 0x32,
(byte) 0x56, (byte) 0x34, (byte) 0xE3, (byte) 0x03 };
// Iteration count
int iterationCount = 19;
try {
KeySpec keySpec = new PBEKeySpec(passPhrase.toCharArray(), salt,
iterationCount);
SecretKey key = SecretKeyFactory.getInstance("PBEWithMD5AndDES")
.generateSecret(keySpec);
ecipher = Cipher.getInstance(key.getAlgorithm());
dcipher = Cipher.getInstance(key.getAlgorithm());
// Prepare the parameters to the cipthers
AlgorithmParameterSpec paramSpec = new PBEParameterSpec(salt,
iterationCount);
ecipher.init(Cipher.ENCRYPT_MODE, key, paramSpec);
dcipher.init(Cipher.DECRYPT_MODE, key, paramSpec);
} catch (InvalidAlgorithmParameterException e) {
System.out.println("EXCEPTION: InvalidAlgorithmParameterException");
} catch (InvalidKeySpecException e) {
System.out.println("EXCEPTION: InvalidKeySpecException");
} catch (NoSuchPaddingException e) {
System.out.println("EXCEPTION: NoSuchPaddingException");
} catch (NoSuchAlgorithmException e) {
System.out.println("EXCEPTION: NoSuchAlgorithmException");
} catch (InvalidKeyException e) {
System.out.println("EXCEPTION: InvalidKeyException");
}
}
/**
* Takes a single String as an argument and returns an Encrypted version of
* that String.
*
* #param str
* String to be encrypted
* #return <code>String</code> Encrypted version of the provided String
*/
public String encrypt(String str) {
try {
// Encode the string into bytes using utf-8
byte[] utf8 = str.getBytes("UTF8");
// Encrypt
byte[] enc = ecipher.doFinal(utf8);
// Encode bytes to base64 to get a string
return new sun.misc.BASE64Encoder().encode(enc);
} catch (BadPaddingException e) {
} catch (IllegalBlockSizeException e) {
} catch (UnsupportedEncodingException e) {
} catch (IOException e) {
}
return null;
}
/**
* Takes a encrypted String as an argument, decrypts and returns the
* decrypted String.
*
* #param str
* Encrypted String to be decrypted
* #return <code>String</code> Decrypted version of the provided String
*/
public String decrypt(String str) {
try {
// Decode base64 to get bytes
byte[] dec = new sun.misc.BASE64Decoder().decodeBuffer(str);
// Decrypt
byte[] utf8 = dcipher.doFinal(dec);
// Decode using utf-8
return new String(utf8, "UTF8");
} catch (BadPaddingException e) {
} catch (IllegalBlockSizeException e) {
} catch (UnsupportedEncodingException e) {
} catch (IOException e) {
}
return null;
}
}
This puts a string into the database like this RX0qxgKAKmjQmS9xjNtFnw==
I need to be able to decrypt this data using PHP.
I have tried using this script from github:
https://github.com/KevinBusse/PBEWithMD5AndDES
But can only get an output of bad magic number
Is this possible? If so any direction would be greatfully appreciated!
The Github-code can be used for decryption if the following settings are taken from the Java-code:
Salt (hex): A99BC8325634E303
Iterations: 19
Example:
Passphrase: MyPassphraseXYZ
Plaintext: The quick brown fox jumps over the lazy dog
Ciphertext from the Java code: xWnsqJJ4pqWTrm8kIwfyw1djD4lu0zig0wnohS+EtwDvHBgEP/BS25qyaE+QEdxd
The ciphertext can be decrypted with the PHP-code as follows:
$data = "xWnsqJJ4pqWTrm8kIwfyw1djD4lu0zig0wnohS+EtwDvHBgEP/BS25qyaE+QEdxd";
$keystring = "MyPassphraseXYZ";
$salt = "A99BC8325634E303";
$iterationsMd5 = 19;
$decrypted = PbeWithMd5AndDes::decrypt($data, $keystring, $salt, $iterationsMd5);
print($decrypted . "\n");
The following must be taken into account: PbeWithMd5AndDes is outdated and that already for years, see here. The Github-code itself uses other deprecated functions such as mcrypt_module_XXX() and mcrypt_generic_YYY(), so that this code can only be executed with PHP < 7.2. In PHP 7.1, deprecated-warnings are displayed. Only for PHP < 7.1 the code can be executed without warnings. All in all, algorithm and code are insecure.
I take a data string = "AkhilRanjanBiharabcdefghijklmnopMovedtoChennai18", encrypt it first and then decrypt it. The string which I get back on decryption is "AkhilRanjanBiharÙ†+™¸„À–ýæó#Movedtoñhennai18" which is almost fine for the first 16 and final 16 characters, but the 16 characters in the middle are absolute junk. What can possibly be going wrong?
My encryption code:-
public String encrypt(String value) {
log.info("This method is not going to be used");
String key = "theabcd#heymaths";
initVector = "{{{{{{{{{{{{{{{{";
String encryptedStr="";
byte[] encrBytes =null;
try {
IvParameterSpec iv = new IvParameterSpec(initVector.getBytes());
SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes(), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv);
encrBytes = cipher.doFinal(value.getBytes());
encryptedStr = new String(encrBytes);
} catch (Exception ex) {
ex.printStackTrace();
}
String strToBeEncoded = encryptedStr +"::"+initVector;
encrBytes = strToBeEncoded.getBytes();
//String encoded = Base64.encodeBase64String(encrBytes);
String encoded = Base64.getEncoder().encodeToString(encrBytes);
String urlEncoded = null;
try {
urlEncoded = java.net.URLEncoder.encode(encoded, CHARSET);
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return urlEncoded;
}
Decryption code:-
public String decrypt(String encrypted) {
String decryptedStr = null;
byte[] base64Bytes = null;
String urlDecoded = null;
String key = HmCommonProperty.getProperty("abcd_crypt_key");
if(key == null || key.isEmpty()) {
key = securityKey;
}
String encryptionMech = HmCommonProperty.getProperty("abcd_crypt_algo");
if(encryptionMech == null || encryptionMech.isEmpty()) {
encryptionMech = CRYPT_MECHANISM;
}
try {
//Url and Base64 decoding
urlDecoded = java.net.URLDecoder.decode(encrypted, CHARSET);
//base64Bytes = Base64.decodeBase64(urlDecoded);
base64Bytes = Base64.getDecoder().decode(urlDecoded);
//Generating IV
String str = new String(base64Bytes);
String[] bodyIVArr = str.split("::");
initVector = bodyIVArr[1];
String bodyStr = bodyIVArr[0];
//AES Decryption
Cipher cipher = Cipher.getInstance(encryptionMech);
IvParameterSpec iv = new IvParameterSpec(initVector.getBytes());
System.out.println("initVector Length -> "
+iv.getIV().length);
System.out.println("input length -> "
+bodyStr.getBytes().length);
SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes(), "AES");
cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv);
byte[] decryptedBytes = cipher.doFinal(bodyStr.getBytes());
decryptedStr = new String(decryptedBytes);
} catch (Exception ex) {
ex.printStackTrace();
log.error("Error occurred while decryption abcd data",ex);
}
return decryptedStr;
}
Your encrypted data is a sequence of bytes. If you need to encode it as a string, you should use base64 or a similar encoding that is intended for encoding arbitrary byte arrays. Pretending that your arbitrary byte array is a valid string-encoding is going to cause you trouble, even if you use ISO_8859_1.
Replace
encryptedStr = new String(encrBytes)
with
encryptedStr = Base64.getEncoder().encodeToString(encrBytes)
and replace
bodyStr.getBytes()
with
Base64.getDecoder().decode(bodyStr)
See also: How to correctly and consistely get bytes from a string for AES encryption?
Your error lies here:
encryptedStr = new String(encrBytes);
strToBeEncoded.getBytes();
These methods use the platform default character set, and when you convert from byte[] to String and back to byte[] the process is lossy in the general case. The only way it's not lossy is if the platform default character set is "ISO_8859_1".
I changed all of 11 such calls to:
encryptedStr = new String(encrBytes, StandardCharsets.ISO_8859_1);
strToBeEncoded.getBytes(StandardCharsets.ISO_8859_1);
(I didn't change CHARSET). The output I get now is:
initVector Length -> 16
input length -> 48
AkhilRanjanBiharabcdefghijklmnopMovedtoChennai18
Bonus warning 1: The encryption uses hardcoded "AES/CBC/NoPadding" but the decryption is dynamic (it should of course also use "AES/CBC/NoPadding").
Bonus warning 2: The chance is low but it's entirely possible that "::" appears inside the encrBytes, screwing up your str.split("::");. One solution is to search for the last occurrence of "::" and only split on that.
I am using a MOL API for VOUCHER recharges. Which returns a PIN number in response is successful. That PIN number will be an encrypted text and I need to triple decrypt it!
I contacted the API support, They provided with the code below:
byte[] PinBytes = Base64.decodeBase64(encryptedText.getBytes("utf-8"));
byte[] VectorBytes = Base64.decodeBase64(vectorKey.getBytes("utf-8"));
byte[] SecretKeyBytes = Base64.decodeBase64(secretKey.getBytes("utf-8"));
TripleDESProvider = CreateTripleDESCryptographicProvider(VectorBytes, SecretKeyBytes)
DecryptedBytes = TripleDESProvider.Decrypt(PinBytes)
Here VectorBytes and SecretKeyBytes are security keys provided by them and PinBytes are the response PIN number which is encrypted.
I googled while I was not able to get a correct solution with these three parameters. Any help, please?
I tried this:
try
{
String encryptedText = "FN0hbSrVzkqhe+w2rQefAQ==";
String vectorKey = "7EsBtzAJjMg=";
//32 bit key
String secretKey = "08061052989102040806105298910204";
byte[] PinBytes = Base64.decodeBase64(encryptedText.getBytes("utf-8"));
byte[] VectorBytes = Base64.decodeBase64(vectorKey.getBytes("utf-8"));
byte[] SecretKeyBytes = Base64.decodeBase64(secretKey.getBytes("utf-8"));
final MessageDigest md = MessageDigest.getInstance("md5");
final byte[] digestOfPassword = md.digest(SecretKeyBytes);
final byte[] keyBytes = Arrays.copyOf(digestOfPassword, 24);
for (int j = 0, k = 16; j < 8;)
{
keyBytes[k++] = keyBytes[j++];
}
final SecretKey key = new SecretKeySpec(keyBytes, "DESede");
final IvParameterSpec iv = new IvParameterSpec(VectorBytes);
final Cipher decipher = Cipher.getInstance("DESede/CBC/PKCS5Padding");
decipher.init(Cipher.DECRYPT_MODE, key, iv);
//final byte[] encData = new sun.misc.BASE64Decoder().decodeBuffer(message);
final byte[] plainText = decipher.doFinal(PinBytes);
System.out.println(plainText.toString());
}
catch (java.security.InvalidAlgorithmParameterException e) { System.out.println("Invalid Algorithm"); }
catch (javax.crypto.NoSuchPaddingException e) { System.out.println("No Such Padding"); }
catch (java.security.NoSuchAlgorithmException e) { System.out.println("No Such Algorithm"); }
catch (java.security.InvalidKeyException e) { System.out.println("Invalid Key"); }
catch (BadPaddingException e) { System.out.println("Invalid Key");}
catch (IllegalBlockSizeException e) { System.out.println("Invalid Key");}
catch (UnsupportedEncodingException e) { System.out.println("Invalid Key");}
catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Error is Invalid KEY
The document they provide is;
Convert Encrypted Pin into Byte Array from Base64 format
PinBytes = ConvertFromBase64String ("FN0hbSrVzkqhe+w2rQefAQ==")
As Vector Key and Secret Key are provided in Base64 format, thus we need to convert both data into Byte Array as well
VectorBytes = ConvertFromBase64String ("xxxxxxxxx")
SecretKeyBytes = ConvertFromBase64String ("xxxxxxxxxxxx")
Please create your Triple DES Cryptographic Provider and set your IV byte array and Secret Key byte array into your Provider.
TripleDESProvider = CreateTripleDESCryptographicProvider(VectorBytes, SecretKeyBytes)
Please invokes Triple DES Provider decrypt method.
DecryptedBytes = TripleDESProvider.Decrypt(PinBytes)
Finally convert back decrypted bytes back to string.
ConvertToString(DecryptedBytes)
The result should be 8157151550.
I am not sure if this is the right approach but with AES encryption it works. They provided you the vector, use it to initialize the IVParameterSpec, then create Key object and Cipher instance:
// this is the encripted text
byte[] PinBytes = Base64.decodeBase64(encryptedText.getBytes("utf-8"));
byte[] VectorBytes = Base64.decodeBase64(vectorKey.getBytes("utf-8"));
byte[] SecretKeyBytes = Base64.decodeBase64(secretKey.getBytes("utf-8"));
// initialize the vector with the one you receive
IvParameterSpec spec = new IvParameterSpec(VectorBytes);
// create the key. DESede should be correct, but if it doesn't work try also with DES
Key key = new SecretKeySpec(SecretKeyBytes, "DESede");
// Initialize the cipher
Cipher c = Cipher.getInstance("DESede/CBC/PKCS5Padding");
// decrypt the string
c.init(Cipher.DECRYPT_MODE, key, spec);
byte[] decodedDecryptedBytes = c.doFinal(PinBytes);
Base64 object i use is from apache common codec library, but you can use the library you want.
Finally, I Got this answer with same provided secretKey!!
try
{
byte[] PinBytes = Base64.decodeBase64(encryptedText);
byte[] VectorBytes = Base64.decodeBase64(vectorKey);
byte[] SecretKeyBytes = Base64.decodeBase64(secretKey);
// initialize the vector with the one you receive
IvParameterSpec spec = new IvParameterSpec(VectorBytes);
// create the key. DESede should be correct, but if it doesn't work try also with DES
Key key = new SecretKeySpec(SecretKeyBytes, "DESede");
// Initialize the cipher
Cipher c = Cipher.getInstance("DESede/CBC/PKCS5Padding");
// decrypt the string
c.init(Cipher.DECRYPT_MODE, key, spec);
byte[] decodedDecryptedBytes = c.doFinal(PinBytes);
return new String(decodedDecryptedBytes, "UTF-8");
}
catch (java.security.InvalidAlgorithmParameterException e) { System.out.println("Invalid Algorithm"); }
catch (javax.crypto.NoSuchPaddingException e) { System.out.println("No Such Padding"); }
catch (java.security.NoSuchAlgorithmException e) { System.out.println("No Such Algorithm"); }
catch (java.security.InvalidKeyException e) { System.out.println("InvalidKeyException : Invalid Key"); }
catch (BadPaddingException e) { System.out.println("BadPaddingException : Invalid Key");}
catch (IllegalBlockSizeException e) { System.out.println("IllegalBlockSizeException : Invalid Key");}
catch (UnsupportedEncodingException e) { System.out.println("UnsupportedEncodingException : Invalid Key");}
catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
The Mistake was doing this:
byte[] SecretKeyBytes = Base64.decodeBase64(secretKey.getBytes("utf-8"));
I don't know why this happens! But by removing that I got the answer!!
I've been banging my head against the wall trying to figure out exactly how to format everything to decrypt this string in PHP that's been encrypted in a custom Java class.
Here's the relevent functions from the Java class. The "salt" variable is a class variable byte array set earlier:
public DesEncrypter(String passPhrase) {
try {
// Create the key
KeySpec keySpec = new PBEKeySpec(passPhrase.toCharArray(), salt,
iterationCount);
SecretKey key = SecretKeyFactory.getInstance("PBEWithMD5AndDES")
.generateSecret(keySpec);
ecipher = Cipher.getInstance(key.getAlgorithm());
dcipher = Cipher.getInstance(key.getAlgorithm());
// Prepare the parameter to the ciphers
AlgorithmParameterSpec paramSpec = new PBEParameterSpec(salt,
iterationCount);
// Create the ciphers
ecipher.init(Cipher.ENCRYPT_MODE, key, paramSpec);
dcipher.init(Cipher.DECRYPT_MODE, key, paramSpec);
} catch (java.security.InvalidAlgorithmParameterException e) {
} catch (java.security.spec.InvalidKeySpecException e) {
} catch (javax.crypto.NoSuchPaddingException e) {
} catch (java.security.NoSuchAlgorithmException e) {
} catch (java.security.InvalidKeyException e) {
}
}
public String encrypt(String str) {
try {
// Encode the string into bytes using utf-8
byte[] utf8 = str.getBytes("UTF8");
// Encrypt
byte[] enc = ecipher.doFinal(utf8);
// Encode bytes to base64 to get a string
return new sun.misc.BASE64Encoder().encode(enc);
} catch (javax.crypto.BadPaddingException e) {
} catch (IllegalBlockSizeException e) {
} catch (UnsupportedEncodingException e) {
}
return null;
}
public String decrypt(String str) {
try {
// Decode base64 to get bytes
byte[] dec = new sun.misc.BASE64Decoder().decodeBuffer(str);
// Decrypt
byte[] utf8 = dcipher.doFinal(dec);
// Decode using utf-8
return new String(utf8, "UTF8");
} catch( Exception ex) {
ex.printStackTrace(System.err);
}
return null;
}
And here's what I have so far in PHP (FYI, I'm using this encryption library in PHP https://github.com/phpseclib/phpseclib):
$app->get('/decrypt', function () use ($app) {
$data = '3aCRLRd3srA/QF4MQb0D+P==';
$salt = pack('nvc*', 0xB7, 0x9A, 0xC1, 0x34, 0x26, 0x89, 0xW3, 0x30);
$secret = "secret";
$keyLength = 16;
$cipher = new Crypt_DES(CRYPT_DES_MODE_CBC);
$cipher->setPassword($secret, 'pbkdf2', 'md5', $salt, $keyLength);
var_dump($cipher->decrypt($data));
});
Right now it's dumping out a bunch of binary, which I've tried base64_decoding, but that doesn't do anything either.
If key.getAlgorithm() is "DES" then you need to provide a fully specified Cipher name like "DES/CBC/PKCS5Padding".
You will also need to provide the IV if it is non-null. Usually the IV is prepended to the ciphertext.
You can get the IV with cipher.getIV() and set with $cipher->setIV('...');.
I am trying to create a signature using the HMAC-SHA256 algorithm and this is my code.
I am using US ASCII encoding.
final Charset asciiCs = Charset.forName("US-ASCII");
final Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
final SecretKeySpec secret_key = new javax.crypto.spec.SecretKeySpec(asciiCs.encode("key").array(), "HmacSHA256");
sha256_HMAC.init(secret_key);
final byte[] mac_data = sha256_HMAC.doFinal(asciiCs.encode("The quick brown fox jumps over the lazy dog").array());
String result = "";
for (final byte element : mac_data)
{
result += Integer.toString((element & 0xff) + 0x100, 16).substring(1);
}
System.out.println("Result:[" + result + "]");
The result that I am getting from the above code is:
f7bc83f430538424b13298e6aa6fb143ef4d59a14946175997479dbc2d1a3cd8
This is same as to that of shown in the wiki
HMAC_SHA256("key", "The quick brown fox jumps over the lazy dog") = 0x f7bc83f430538424b13298e6aa6fb143ef4d59a14946175997479dbc2d1a3cd8
except for the 0x.
I am looking for ideas/comments if I am doing everything right or may be I can improve my code.
Here is my solution:
public static String encode(String key, String data) throws Exception {
Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
SecretKeySpec secret_key = new SecretKeySpec(key.getBytes("UTF-8"), "HmacSHA256");
sha256_HMAC.init(secret_key);
return Hex.encodeHexString(sha256_HMAC.doFinal(data.getBytes("UTF-8")));
}
public static void main(String [] args) throws Exception {
System.out.println(encode("key", "The quick brown fox jumps over the lazy dog"));
}
Or you can return the hash encoded in Base64:
Base64.encodeBase64String(sha256_HMAC.doFinal(data.getBytes("UTF-8")));
The output in hex is as expected:
f7bc83f430538424b13298e6aa6fb143ef4d59a14946175997479dbc2d1a3cd8
The 0x just denotes that the characters after it represent a hex string.
0x1A == 1Ah == 26 == 1A
So the 0x is just to clarify what format the output is in, no need to worry about it.
If you're using Guava, its latest release now lets you use
Hashing.hmacSha256()
One example of using this:
String hash = Hashing.hmacSha256("mykey".getBytes(StandardCharsets.UTF_8)).hashString("my_message", StandardCharsets.UTF_8).toString()
Further documentation here: https://guava.dev/releases/23.0/api/docs/com/google/common/hash/Hashing.html#hmacSha256-byte:A-
The answer that you got there is correct. One minor thing in the code above, you need to init(key) before you can call doFinal()
final Charset charSet = Charset.forName("US-ASCII");
final Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
final SecretKeySpec secret_key = new javax.crypto.spec.SecretKeySpec(charSet.encode("key").array(), "HmacSHA256");
try {
sha256_HMAC.init(secret_key);
} catch (InvalidKeyException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
...
This is working fine for me
I have add dependency
compile 'commons-codec:commons-codec:1.9'
ref: http://mvnrepository.com/artifact/commons-codec/commons-codec/1.9
my function
public String encode(String key, String data) {
try {
Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
SecretKeySpec secret_key = new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), "HmacSHA256");
sha256_HMAC.init(secret_key);
return Hex.encodeHexString(sha256_HMAC.doFinal(data.getBytes(StandardCharsets.UTF_8)));
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return null;
}
Try this
Sorry for being late, I have tried all above answers but none of them is giving me correct value, After doing the lot of R&D I have found a simple way that gives me exact value.
Declare this method in your class
private String hmacSha(String KEY, String VALUE, String SHA_TYPE) {
try {
SecretKeySpec signingKey = new SecretKeySpec(KEY.getBytes("UTF-8"), SHA_TYPE);
Mac mac = Mac.getInstance(SHA_TYPE);
mac.init(signingKey);
byte[] rawHmac = mac.doFinal(VALUE.getBytes("UTF-8"));
byte[] hexArray = {(byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', (byte)'5', (byte)'6', (byte)'7', (byte)'8', (byte)'9', (byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f'};
byte[] hexChars = new byte[rawHmac.length * 2];
for ( int j = 0; j < rawHmac.length; j++ ) {
int v = rawHmac[j] & 0xFF;
hexChars[j * 2] = hexArray[v >>> 4];
hexChars[j * 2 + 1] = hexArray[v & 0x0F];
}
return new String(hexChars);
}
catch (Exception ex) {
throw new RuntimeException(ex);
}
}
Use this like
Log.e("TAG", "onCreate: "+hmacSha("key","text","HmacSHA256"));
Verification
1.Android studio output
2. Online HMAC generator Output(Visit here for Online Genrator)
Java simple code to generate encoded(HMAC-x) signatures. (Tried using Java-8 and Eclipse)
import java.io.UnsupportedEncodingException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import com.sun.org.apache.xml.internal.security.utils.Base64;
/**
* Encryption class to show how to generate encoded(HMAC-x) signatures.
*
*/
public class Encryption {
public static void main(String args[]) {
String message = "This is my message.";
String key = "your_key";
String algorithm = "HmacMD5"; // OPTIONS= HmacSHA512, HmacSHA256, HmacSHA1, HmacMD5
try {
// 1. Get an algorithm instance.
Mac sha256_hmac = Mac.getInstance(algorithm);
// 2. Create secret key.
SecretKeySpec secret_key = new SecretKeySpec(key.getBytes("UTF-8"), algorithm);
// 3. Assign secret key algorithm.
sha256_hmac.init(secret_key);
// 4. Generate Base64 encoded cipher string.
String hash = Base64.encode(sha256_hmac.doFinal(message.getBytes("UTF-8")));
// You can use any other encoding format to get hash text in that encoding.
System.out.println(hash);
/**
* Here are the outputs for given algorithms:-
*
* HmacMD5 = hpytHW6XebJ/hNyJeX/A2w==
* HmacSHA1 = CZbtauhnzKs+UkBmdC1ssoEqdOw=
* HmacSHA256 =gCZJBUrp45o+Z5REzMwyJrdbRj8Rvfoy33ULZ1bySXM=
* HmacSHA512 = OAqi5yEbt2lkwDuFlO6/4UU6XmU2JEDuZn6+1pY4xLAq/JJGSNfSy1if499coG1K2Nqz/yyAMKPIx9C91uLj+w==
*/
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
}
}
}
NOTE: You can use any other Algorithms and can try generating HmacMD5, HmacSHA1, HmacSHA256, HmacSHA512 signatures.
If but any chance you found a solution how to calculate HMAC-SHA256 here, but you're getting an exception like this one:
java.lang.NoSuchMethodError: No static method
encodeHexString([B)Ljava/lang/String; in class
Lorg/apache/commons/codec/binary/Hex; or its super classes
(declaration of 'org.apache.commons.codec.binary.Hex' appears in
/system/framework/org.apache.http.legacy.boot.jar)
Then use:
public static String encode(String key, String data) {
try {
Mac hmac = Mac.getInstance("HmacSHA256");
SecretKeySpec secret_key = new SecretKeySpec(key.getBytes("UTF-8"), "HmacSHA256");
hmac.init(secret_key);
return new String(Hex.encodeHex(hmac.doFinal(data.getBytes("UTF-8"))));
} catch (Exception e) {
throw new RuntimeException(e);
}
}
Here is my solution:
public String HMAC_SHA256(String secret, String message)
{
String hash="";
try{
Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
SecretKeySpec secret_key = new SecretKeySpec(secret.getBytes(), "HmacSHA256");
sha256_HMAC.init(secret_key);
hash = Base64.encodeToString(sha256_HMAC.doFinal(message.getBytes()), Base64.DEFAULT);
}catch (Exception e)
{
}
return hash.trim();
}