I have a code in PHP:
$str=base64_encode('1234');
$key='1234';
print(base64_encode(hash_hmac('sha256', $str, $key,true)));
And what code for Android Java (Android Studio)?
This code gives different result that in PHP:
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Base64;
import android.util.Log;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
private String hash_hmac(String str, String secret) throws Exception{
Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
byte[] string = str.getBytes();
String stringInBase64 = Base64.encodeToString(string, Base64.DEFAULT);
SecretKeySpec secretKey = new SecretKeySpec(secret.getBytes(), "HmacSHA256");
sha256_HMAC.init(secretKey);
String hash = Base64.encodeToString(sha256_HMAC.doFinal(stringInBase64.getBytes()), Base64.DEFAULT);
return hash;
}
String str = "1234";
String key = "1234";
try {
Log.d("HMAC:", hash_hmac(str,key));
} catch (Exception e) {
Log.d("HMAC:","stop");
e.printStackTrace();
}
But in native Java it works fine. I can not resolve this ;(
Maybe any limits for Android platform or device?
You are converting your input string to base64 that's why it's not matching. here is correct code -
private String hash_hmac(String str, String secret) throws Exception{
Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
SecretKeySpec secretKey = new SecretKeySpec(secret.getBytes(), "HmacSHA256");
sha256_HMAC.init(secretKey);
String hash = Base64.encodeToString(sha256_HMAC.doFinal(str.getBytes()), Base64.DEFAULT);
return hash;
}
Related
I'm getting this error when I try to decrypt a message which has already been encrypted.
I've done this by first encrypting a message in this case "Testing message"
Then running the method again but decrypting instead of encrypting.
I've looked at the other questions regarding this but could not fix the problem
import java.nio.charset.StandardCharsets;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.spec.InvalidKeySpecException;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.PBEParameterSpec;
public class PBEusing {
public static void main(String[] args) throws Exception {
PBEusing pbe = new PBEusing();
String encrypt = pbe.pbe("encrypt", "passwordTest", "Testing message");
System.out.println(encrypt);
String decrypt = pbe.pbe("decrypt", "passwordTest", encrypt);
System.out.println(decrypt);
}
public static String pbe(String cipherMethod, String clientPassword, String clientMessage) throws Exception {
String method = cipherMethod.toUpperCase();
String output = "";
SecureRandom rnd = new SecureRandom();
byte[] iv = new byte[16];
rnd.nextBytes(iv);
byte[] plaintext = clientMessage.getBytes(StandardCharsets.UTF_8); // input message from user
byte[] salt = "01234567".getBytes(StandardCharsets.UTF_8);
IvParameterSpec ivParamSpec = new IvParameterSpec(iv);
PBEParameterSpec pbeParamSpec = new PBEParameterSpec(salt, 10000, ivParamSpec);
PBEKeySpec keySpec = new PBEKeySpec(clientPassword.toCharArray());
try {
SecretKeyFactory kf = SecretKeyFactory.getInstance("PBEWithHmacSHA256AndAES_128");
SecretKey secretKey = kf.generateSecret(keySpec);
// On J2SE the SecretKeyfactory does not actually generate a key, it just wraps the password.
// The real encryption key is generated later on-the-fly when initializing the cipher
System.out.println(new String(secretKey.getEncoded()));
// Encrypt
if (method.equals("ENCRYPT")) {
Cipher enc = Cipher.getInstance("PBEWithHmacSHA256AndAES_128");
enc.init(Cipher.ENCRYPT_MODE, secretKey, pbeParamSpec);
byte[] encrypted = enc.doFinal(plaintext);
output = new BASE64Encoder().encode(encrypted);
System.out.println("Encrypted text: " + output);
} else {
// Decrypt
Cipher dec = Cipher.getInstance("PBEWithHmacSHA256AndAES_128");
dec.init(Cipher.DECRYPT_MODE, secretKey, pbeParamSpec);
byte[] decrypted = dec.doFinal(plaintext);
String test = new BASE64Encoder().encode(decrypted);
//String message = new String(test, StandardCharsets.UTF_8);
output = test;
}
} catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
}
return output;
}
}
You're Base64 encoding the output of the encryption but aren't Base64 decoding it again before you try to decrypt it.
Related to this question:
Cannot decrypt AES-256 GCM with Java
The Java decrypt issue seems to only be fixed if the encrypted message is short, i.e. two words or so. I've tried with the words, "hello" and "short string", and both of these words were decrypted fine. When I tried something like,
Alphanumeric string test1 with more numbers such as 5, 4, 3, 2, 1
AEADBadTagException came up again.
EDIT:
This issue is directly related to how long the encrypted message is. Two words is a bit of an exaggeration, but as long as the encrypted message is about as long as this or longer then Java will run into the exception.
Encrypted message sample:
d+nyOuSfH3wup+5KHJRQyVwVHE0nn7dOfLQsSxb2LsR1LuogHxmVobHoQSTbdyqupd/UvwGfbhkUQz+8CjIBSd7FoEVpgpYv9dAQ3GGUr3AtA+rJJrFHo/EM443sQlSOG4cIBQ7trF7udmrIhtiZ9wMchaBEJFmDBL5Jwl8ZMM0ath8VNWqfyyhghPW8U2NiORAy5mw6v07o7v3UT2
lBzJThBsM=
Decrypted with node:
this is a longer string to make the encrypted message longer than before
EDIT 2:
Java code:
package decryption;
import java.io.UnsupportedEncodingException;
import java.util.Arrays;
import java.util.Base64;
import javax.crypto.Cipher;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
public class DecryptAES256 {
private static String salt;
private static byte[] iv;
private static byte[] encryptedMessageAndTag;
private static byte[] key;
public static void main(String[] args) {
String key = "123456789aabbccddeefffffffffffff";
String sourceText = "zMX8Xp8lCLGP3FsF7dy1uEODFG0+lhpoWR+xZPpNAXm2D39+CJUK5Kk0z4NbDfb/WbP8lHVWcTOuXf8hRA1AmtEV2G5kP3SH3mrGbyf4QthR4aOTqEQQAvt1T8LlIkBlgx32gehP/nwwm3DYyJV+NnN21Ac17L4=";
System.out.println(decrypt(key, sourceText));
}
public static String decrypt(String masterkey, String encryptedText) {
// decode encryptedText
encryptedText = new String(Base64.getDecoder().decode(encryptedText.getBytes()));
// extract the different parts
byte[] parts = encryptedText.getBytes();
salt = new String(Arrays.copyOfRange(parts, 0, 64)); // not using for testing purposes
iv = Arrays.copyOfRange(parts, 64, 76);
encryptedMessageAndTag = Arrays.copyOfRange(parts, 76, parts.length);
try {
key = masterkey.getBytes("UTF-8");
} catch (UnsupportedEncodingException e) {
// not going to reach here
}
// call helper method to decrypt
byte[] decipheredText = decodeAES_256_CBC();
return new String(decipheredText);
}
private static byte[] decodeAES_256_CBC() {
try {
SecretKeySpec skeySpec = new SecretKeySpec(key, "AES");
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
GCMParameterSpec params = new GCMParameterSpec(128, iv, 0, iv.length);
cipher.init(Cipher.DECRYPT_MODE, skeySpec, params);
return cipher.doFinal(encryptedMessageAndTag);
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("Failed to decrypt");
}
}
}
EDIT 3:
Cleaned up Java code for readability
We are using phpseclib for Public key signing of data and android java is used for Public key verification. But it repeatedtly failed.
PHP Code For generating keys and signing by private key
include_once("phpseclib/autoload.php");
function getKeys($keysize=2048){
$rsa = new Crypt_RSA();
//$rsa->setPublicKeyFormat(CRYPT_RSA_PUBLIC_FORMAT_OPENSSH);
//$rsa->setPublicKeyFormat(CRYPT_RSA_PRIVATE_FORMAT_PKCS1);
$rsa->setPrivateKeyFormat(CRYPT_RSA_PRIVATE_FORMAT_PKCS8);
$rsa->setPublicKeyFormat(CRYPT_RSA_PUBLIC_FORMAT_PKCS1);
$d = $rsa->createKey($keysize);
return array("publickey"=>$d['publickey'], "privatekey"=>$d['privatekey']);
}
function encryptdata($message, $encryptionKey){
$rsa = new Crypt_RSA();
//$rsa->setPublicKeyFormat(CRYPT_RSA_PRIVATE_FORMAT_PKCS1);
$rsa->setPrivateKeyFormat(CRYPT_RSA_PRIVATE_FORMAT_PKCS8);
$rsa->setPublicKeyFormat(CRYPT_RSA_PUBLIC_FORMAT_PKCS1);
//$rsa->setPublicKeyFormat(CRYPT_RSA_PUBLIC_FORMAT_OPENSSH);
$rsa->loadKey($encryptionKey); // public key
return $rsa->encrypt($message);
}
function decryptdata($message, $decryptionKey){
$rsa = new Crypt_RSA();
// $rsa->setPublicKeyFormat(CRYPT_RSA_PUBLIC_FORMAT_OPENSSH);
// $rsa->setPublicKeyFormat(CRYPT_RSA_PRIVATE_FORMAT_PKCS1);
$rsa->setPrivateKeyFormat(CRYPT_RSA_PRIVATE_FORMAT_PKCS8);
$rsa->setPublicKeyFormat(CRYPT_RSA_PUBLIC_FORMAT_PKCS1);
$rsa->loadKey($decryptionKey); // private key
return $rsa->decrypt($message);
}
$keys = getKeys();
file_put_contents("key.pub", $keys["publickey"]);
file_put_contents("key.priv", $keys["privatekey"]);
$publickey = file_get_contents("key.pub");
$privatekey = file_get_contents("key.priv");
//print_r($keys);
$string = "Hi I m here";
$hash = hash("sha256", $string);
$encdata = encryptdata($hash, $privatekey);
echo $base_encdata = base64_encode($encdata);
JAVA Code
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.io.UnsupportedEncodingException;
import org.apache.commons.codec.binary.Base64;
import java.security.spec.X509EncodedKeySpec;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.KeyFactory;
import java.security.Signature;
import java.security.PublicKey;
import java.security.SignatureException;
import java.security.spec.InvalidKeySpecException;
import java.lang.String;
class PubCheck {
public static boolean verify(String message, String signature, PublicKey publicKey) throws SignatureException{
try {
Signature sign = Signature.getInstance("SHA1withRSA");
sign.initVerify(publicKey);
sign.update(message.getBytes("UTF-8"));
return sign.verify(Base64.decodeBase64(signature.getBytes("UTF-8")));
} catch (Exception ex) {
throw new SignatureException(ex);
}
}
public static void main(String[] args)
throws NoSuchAlgorithmException, UnsupportedEncodingException, InvalidKeySpecException, SignatureException
{
String plainData = "Hi I m here";
String pkey = "MIIBCgKCAQEA2tF2g/muNw9xKTVcIkjUMvMhygtIW49yo1PgbwqDQ/w9MSfEARtYYF6Tenfz0twaR/eI14GXmlIffflORe4eaSuMBhwQFOIKU/1+v1BV3RLqGGblvHTVaMVm49AGiqxNnh1LBbcSrC5UhMqlL/HGiku0oYsbjLzwcLc5ac6aBQVD60wWGNm1g26lRQGRbCLqxVfcWKT3AMvEQK3cEx/En7/5Vg1V8xnJraNMrO8UGnaX8LLJFzYJiSCEShh7F+pMHbf4MaBekw7Aaf5hPJtczNsR137R92Be3OP4idI5NLmTV+Pi1DWlxhjEhswKH88SP+gsW31gS7B/ddECUqewQwIDAQAB";
String data = "aP0nuYYA1hE5odsCkR/DcdRbBvO2Z8IOlqXf/bKZJiG8HELIop90Vno1dKC1qyHEAOXy0gtH7GtJamzoBjDZmHPT6eto9EZP/xE7xZ8L05kjp0z2thLqO7on4C6DrG++TK1j+E3T7V0UeU874WIB0AEVzu1XUKFW6aeuU67a/gdn8N2n7N/WXtlyNSVZXg8f4PeUhGvFJrhINZT7BuMMZj1gZs4wMJPAICwfvVeg02RPH0N3Ybf2iVgRuZlmtQXGTyBlCxe9ybdHzuQM6nXghpLNmaOzCypb+yVs3Da7E0b3/fKQ7JqPSquWex2ERZbIMSTC6oCzc1rOF6iKVAd92Q==";
byte[] encodedPublicKey = pkey.getBytes( "utf-8" );
//System.out.println(new String(encodedPublicKey, "UTF-8") + "\n");
X509EncodedKeySpec publicKeySpec = new X509EncodedKeySpec( encodedPublicKey );
//PKCS8EncodedKeySpec publicKeySpec = new PKCS8EncodedKeySpec(encodedPublicKey);
KeyFactory keyFactory = KeyFactory.getInstance( "RSA" );
PublicKey publicKey = keyFactory.generatePublic( publicKeySpec );
boolean retvar = verify(plainData, data, publicKey);
// 3 - verifying content with signature and content :
/*Signature sig = Signature.getInstance( "SHA256withRSA" );
sig.initVerify( publicKey );
sig.update( data.getBytes( ) );
ret = sig.verify( sign.getBytes( ) );*/
//byte[] decoded = Base64.decodeBase64(data);
}
}
I compiled java code by
javac -cp commons-codec-1.10.jar:. PubCheck.java
java -cp commons-codec-1.10.jar:. PubCheck
Then found following exception
Exception in thread "main" java.security.spec.InvalidKeySpecException: java.security.InvalidKeyException: invalid key format
at sun.security.rsa.RSAKeyFactory.engineGeneratePublic(RSAKeyFactory.java:205)
at java.security.KeyFactory.generatePublic(KeyFactory.java:334)
at PubCheck.main(PubCheck.java:67)
Caused by: java.security.InvalidKeyException: invalid key format
at sun.security.x509.X509Key.decode(X509Key.java:387)
at sun.security.x509.X509Key.decode(X509Key.java:403)
at sun.security.rsa.RSAPublicKeyImpl.<init>(RSAPublicKeyImpl.java:83)
at sun.security.rsa.RSAKeyFactory.generatePublic(RSAKeyFactory.java:298)
at sun.security.rsa.RSAKeyFactory.engineGeneratePublic(RSAKeyFactory.java:201)
... 2 more
Disclaimer : I have zero knowledge about java. all the code I try found from net.
UPDATE : Issue finally solved and java code able to verify by help from Maarten Bodewes. Code he provided works with one change I need to pass PKCS1 from phpseclib So I changed
Signature sig = Signature.getInstance( "SHA256withRSAandMGF1");
to
Signature sig = Signature.getInstance( "SHA256withRSA");
PHP Code need changes for using sign instead of manually encrypt/hashing.
function getKeys($keysize=2048){
$rsa = new Crypt_RSA();
$rsa->setPrivateKeyFormat(CRYPT_RSA_PRIVATE_FORMAT_PKCS8);
$rsa->setPublicKeyFormat(CRYPT_RSA_PUBLIC_FORMAT_PKCS1);
$d = $rsa->createKey($keysize);
return array("publickey"=>$d['publickey'], "privatekey"=>$d['privatekey']);
}
$string = "Hi I m here";
/*
$keys = getKeys();
file_put_contents("key1.pub", $keys["publickey"]);
file_put_contents("key1.priv", $keys["privatekey"]);
die;*/
$publickey = file_get_contents("key1.pub");
$privatekey = file_get_contents("key1.priv");
$hash = new Crypt_Hash('sha256');
$rsa = new Crypt_RSA();
$rsa->loadKey($privatekey);
$rsa->setSignatureMode(CRYPT_RSA_ENCRYPTION_PKCS1);
$rsa->setHash('sha256');
$signature = $rsa->sign($string);
echo base64_encode($signature);
PKCS#1 keys are almost but not completely the same as X.509 keys.
The following snippet will create a Java JCA compliant public key. It will then try and perform the (default) OAEP decryption.
package nl.owlstead.stackoverflow;
import static java.nio.charset.StandardCharsets.UTF_8;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.Security;
import java.security.Signature;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.RSAPublicKeySpec;
import java.util.Base64;
import java.util.Base64.Decoder;
import javax.crypto.Cipher;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
public class PKCS1PublicKey {
public static RSAPublicKey fromPKCS1Encoding(byte[] pkcs1EncodedPublicKey) {
// --- parse public key ---
org.bouncycastle.asn1.pkcs.RSAPublicKey pkcs1PublicKey;
try {
pkcs1PublicKey = org.bouncycastle.asn1.pkcs.RSAPublicKey
.getInstance(pkcs1EncodedPublicKey);
} catch (Exception e) {
throw new IllegalArgumentException(
"Could not parse BER PKCS#1 public key structure", e);
}
// --- convert to JCE RSAPublicKey
RSAPublicKeySpec spec = new RSAPublicKeySpec(
pkcs1PublicKey.getModulus(), pkcs1PublicKey.getPublicExponent());
KeyFactory rsaKeyFact;
try {
rsaKeyFact = KeyFactory.getInstance("RSA");
} catch (NoSuchAlgorithmException e) {
throw new IllegalStateException("RSA KeyFactory should be available", e);
}
try {
return (RSAPublicKey) rsaKeyFact.generatePublic(spec);
} catch (InvalidKeySpecException e) {
throw new IllegalArgumentException(
"Invalid RSA public key, modulus and/or exponent invalid", e);
}
}
public static void main(String[] args) throws Exception {
Security.addProvider(new BouncyCastleProvider());
String pkey = "MIIBCgKCAQEA2tF2g/muNw9xKTVcIkjUMvMhygtIW49yo1PgbwqDQ/w9MSfEARtYYF6Tenfz0twaR/eI14GXmlIffflORe4eaSuMBhwQFOIKU/1+v1BV3RLqGGblvHTVaMVm49AGiqxNnh1LBbcSrC5UhMqlL/HGiku0oYsbjLzwcLc5ac6aBQVD60wWGNm1g26lRQGRbCLqxVfcWKT3AMvEQK3cEx/En7/5Vg1V8xnJraNMrO8UGnaX8LLJFzYJiSCEShh7F+pMHbf4MaBekw7Aaf5hPJtczNsR137R92Be3OP4idI5NLmTV+Pi1DWlxhjEhswKH88SP+gsW31gS7B/ddECUqewQwIDAQAB";
Decoder decoder = Base64.getDecoder();
byte[] dpkey = decoder.decode(pkey);
RSAPublicKey publicKey = fromPKCS1Encoding(dpkey);
String plainData = "Hi I m here";
String data = "aP0nuYYA1hE5odsCkR/DcdRbBvO2Z8IOlqXf/bKZJiG8HELIop90Vno1dKC1qyHEAOXy0gtH7GtJamzoBjDZmHPT6eto9EZP/xE7xZ8L05kjp0z2thLqO7on4C6DrG++TK1j+E3T7V0UeU874WIB0AEVzu1XUKFW6aeuU67a/gdn8N2n7N/WXtlyNSVZXg8f4PeUhGvFJrhINZT7BuMMZj1gZs4wMJPAICwfvVeg02RPH0N3Ybf2iVgRuZlmtQXGTyBlCxe9ybdHzuQM6nXghpLNmaOzCypb+yVs3Da7E0b3/fKQ7JqPSquWex2ERZbIMSTC6oCzc1rOF6iKVAd92Q==";
byte[] ciphertext = decoder.decode(data);
// this will fail of course if the "signature" was generated using OAEP - use PSS signatures instead (see comments below)
verifyBC(publicKey, plainData, ciphertext);
System.out.flush();
decryptBC(publicKey, plainData, ciphertext);
System.out.flush();
decryptSun(publicKey, plainData, ciphertext);
System.out.flush();
}
private static void decryptBC(RSAPublicKey publicKey, String plainData,
byte[] ciphertext) throws Exception {
Cipher oaep = Cipher.getInstance("RSA/ECB/OAEPWithSHA1AndMGF1Padding", "BC");
// this *should* fail
oaep.init(Cipher.DECRYPT_MODE, publicKey);
byte[] plaintext = oaep.doFinal(ciphertext);
System.out.println(new String(plaintext, UTF_8));
}
private static void decryptSun(RSAPublicKey publicKey, String plainData,
byte[] ciphertext) throws Exception {
Cipher oaep = Cipher.getInstance("RSA/ECB/OAEPWithSHA1AndMGF1Padding", "SunJCE");
// this fails beautifully
oaep.init(Cipher.DECRYPT_MODE, publicKey);
byte[] plaintext = oaep.doFinal(ciphertext);
System.out.println(new String(plaintext, UTF_8));
}
private static void verifyBC(RSAPublicKey publicKey, String plainData,
byte[] ciphertext) throws Exception {
// what should work (for PKCS#1 v1.5 signatures), requires Bouncy Castle provider
Signature sig = Signature.getInstance( "SHA256withRSAandMGF1");
sig.initVerify(publicKey);
sig.update(plainData.getBytes(UTF_8));
System.out.println(sig.verify(ciphertext));
}
}
The SunJCE implementation of OAEP will fail because it will not accept the public key for signature verification:
OAEP cannot be used to sign or verify signatures
Now that has to be one of the most clear and informative exceptions I've met in a cryptography API. You can also use the Bouncy Castle provider and this one will "decrypt" the hash value. That's however not how OAEP should be used, you should be using PSS to verify signatures.
You should be using the PHP RSA sign method instead, using setHash to setup SHA-256.
Although Martin's answer works there's another way to get rid of the InvalidKeySpecException exception.
In your original code pkey is a PKCS1 formatted RSA private key. It needs to be a PKCS8 formatted private key to work with X509EncodedKeySpec (which corresponds to an X509 cert's SubjectPublicKeyInfo). It also needs to be base64 decoded.
So in your PHP code you wouldn't do $rsa->setPublicKeyFormat(CRYPT_RSA_PUBLIC_FORMAT_PKCS1) - you'd do $rsa->setPublicKeyFormat(CRYPT_RSA_PUBLIC_FORMAT_PKCS8).
I converted your PKCS1 key to PKCS8 myself and got this:
String pkey = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2tF2g/muNw9xKTVcIkjU" +
"MvMhygtIW49yo1PgbwqDQ/w9MSfEARtYYF6Tenfz0twaR/eI14GXmlIffflORe4e" +
"aSuMBhwQFOIKU/1+v1BV3RLqGGblvHTVaMVm49AGiqxNnh1LBbcSrC5UhMqlL/HG" +
"iku0oYsbjLzwcLc5ac6aBQVD60wWGNm1g26lRQGRbCLqxVfcWKT3AMvEQK3cEx/E" +
"n7/5Vg1V8xnJraNMrO8UGnaX8LLJFzYJiSCEShh7F+pMHbf4MaBekw7Aaf5hPJtc" +
"zNsR137R92Be3OP4idI5NLmTV+Pi1DWlxhjEhswKH88SP+gsW31gS7B/ddECUqew" +
"QwIDAQAB";
byte[] encodedPublicKey = Base64.decodeBase64(pkey);
You'd, of course, need to remove your existing pkey and encodedPublicKey assignments.
Also, you could do return $d instead of return array("publickey"=>$d['publickey'], "privatekey"=>$d['privatekey']) in your PHP code..
I saw some same questions in stack-overflow but it doesn't help me.
I have this php code
$signature=base64_encode(hash_hmac("sha256", trim($xmlReq), $signature_key, True));
I want to write java equivalent to that and this is my java code.
public static String encodeXML(String key, String data) {
String result = "";
try {
Mac mac = Mac.getInstance("HmacSHA256");
SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes("UTF-8"), "HmacSHA256");
mac.init(secretKeySpec);
result = Base64.encodeBase64String(mac.doFinal(data.getBytes("UTF-8")));
} catch (NoSuchAlgorithmException | InvalidKeyException | UnsupportedEncodingException e) {
log.error("exception occured when encording HmacSHA256 hash");
}
return result;
}
but they give different results.
someone help.
Apache Commons Codec
import org.apache.commons.codec.binary.Base64;
....
Base64.encodeBase64String(.....);
PHP Test Code:
$signature=base64_encode(hash_hmac("sha256", 'Message', 'secret', true));
echo $signature;
Java Test Code:
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.util.Base64;
public class TestJava {
public static void main(String[] args) {
try {
String secret = "secret";
String message = "Message";
Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
SecretKeySpec secret_key = new SecretKeySpec(secret.getBytes(), "HmacSHA256");
sha256_HMAC.init(secret_key);
Base64.Encoder encoder = Base64.getEncoder();
String hash = encoder.encodeToString(sha256_HMAC.doFinal(message.getBytes()));
System.out.println(hash);
} catch (Exception e){
System.out.println("Error");
}
}
}
Output for both should be:
qnR8UCqJggD55PohusaBNviGoOJ67HC6Btry4qXLVZc=
On the server side, the encyption/decryption of the password field is done in C#.
Now, i need to implement same functionality in my android application. So, i followed this tutorial: http://ttux.net/post/3des-java-encrypter-des-java-encryption/ as below:
import java.security.MessageDigest;
import java.security.spec.KeySpec;
import java.util.Arrays;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESedeKeySpec;
import javax.crypto.spec.IvParameterSpec;
import org.apache.commons.codec.binary.Base64;
public class Encrypter {
private KeySpec keySpec;
private SecretKey key;
private IvParameterSpec iv;
public Encrypter(String keyString, String ivString) {
try {
final MessageDigest md = MessageDigest.getInstance("md5");
final byte[] digestOfPassword = md.digest(Base64.decodeBase64(keyString.getBytes("utf-8")));
final byte[] keyBytes = Arrays.copyOf(digestOfPassword, 24);
for (int j = 0, k = 16; j < 8;) {
keyBytes[k++] = keyBytes[j++];
}
keySpec = new DESedeKeySpec(keyBytes);
key = SecretKeyFactory.getInstance("DESede").generateSecret(keySpec);
iv = new IvParameterSpec(ivString.getBytes());
} catch(Exception e) {
e.printStackTrace();
}
}
public String encrypt(String value) {
try {
Cipher ecipher = Cipher.getInstance("DESede/CBC/PKCS5Padding","SunJCE");
ecipher.init(Cipher.ENCRYPT_MODE, key, iv);
if(value==null)
return null;
// Encode the string into bytes using utf-8
byte[] utf8 = value.getBytes("UTF8");
// Encrypt
byte[] enc = ecipher.doFinal(utf8);
// Encode bytes to base64 to get a string
return new String(Base64.encodeBase64(enc),"UTF-8");
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public String decrypt(String value) {
try {
Cipher dcipher = Cipher.getInstance("DESede/CBC/PKCS5Padding","SunJCE");
dcipher.init(Cipher.DECRYPT_MODE, key, iv);
if(value==null)
return null;
// Decode base64 to get bytes
byte[] dec = Base64.decodeBase64(value.getBytes());
// Decrypt
byte[] utf8 = dcipher.doFinal(dec);
// Decode using utf-8
return new String(utf8, "UTF8");
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
but i dont know what values i need to provide for KeyValue and ivValue for the above code. Please help me...
Use this code to encrypt your string
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import android.util.Base64;
//string encryption
public class EncryptionHelper {
// Encrypts string and encode in Base64
public static String encryptText(String plainText) throws Exception {
// ---- Use specified 3DES key and IV from other source --------------
byte[] plaintext = plainText.getBytes();//input
byte[] tdesKeyData = Constants.getKey().getBytes();// your encryption key
byte[] myIV = Constants.getInitializationVector().getBytes();// initialization vector
Cipher c3des = Cipher.getInstance("DESede/CBC/PKCS5Padding");
SecretKeySpec myKey = new SecretKeySpec(tdesKeyData, "DESede");
IvParameterSpec ivspec = new IvParameterSpec(myIV);
c3des.init(Cipher.ENCRYPT_MODE, myKey, ivspec);
byte[] cipherText = c3des.doFinal(plaintext);
String encryptedString = Base64.encodeToString(cipherText,
Base64.DEFAULT);
// return Base64Coder.encodeString(new String(cipherText));
return encryptedString;
}
}
This is how you can encrypt the string
String encryptedPassword = EncryptionHelper.encryptText(edtText.getText().toString());
EDIT
Code for Constants.java
Class Constants {
private final String initializationVector = "INITALIZATION_VECTOR";
private final String ecnryptionKey = "ENCRYPTION_KEY";
public static String getInitializationVector() {
return initializationVector;
}
public static String getKey() {
return ecnryptionKey;
}
}
Triple DES is called "DESede" (DES using single DES Encrypt, Decrypt, Encrypt for encryption) in both Java and Android runtimes. So it is build in functionality which can be access through the Cipher class. It also lists the available algorithms. For triple DES you could use "DESede/CBC/PKCS5Padding"`. Don't forget to supply it a random IV of 8 bytes.
Triple DES should only be used for backwards compatibility. If you decide to use it at least supply it 24 bytes of key material, otherwise there is a chance that your ciphertext can be cracked. For a more modern approach use AES, preferably in an authenticated mode such as GCM ("AES/GCM/NoPadding"). Note that GCM requires a unique nonce of 12 bytes.