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).
Related
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)
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 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);
I got an exception in the following code for AES algorithm in java.
Code decryptes an encrypted string and returns the original string.
Plz help me to fix this.
Code:
public class AES
{
public byte[] encrypted;
public byte[] original;
public String originalString;
public static String asHex (byte buf[])
{
StringBuffer strbuf = new StringBuffer(buf.length * 2);
int i; for (i = 0; i < buf.length; i++)
{
if (((int) buf[i] & 0xff) < 0x10) strbuf.append("0");
strbuf.append(Long.toString((int) buf[i] & 0xff, 16));
}
return strbuf.toString();
}
public String AESencryptalgo(byte[] text)
{
String newtext="";
// Get the KeyGenerator
try
{
KeyGenerator kgen = KeyGenerator.getInstance("AES");
kgen.init(128); // 192 and 256 bits may not be available
// Generate the secret key specs.
SecretKey skey = kgen.generateKey();
byte[] raw = skey.getEncoded();
SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
// Instantiate the cipher Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec); encrypted = cipher.doFinal(text);
System.out.println("encrypted string: " + asHex(encrypted));
cipher.init(Cipher.DECRYPT_MODE, skeySpec); original = cipher.doFinal(encrypted);
originalString = new String(original); System.out.println("Original string: " + originalString + " " + asHex(original));
}
catch(Exception e)
{ }
finally
{
newtext=new String(encrypted);
System.out.println("ENCRYPTED "+newtext);
//AESdecryptalgo(newtext.getBytes());
return newtext;
}
}
public String AESdecryptalgo(byte[] text)
{
// Get the KeyGenerator
try
{
KeyGenerator kgen = KeyGenerator.getInstance("AES");
kgen.init(128); // 192 and 256 bits may not be available
// Generate the secret key specs.
SecretKey skey = kgen.generateKey();
byte[] raw = skey.getEncoded();
SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
// Instantiate the cipher
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.DECRYPT_MODE, skeySpec);
original = cipher.doFinal(text); //Exception occurs here
originalString = new String(original);
System.out.println("Original string: " + originalString + " " + asHex(original));
}
catch(Exception e)
{
System.out.println("exception");
}
finally
{
System.out.println("DECRYPTED "+originalString);
return originalString;
}
}
public static void main(String[] args)
{
AES a=new AES();
a.AESencryptalgo("hello".getBytes());
System.out.println();
}}
`
exception:
javax.crypto.BadPaddingException: Given final block not properly padded at
com.sun.crypto.provider.SunJCE_f.b(DashoA13*..) at
com.sun.crypto.provider.SunJCE_f.b(DashoA13*..) at
com.sun.crypto.provider.AESCipher.engineDoFinal(DashoA13*..) at
javax.crypto.Cipher.doFinal(DashoA13*..)
According to Java™ Cryptography Architecture (JCA) Reference Guide (emphasis mine):
Cipher objects are obtained by using
one of the Cipher getInstance() static
factory methods. Here, the algorithm
name is slightly different than with
other engine classes, in that it
specifies not just an algorithm name,
but a "transformation". A
transformation is a string that
describes the operation (or set of
operations) to be performed on the
given input to produce some output. A
transformation always includes the
name of a cryptographic algorithm
(e.g., DES), and may be followed by a
mode and padding scheme.
A transformation is of the form:
"algorithm/mode/padding" or
"algorithm"
For example, the following are valid transformations:
"DES/CBC/PKCS5Padding"
"DES"
If just a transformation name is specified, the system will
determine if there is an
implementation of the requested
transformation available in the
environment, and if there is more than
one, returns there is a preferred one.
If both a transformation name and a
package provider are specified, the
system will determine if there is an
implementation of the requested
transformation in the package
requested, and throw an exception if
there is not.
If no mode or padding is specified,
provider-specific default values for
the mode and padding scheme are used.
For example, the SunJCE provider uses
ECB as the default mode, and
PKCS5Padding as the default padding
scheme for DES, DES-EDE and Blowfish
ciphers. This means that in the case
of the SunJCE provider:
Cipher c1 = Cipher.getInstance("DES/ECB/PKCS5Padding");
and
Cipher c1 = Cipher.getInstance("DES");
are equivalent statements.
Using modes
such as CFB and OFB, block ciphers can
encrypt data in units smaller than the
cipher's actual block size. When
requesting such a mode, you may
optionally specify the number of bits
to be processed at a time by appending
this number to the mode name as shown
in the "DES/CFB8/NoPadding" and
"DES/OFB32/PKCS5Padding"
transformations. If no such number is
specified, a provider-specific default
is used. (For example, the SunJCE
provider uses a default of 64 bits for
DES.) Thus, block ciphers can be
turned into byte-oriented stream
ciphers by using an 8 bit mode such as
CFB8 or OFB8.
Appendix A of this document contains a
list of standard names that can be
used to specify the algorithm name,
mode, and padding scheme components of
a transformation.
The objects returned by factory
methods are uninitialized, and must be
initialized before they become usable.
Because your code does not specify mode or padding, provider-specific default values are being used. It appears that your provider is SunJCE and that it's default padding is probably "NoPadding". With this padding, you are responsible for ensuring that the size of the byte array being encrypted is a multiple of the number of bytes in the secret key. You can make you're life easier by specifying the mode and padding in your transformation:
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
WARNING: You should not use ECB mode in real code. Try CBC instead.
Update: I didn't think it was fair to recommend CBC mode without offering a little sample of how it works:
public static void main(String... args) throws Exception {
byte[] data = "hello".getBytes();
KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
keyGenerator.init(128); // 192 and 256 bits may not be available
SecretKey secretKey = keyGenerator.generateKey();
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
// By initializing the cipher in CBC mode, an "initialization vector" has been randomly
// generated. This initialization vector will be necessary to decrypt the encrypted data.
// It is safe to store the initialization vector in plain text for later use. You can obtain
// it's bytes by calling iv.getIV().
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
IvParameterSpec iv = cipher.getParameters().getParameterSpec(IvParameterSpec.class);
byte[] encryptedData = cipher.doFinal(data);
// When decrypting the encrypted data, you must provide the initialization vector used
// during the encryption phase.
cipher.init(Cipher.DECRYPT_MODE, secretKey, iv);
byte[] decryptedData = cipher.doFinal(encryptedData);
if (!Arrays.equals(data, decryptedData)) {
throw new Exception("Data was not decrypted successfully");
}
}
Well if this is the error Input length must be multiple of 16 when decrypting with padded cipher. Than the answear is obvious, the length of your buffer must be a multiple of 16.
Have you checked the length of buf[]?
Your code manages to get almost everything wrong. Just for a start, your mistakes include:
generating a new random symmetric key before encryption and decryption. You have to use the same key to decrypt that was used to encrypt.
Using String as a container for binary data. cipher output is cannot be reliably made into a String unless you use an encoding, such as base64.
Your exception handling is incorrect.
Furthermore, your code runs without exceptions for me.