I've been trying to do AES_GCM encryption in Python and decryption in Java, below is the relevant code snippet for what I'm trying to accomplish, I have also checked the question at: Pycrypto AES GCM encryption and Java decryption this is similar to my problem. I am suspecting that the IV/nonce I set for Python is incorrect, any help would be greatly appreciated.
Python Encryption Code:
from Crypto.Cipher import AES
from base64 import b64encode
someKey = 'Sixteen byte key'
cipher = AES.new(someKey, AES.MODE_GCM, nonce='0000000000000000', mac_len=16)
ciphertext, tag = cipher.encrypt_and_digest(data)
ciphertext = ciphertext + tag
print b64encode(ciphertext)
Java Decryption Code:
private static byte[] initializationVector = new byte[16];
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
SecretKeySpec keySpec = new SecretKeySpec(someKey, "AES");
GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(128, initializationVector);
cipher.init(Cipher.DECRYPT_MODE, keySpec, gcmParameterSpec);
byte[] bytesString = Base64.getDecoder().decode(theString);
return new String(cipher.doFinal(bytesString), "UTF-8");
I am however unable to decrypt this in Java, I get the following error:
javax.crypto.AEADBadTagException: Tag mismatch!
at java.base/com.sun.crypto.provider.GaloisCounterMode.decryptFinal(GaloisCounterMode.java:623)
at java.base/com.sun.crypto.provider.CipherCore.finalNoPadding(CipherCore.java:1116)
at java.base/com.sun.crypto.provider.CipherCore.fillOutputBuffer(CipherCore.java:1053)
at java.base/com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:853)
at java.base/com.sun.crypto.provider.AESCipher.engineDoFinal(AESCipher.java:446)
at java.base/javax.crypto.Cipher.doFinal(Cipher.java:2202)
Related
I'm trying to use Java class Cipher to encrypt/decrypt some of my data. But I'm confused why the same result is produced in encrypt and decrypt mode. Below is my code:
KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA");
KeyPair keyPair = generator.genKeyPair();
PrivateKey privateKey = keyPair.getPrivate();
byte[] data = "12345".getBytes();
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, privateKey);
byte[] encrypted = cipher.doFinal(data);
Cipher cipher2 = Cipher.getInstance("RSA");
cipher2.init(Cipher.DECRYPT_MODE, privateKey);
byte[] decrypted = cipher2.doFinal(data);
Here the encrypted has exactly same content with decrypted. It really confused me why same result is produced regardless of what mode I'm using.
What I have tried?
If I use Cipher.getInstance("DES") to decrypt "12345".getBytes(), exception will be thrown.
Does it mean class Cipher can automatically detect whether encrypt or decrypt should be used(ignore mode parameter that I've specified when initialize the Cipher class) when using RSA?
Really appreciate for your help.
===Update===
My bad, looks Java has different behavior with Android. I should limit my question to Android platform only. If I run the above code as a normal Java application, javax.crypto.BadPaddingException: Decryption error will be thrown when decrypting "123456".
===Update===
First of all RSA is asymetric thus you use public key to encrypt and private key decrypt.
Second, those do not produce the same output.
KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA");
KeyPair keyPair = generator.genKeyPair();
PrivateKey privateKey = keyPair.getPrivate();
byte[] data = "12345".getBytes();
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, keyPair.getPublic());
byte[] encrypted = cipher.doFinal(data);
System.out.println(new String(encrypted));
Cipher cipher2 = Cipher.getInstance("RSA");
cipher2.init(Cipher.DECRYPT_MODE, privateKey);
byte[] decrypted = cipher2.doFinal(encrypted);
System.out.println(new String(decrypted));
with the output of
D=1��� 12345
Third, there is no autodetection, but cipher can detect that decrypt material is corrupted or invalid - wrong pading in your case causing the error.
Trying to decrypt data as byte array encrypted with AES-128 with String key "keykeykeykeykey1"
code:
byte[] dataBytes = new byte []{(byte)0xf3,(byte)0x8b,(byte)0x0c,(byte)0xb3,(byte)0xa3,(byte)0x26,(byte)0x12,(byte)0x23,(byte)0xe0,(byte)0xe0,(byte)0x9f,(byte)0x1f,(byte)0x28,(byte)0x01,(byte)0x28,(byte)0x35};
SecretKeySpec secretKeySpec = new SecretKeySpec("keykeykeykeykey1".getBytes("UTF-8"), "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec);
byte[] decryptedData = cipher.doFinal(dataBytes); //this line throws exception
gives me BadPaddingException. What I missed?
You don't specify the mode or padding in your Cipher algorithm. You need to establish what values were used when the data was enciphered.
When you change the algo to "AES/ECB/NOPADDING" there is no error, but this might not necessarily be the right mode or padding.
I did a simple encrypt/decrypt string class in java and tested it. It workes fine, now im trying to use it in an android device to encrypt a string, send it to my server and there decrypt it. All using the same custom class. Why is this not working? Is it simply not supported? what else can i do to easily encrypt/decrypt a string for this purpose? Base64 is not accepted:)
package crypto;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
public class Crypto {
public static byte[] encrypt(String message) throws Exception
{
String symmetricKey = "25Ae1f1711%z1 )1";
SecretKeySpec aesKey = new SecretKeySpec(symmetricKey.getBytes(), "AES");
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding", "SunJCE");
cipher.init(Cipher.ENCRYPT_MODE, key);
return cipher.doFinal(message.getBytes());
}
public static String decrypt(byte[] encryptedMessage) throws Exception
{
String symmetricKey = "25Ae1f1711%z1 )1";
SecretKeySpec aesKey = new SecretKeySpec(symmetricKey.getBytes(), "AES");
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding", "SunJCE");
cipher.init(Cipher.DECRYPT_MODE, key);
return new String(cipher.doFinal(encryptedMessage));
}
}
In logcat i can see the following exception pop up: java.security.NoSuchProviderException: Provider not avalible: SunJCE
I deleted the specified provider "SunJCE" from the line.
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding", "SunJCE");
i found the solution to this over here
now i get this error at the serverside instead:
javax.crypto.IllegalBlockSizeException: Input length must be multiple of 16 when decrypting with padded cipher
at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:750)
at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:676)
at com.sun.crypto.provider.AESCipher.engineDoFinal(AESCipher.java:317)
at javax.crypto.Cipher.doFinal(Cipher.java:1813)
at crypto.Crypto.decrypt(Crypto.java:20)
at io.network.UDPServer.run(UDPServer.java:37
tried with BC but i still have the same error
Try this code:
Call encrypt method like this: encrypt_text = Encrypt(Text,"avs3qt");
Encrypt function definition:
String Encrypt(String text, String key) throws Exception {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
byte[] keyBytes = new byte[16];
byte[] b = key.getBytes("UTF-8");
int len = b.length;
if (len > keyBytes.length)
len = keyBytes.length;
System.arraycopy(b, 0, keyBytes, 0, len);
SecretKeySpec keySpec = new SecretKeySpec(keyBytes, "AES");
IvParameterSpec ivSpec = new IvParameterSpec(keyBytes);
cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec);
byte[] results = cipher.doFinal(text.getBytes("UTF-8"));
BASE64Decoder encoder = new BASE64Decoder();
return encoder.encodeBytes(results);
}
call Decrypt method like this:
decrypt_text = Decrypt(Text, "avs3qt");
Function definition:
String Decrypt(String text, String key) throws Exception {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
byte[] keyBytes = new byte[16];
byte[] b = key.getBytes("UTF-8");
int len = b.length;
if (len > keyBytes.length)
len = keyBytes.length;
System.arraycopy(b, 0, keyBytes, 0, len);
SecretKeySpec keySpec = new SecretKeySpec(keyBytes, "AES");
IvParameterSpec ivSpec = new IvParameterSpec(keyBytes);
cipher.init(Cipher.DECRYPT_MODE, keySpec,ivSpec);
BASE64Decoder decoder = new BASE64Decoder();
byte[] results = cipher.doFinal(decoder.decode(text));
return new String(results, "UTF-8");
}
It depends on the security provider. On Android and in JVM are different defaults and not all Sun/Oracle Algorithm exist on android, too.
I had nearly the same problem and was solving it by switching to bounce castle (BC). It exist on both sides and are working in exactly the same metter.
I'm using "RSA/ECB/PKCS1Padding" as Transformation on both sides. And its working.
Your code won't run on any Java VM except Oracle's. The reason is that you request a specific cryptography provider called "SunJCE", which is the reference implementation of the JCE API from Oracle (previously Sun).
Just change your code to accept any provider capable of handling the requested algorithm:
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
Yo Brother,
I have just made an App on android which uses encryption
Have a look at this following question
Java 256-bit AES Password-Based Encryption
Check out the reply given by wufoo (the one having 14 pts)
Just omit use of import org.apache.commons.codec.binary.Hex; and its methods (As the method used in by Hex class are from new version of the apache api and android have built in apache of older version, Plus in android native api is given preference over external library/api )
and download plus reference the org.apache.common from this link http://commons.apache.org/proper/commons-codec/
I hope it helps
Devel
I have Perl code that decrypts a String and I want to do the same in Java. This is the Perl code:
my $c = Crypt::CBC->new( -key => $keyString, -cipher => 'Blowfish', -header => 'randomiv');
return $c->decrypt_hex($config->{encrypted_password})
This is my attempt at the Java code:
Cipher cipher = Cipher.getInstance("Blowfish/CBC/PKCS5Padding");
// setup an IV (initialization vector) that should be
// randomly generated for each input that's encrypted
byte[] iv = new byte[cipher.getBlockSize()];
new SecureRandom().nextBytes(iv);
IvParameterSpec ivSpec = new IvParameterSpec(iv);
// decrypt
SecretKey secretKey = new SecretKeySpec(Base64.decodeBase64(keyString), "Blowfish");
cipher.init(Cipher.DECRYPT_MODE, secretKey, ivSpec);
byte[] decrypted = cipher.doFinal(Base64.decodeBase64(input));
return Hex.encodeHexString(decrypted);
I'm getting:javax.crypto.BadPaddingException: Given final block not properly padded. But according to this, the Crypt CBC library uses PKCS5 as the default padding.
Also, am I doing the hex encoding at the end right?
One of the problems you have is that you generate a random IV instead of importing the one used for encryption. Do you have access to the IV used at encryption? Could it be at the start of the ciphertext?
I don't do Perl, so I'm not quite sure if my response is valid. Base64 is probably not the right decoding you're looking for.
For creating your SecretKeySpec, try doing something like:
SecretKey secretKey = new SecretKeySpec(keyString.getBytes("ASCII"), "Blowfish");
For decoding the text, check out Hex.decodeHex(char[]) which can be found at http://commons.apache.org/codec/apidocs/org/apache/commons/codec/binary/Hex.html ... so your code might look something like this:
byte[] decrypted = cipher.doFinal(Hex.decodeHex(input.toCharArray()));
String unencryptedStuff = new String(decrypted);
My objective is to encrypt data in Iphone and decrypt it on java server.
I am using Symmetric encryption .
I have generated the key using KeyGenerator at the java side.
code for generating key is as follows:
//Java Code for key generation
File keyFile = new File("F:/key","mykey.key");
KeyGenerator kgen = KeyGenerator.getInstance("AES");
kgen.init(128);
SecretKey skey = kgen.generateKey();
byte[] enc= skey.getEncoded();
FileUtils.writeStringToFile(keyFile ,Base64.encodeBase64String(enc),"UTF-8");
Following is the java code for decryption:
//get key from file
File file = new File("F:/key", "mykey.key");
SecretKeySpec keySpec= null;
try {
byte[] keyBytes = Base64.decodeBase64(FileUtils.readFileToString(file,"UTF-8"));
keySpec= new SecretKeySpec(keyBytes, 0, 16, "AES");
byte[] raw = keySpec.getEncoded();
} catch (Exception e) {
e.printStackTrace();
}
//Decrypt String encryptedString(from Iphone)
byte[] tempByte = Base64.decodeBase64(encryptedString);
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.DECRYPT_MODE, keySpec);
byte[] cipherData = cipher.doFinal(tempByte);
String ttt = new String(cipherData ,"UTF-8");
System.out.println(ttt);
And the iphone code is similar to th code given in following link:
Encrypting data with Objective-C and decrypt it with Java Problem
I am getting the following exception while decrypting in java.
javax.crypto.BadPaddingException: Given final block not properly padded
Please help...
Well the padding and mode has to match. If you copied the Objective-C code, then the ciphertext on the Objective-C side has the ECB mode and the PKCS7 padding.
By default the java AES cipher has the CBC mode and PKCS5 padding (though I'm not sure, and AFAIK PKCS5 and PKCS7 are somewhat compatible). I guess you have to specify these explicitly. Those settings have to match otherwise something goes wrong. So you have to create the cipher like that:
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
Btw. if you can choose the encryption-mode you should use CBC (but then on both sides).