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();
}
Related
Trying to create java class which will encrypt and decrypt as like C# code in below.
Below is my code for C#, I need to convert it to Java. Can someone help me how to do it?
I've been doing this for almost 2 days yet can't find any solutions on how to convert it. I am new to crypto.
Note - It seems C# code used ACME libraries. But in my java, i should not use ACME jar files.
public static string EncryptBase64(string key, string clearText)
{
if (key.Length > 8)
key = key.Substring(0, 8);
byte[] keyBytes = System.Text.Encoding.ASCII.GetBytes(key);
byte[] clearBytes = GetClearTextBytes(clearText);
// calculate the number of legitimate bytes in the last block
byte lastByte = (byte)(8 - (clearBytes.Length - textEncoding.GetByteCount(clearText)));
MemoryStream ms = new MemoryStream();
DES des = new DESCryptoServiceProvider();
des.Padding = PaddingMode.None;
des.GenerateIV();
System.Security.Cryptography.ICryptoTransform ict = des.CreateEncryptor(keyBytes, des.IV);
CryptoStream cs = new CryptoStream(ms, ict, CryptoStreamMode.Write);
cs.Write(clearBytes, 0, clearBytes.Length);
cs.FlushFinalBlock();
ms.Close();
byte[] cipherBytes = ms.ToArray();
// create a byte output stream for Acme compatibality
MemoryStream acmeCompatStream = new MemoryStream();
// Acme writes the IV to the frist block
acmeCompatStream.Write(des.IV, 0, 8);
for (int i = 0; i < cipherBytes.Length; i = i + 8)
{
// write the next block
acmeCompatStream.Write(cipherBytes, i, 8);
// write the number of valid bytes in the block
if (i == cipherBytes.Length - 8)
acmeCompatStream.WriteByte(lastByte);
else
acmeCompatStream.WriteByte(8);
}
acmeCompatStream.Close();
cipherBytes = acmeCompatStream.ToArray();
return (System.Convert.ToBase64String(cipherBytes));
}
Below is the code which i have tried in java. I have two different encryption function. I have tried both of the encryption method. But both are not giving expected encrypted string to decrypt in acme.
package com.abc.some.common.nativeDES;
import java.io.ByteArrayOutputStream;
import java.security.spec.KeySpec;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESKeySpec;
import javax.crypto.spec.IvParameterSpec;
import javax.xml.bind.DatatypeConverter;
public class DESEncrypt {
public String keyValue = "123456789";
public static void main(String[] args) {
String text = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?><SomeRequest><OrderNumber>1564578</OrderNumber></SomeRequest>";
String codedtext ="not encrypted";
try{
codedtext = new DESEncrypt().Encrypt1(text);
//codedtext = new DESEncrypt().encrypt(text);
}catch (Exception e) {
System.out.println("Exception in Encryption.. " + e.getMessage());
}
System.out.println(codedtext);
}
public String Encrypt1(String CXML) {
try {
KeySpec myKey = new DESKeySpec(keyValue.getBytes("UTF8"));
SecretKey key = SecretKeyFactory.getInstance("DES").generateSecret(myKey);
Cipher ecipher = Cipher.getInstance("DES");
ecipher.init(Cipher.ENCRYPT_MODE, key);
byte[] data = CXML.getBytes("ASCII");
Cipher c = Cipher.getInstance("DES/CBC/PKCS5Padding");
c.init(Cipher.ENCRYPT_MODE, key);
byte[] crypt = ecipher.doFinal(data);
//String encoded = DatatypeConverter.printBase64Binary(crypt.toString().getBytes("ASCII"));
//String encoded = DatatypeConverter.printBase64Binary(crypt.getBytes("ASCII"));
String encoded = DatatypeConverter.printBase64Binary(crypt).toString();
System.out.println(encoded);
return encoded;
} catch (Exception ex) {
}
return null;
}
}
But I have used below java file to encrypt the string which is using acme jar files. This was working as expected. But as per my project requirement i should not use the external(ACME) jar files.
package com.abc.common.encryption;
import java.io.FileInputStream;
import java.util.Properties;
import Acme.Crypto.SecurityDES;
public class ESBCryptoDES {
public static void main(String args[]){
// DESEncrypt("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?><SomeRequest><OrderNumber>1564578</OrderNumber></SomeRequest>"
// ,"D:\\name\\proj\\order\\Encryption\\st.properties");
DESDecrypt("vQ9C7ZrLzjQpHvZjtHvUb0mFCr824/aClY2jKbeciczsRVr+kEETFvDuHgdBS/aLskYV3WX3U5TANSlK3pH80r3xOyn9Q8rTjlB/yXyU7J9MgibJ66jJx0wrqeloAkmQzqj+b5+I/lXANSlK3pH80kT1D+jqWAeV"
,"D:\\name\\proj\\order\\Encryption\\stDecrypt.properties");
}
public static String DESEncrypt(String SourceStrForCrypt,String PropPath) {
String encrypt = "";
String decrypt = "";
// Load the property file.
Properties prop = new Properties();
try {
FileInputStream in = new FileInputStream(PropPath);
prop.load(in);
in.close();
} catch (Exception e) {
System.out.println("Exception in loading property file.. "
+ e.getMessage());
}
// Encrypt the given content.
try {
String keypath = prop.getProperty("proj.sample.DEV");
System.out.println("sample" + keypath);
String SourceToEncrypt = SourceStrForCrypt; //This will provide the xml string to encrypt
// Encryption
encrypt = SecurityDES.DesEncryptBase64(keypath,SourceToEncrypt);
System.out.println(encrypt);
// Decryption
decrypt = SecurityDES.DesDecryptBase64(keypath, encrypt);
System.out.println(decrypt);
} catch (Exception e) {
System.out.println("Exception in Encryption.. " + e.getMessage());
}
return encrypt;
}
public static String DESDecrypt(String SourceStrForCrypt,String PropPath) {
// TODO Auto-generated method stub
String decrypt = "";
Properties prop = new Properties();
try {
FileInputStream in = new FileInputStream(PropPath);
prop.load(in);
in.close();
} catch (Exception e) {
System.out.println("Exception in loading property file.. "+ e.getMessage());
}
try {
String abc_keyPath = prop
.getProperty("proj.abc.DEV");
System.out.println("keypath" + abc_keyPath);
// Decryption
decrypt = SecurityDES.DesDecryptBase64(abc_keyPath, SourceStrForCrypt);
System.out.println("decrypted..."+decrypt);
} catch (Exception e) {
System.out.println("Exception in Encryption.. " + e.getMessage());
}
return decrypt;
}
}
Calling doFinal() twice doesn't make sense.
Printing the value of byte[].toString() doesn't make sense. It doesn't contain the ciphertext.
Converting that to base-64 doesn't make sense. It still doesn't contain the ciphertext.
You need to convert the byte[] array returned by the first doFinal() call directly to base-64, without the round-trip to and from String() caused by calling toString() and then `getBytes().
NB For some reason you have a variable called encrypt in your decrypt method, and for some even stranger reason you are returning it instead of decrypt, which is the only variable that actually contains plaintext.
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.
I am converting my C# encryption code to Android.
I am facing issue like I am not able to encrypt the text as same as C#.
Below I copy paste both code.
Both are working code regarding using it you can use any password & any plain text .You will find both have different output.
C# CODE
System.security.Cryptography.RijndaelManaged AES = new System.Security.Cryptography.RijndaelManaged();
System.Security.Cryptography.MD5CryptoServiceProvider Hash_AES = new System.Security.Cryptography.MD5CryptoServiceProvider();
final MessageDigest Hash_AES = MessageDigest.getInstance("MD5");
String encrypted = "";
try {
byte[] hash = new byte[32];
byte[] temp = Hash_AES.ComputeHash(System.Text.ASCIIEncoding.ASCII.GetBytes(pass));
final byte[] temp = Hash_AES.digest(pass.getBytes("US-ASCII"));
Array.Copy(temp, 0, hash, 0, 16);
Array.Copy(temp, 0, hash, 15, 16);
AES.Key = hash;
AES.Mode = System.Security.Cryptography.CipherMode.ECB;
System.Security.Cryptography.ICryptoTransform DESEncrypter = AES.CreateEncryptor();
byte[] Buffer = System.Text.ASCIIEncoding.ASCII.GetBytes(input);
encrypted = Convert.ToBase64String(DESEncrypter.TransformFinalBlock(Buffer, 0, Buffer.Length));
} catch (Exception ex) {
}
return encrypted;
Here is my Android java code.
ANDROID JAVA CODE
private static String TRANSFORMATION = "AES/ECB/NoPadding";
private static String ALGORITHM = "AES";
private static String DIGEST = "MD5";
byte[] encryptedData;
public RijndaelCrypt(String password,String plainText) {
try {
//Encode digest
MessageDigest digest;
digest = MessageDigest.getInstance(DIGEST);
_password = new SecretKeySpec(digest.digest(password.getBytes()), ALGORITHM);
//Initialize objects
_cipher = Cipher.getInstance(TRANSFORMATION);
_cipher.init(Cipher.ENCRYPT_MODE, _password);
encryptedData = _cipher.doFinal(text);
} catch (InvalidKeyException e) {
Log.e(TAG, "Invalid key (invalid encoding, wrong length, uninitialized, etc).", e);
return null;
} catch (InvalidAlgorithmParameterException e) {
Log.e(TAG, "Invalid or inappropriate algorithm parameters for " + ALGORITHM, e);
return null;
} catch (IllegalBlockSizeException e) {
Log.e(TAG, "The length of data provided to a block cipher is incorrect", e);
return null;
} catch (BadPaddingException e) {
Log.e(TAG, "The input data but the data is not padded properly.", e);
return null;
}
return Base64.encodeToString(encryptedData,Base64.DEFAULT);
}
Should I need to use "US-ASCII" in pass or does it take it?
Use the same mode of operation: either ECB or CBC
Use the same character set: it's best to stick to "UTF-8"
Use the same key: in the C# code you're doubling the 128-bit key to 256 bits
When using CBC with a random IV, it is expected that the ciphertext differs for the same plaintext. The decryption is the operation that determines whether you succeeded.
Note that ECB is not semantically secure. Use CBC with a random IV. The IV doesn't have to be secret, so you can just prepend it to the ciphertext and slice it off before decryption.
It's better to use an authenticated mode like GCM or EAX or if it's not provided an encrypt-then-MAC scheme. It's hard to implement it correctly yourself so stick to some library that does this for you like RNCryptor.
I am porting part of an iOS app to Android, and I'm having trouble porting the following signature generating code in iOS to Android. The iOS code is:
+ (NSString *)hashedBase64ValueOfData:(NSString *) data WithSecretKey:(NSString*)secret {
// ascii convirsion
const char *cKey = [secret cStringUsingEncoding:NSASCIIStringEncoding];
const char *cData = [data cStringUsingEncoding:NSASCIIStringEncoding];
// HMAC Data structure initializtion
unsigned char cHMAC[CC_SHA256_DIGEST_LENGTH];
CCHmac(kCCHmacAlgSHA256, cKey, strlen(cKey), cData, strlen(cData), cHMAC);
// Gerating hased value
NSData *da = [[NSData alloc] initWithBytes:cHMAC length:sizeof(cHMAC)];
return [da base64EncodedString];// conversion to base64 string & returns
}
The Android Java code I have written and tried is:
private static String hashedBase64ValueOfDataWithSecretKey(String data, String secret) {
try {
SecretKeySpec signingKey = new SecretKeySpec(secret.getBytes(), HMAC_SHA1_ALGORITHM);
Mac mac = Mac.getInstance(HMAC_SHA1_ALGORITHM);
mac.init(signingKey);
byte[] rawHmac = mac.doFinal(data.getBytes());
return Base64.encodeToString(rawHmac, 0);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
Upon testing, the Android function is not outputting the same thing as the iOS function (given the same input), and I'm not sure why.
Not an expert at this, but NSASCIIStringEncoding seems to imply that you want data and secret interpreted as ASCII, whereas String.getBytes() uses the default character set by default (i.e. UTF-8).
You probably need to use a different charset:
data.getBytes(StandardCharsets.US_ASCII);
secret.getBytes(StandardCharsets.US_ASCII);
For Java pre-1.7, you'll need to use this and catch the UnsupportedEncodingException:
data.getBytes("US-ASCII");
secret.getBytes("US-ASCII");
You might use extras org.apache.commons.codec.binary.Base64. Google it and find it, then you can fellow the codes below. I think the hashed value will be generated by "private key" and appended behind a "public key" being sent to server with a "http-head" together. If no, you can just remove them. Anyway the codes might give you some suggestions. :)
private String getAppendedHeader(String str) {
try {
String hash = getHash(str);
String signature = new String(Base64.encodeBase64(hash.getBytes()));
StringBuilder sb = new StringBuilder();
sb.append(PUBLIC_KEY).append(' ').append(signature);
return sb.toString();
} catch (NoSuchAlgorithmException _e) {
LL.e("Get mac error: " + _e.getMessage());
return null;
} catch (InvalidKeyException _e) {
LL.e("Init mac error: " + _e.getMessage());
return null;
}
}
private String getHash(String str) throws NoSuchAlgorithmException, InvalidKeyException {
Mac mac = Mac.getInstance("HmacSHA256");
SecretKeySpec secret = new SecretKeySpec(PRIVATE_KEY.getBytes(), "HmacSHA256");
mac.init(secret);
byte[] digest = mac.doFinal(str.getBytes());
BigInteger hash = new BigInteger(1, digest);
String hmac = hash.toString(16);
if (hmac.length() % 2 != 0) {
hmac = "0" + hmac;
}
return hmac;
}
This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
How do I use 3des encryption/decryption in Java?
How do I encrypt/decrypt a string of text using 3DES in java?
I found my answer. Duplicate question that didn't show up when I asked this one.
How do I use 3des encryption/decryption in Java?
From an old code:
public void testSymCypher(SecretKey k, String str)
throws BadPaddingException, IllegalBlockSizeException,
InvalidAlgorithmParameterException, InvalidKeyException,
NoSuchAlgorithmException, NoSuchPaddingException
{
Cipher cip = Cipher.getInstance("DESede/CBC/PKCS5Padding");
cip.init(Cipher.ENCRYPT_MODE,k);
byte[] ciphered = cip.doFinal(str.getBytes());
byte iv[] = cip.getIV();
// printing the ciphered string
printHexadecimal(ciphered);
IvParameterSpec dps = new IvParameterSpec(iv);
cip.init(Cipher.DECRYPT_MODE,k,dps);
byte[] deciphered = cip.doFinal(ciphered);
// printing the deciphered string
printHexadecimal(deciphered);
}
Notice than other usage of DESede are available in Java JDK 6:
DESede/CBC/NoPadding (168)
DESede/CBC/PKCS5Padding (168)
There is also ECB mode available (but be carreful to not use it twice !!), you don't need to use iv part in this case:
DESede/ECB/NoPadding (168)
DESede/ECB/PKCS5Padding (168)
To generate key for DESede:
KeyGenerator generatorDes = KeyGenerator.getInstance("DESede");
SecretKey skaes = generatorDes.generateKey();
Finally I recommand reading this document from SUN if you need to work on Java and Cryptography
We use this little helper class for password-based DES encryption from String to Hex String and back - not sure how to get this working with 3DES though:
import java.security.spec.KeySpec;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.PBEParameterSpec;
public class DesHelper {
private static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(DesHelper.class);
static final byte[] SALT = { (byte) 0x09, /* snip - randomly chosen but static salt*/ };
static final int ITERATIONS = 11;
private Cipher _ecipher;
private Cipher _dcipher;
public DesHelper(final String passphrase) {
try {
final PBEParameterSpec params = new PBEParameterSpec(SALT, ITERATIONS);
final KeySpec keySpec = new PBEKeySpec(passphrase.toCharArray());
final SecretKey key = SecretKeyFactory.getInstance("PBEWithMD5AndDES")
.generateSecret(keySpec);
_ecipher = Cipher.getInstance(key.getAlgorithm());
_dcipher = Cipher.getInstance(key.getAlgorithm());
_ecipher.init(Cipher.ENCRYPT_MODE, key, params);
_dcipher.init(Cipher.DECRYPT_MODE, key, params);
} catch (final Exception e) {
throw new RuntimeException(e);
}
}
public String encrypt(final String string) {
try {
// Encode the string into bytes using utf-8
final byte[] bytes = string.getBytes("UTF-8");
// Encrypt
final byte[] enc = _ecipher.doFinal(bytes);
// Encode bytes to base64 to get a string
return bytesToHex(enc);
} catch (final Exception e) {
throw new RuntimeException(e);
}
}
public String decrypt(final String str) {
try {
// Decode base64 to get bytes
final byte[] dec = hexToBytes(str);
// Decrypt
final byte[] utf8 = _dcipher.doFinal(dec);
// Decode using utf-8
return new String(utf8, "UTF8");
} catch (final Exception e) {
log.info("decrypting string failed: " + str + " (" + e.getMessage() + ")");
return null;
}
}
private static String bytesToHex(final byte[] bytes) {
final StringBuilder buf = new StringBuilder(bytes.length * 2);
for (final byte b : bytes) {
final String hex = Integer.toHexString(0xff & b);
if (hex.length() == 1) {
buf.append("0");
}
buf.append(hex);
}
return buf.toString();
}
private static byte[] hexToBytes(final String hex) {
final byte[] bytes = new byte[hex.length() / 2];
for (int i = 0; i < bytes.length; i++) {
bytes[i] = (byte) Integer.parseInt(hex.substring(i * 2, i * 2 + 2), 16);
}
return bytes;
}
}
You would use this class like this:
public static void main(final String[] args) {
final DesHelper h = new DesHelper("blabla");
System.out.println(h.decrypt(h.encrypt("foobar")));
}
I wrote an article on this sometimes back. Please visit the following link in my blog that has a working, completed code with explanations and diagram.
View My Triple DES Encryption Article, Code Here
Hopefully you will find it helpful.
You may also consider using a stream cipher (e.g., OFB or CTR mode on top of a 3DES block encryption), so that you don't have to deal with padding the string to a multiple of the cipher blocksize.