public static String decrypt(byte[] text, PrivateKey key) {
byte[] decryptedText = null;
try {
final Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, key);
decryptedText = cipher.doFinal(text);
} catch (Exception e) {
e.printStackTrace();
}
return new String(decryptedText);
}
There is my code, for some reason I get this error and I think it is something to do with using the default constructor of the Cipher class.
Why am I getting this error?
In order to lead to reasonable results, you often don't want to encrypt an arbitrary number of bytes.
Instead: you enlarge the number of bytes to encrypt using padding.
See here on the "why to pad?"
Related
I am currently making an Android app that includes encrypting a String with AES. But for some reason my app does not decrypt properly. I tried to change the Base64 format but it does not fix it. The code is similar to the example on Android Encryption with the Android Cryptography API
Does anyone know where did I go wrong with my functions? Since it does not decode to the same string as my encoded string ("pls").
Your help is much appreciated.
byte[] a = encryptFIN128AES("pls");
String b = decryptFIN128AES(a);
Log.e("AES_Test", "b = " + b);
/**
* Encrypts a string with AES (128 bit key)
* #param fin
* #return the AES encrypted byte[]
*/
private byte[] encryptFIN128AES(String fin) {
SecretKeySpec sks = null;
try {
sks = new SecretKeySpec(generateKey("Test1".toCharArray(), "Test2".getBytes()).getEncoded(),"AES");
} catch (Exception e) {
Log.e("encryptFIN128AES", "AES key generation error");
}
// Encode the original data with AES
byte[] encodedBytes = null;
try {
Cipher c = Cipher.getInstance("AES");
c.init(Cipher.ENCRYPT_MODE, sks);
encodedBytes = c.doFinal(fin.getBytes());
} catch (Exception e) {
Log.e("encryptFIN128AES", "AES encryption error");
}
return encodedBytes;
}
/**
* Decrypts a string with AES (128 bit key)
* #param encodedBytes
* #return the decrypted String
*/
private String decryptFIN128AES(byte[] encodedBytes) {
SecretKeySpec sks = null;
try {
sks = new SecretKeySpec(generateKey("Test1".toCharArray(), "Test2".getBytes()).getEncoded(),"AES");
} catch (Exception e) {
Log.e("decryptFIN128AES", "AES key generation error");
}
// Decode the encoded data with AES
byte[] decodedBytes = null;
try {
Cipher c = Cipher.getInstance("AES");
c.init(Cipher.DECRYPT_MODE, sks);
decodedBytes = c.doFinal(encodedBytes);
} catch (Exception e) {
Log.e("decryptFIN128AES", "AES decryption error");
}
return Base64.encodeToString(decodedBytes, Base64.DEFAULT);
}
public static SecretKey generateKey(char[] passphraseOrPin, byte[] salt)
throws NoSuchAlgorithmException, InvalidKeySpecException {
final int iterations = 1000;
// Generate a 256-bit key
final int outputKeyLength = 128;
SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
KeySpec keySpec = new PBEKeySpec(passphraseOrPin, salt, iterations, outputKeyLength);
SecretKey secretKey = secretKeyFactory.generateSecret(keySpec);
return secretKey;
}
Output:
E/AES_Test: b = cGxz
**
[EDIT] Modified my code but now there is a NullPointerException
**
/**
* Encrypts a string with AES (128 bit key)
* #param fin
* #return the AES encrypted string
*/
private byte[] encryptFIN128AES(String fin) {
SecretKeySpec sks = null;
try {
sks = new SecretKeySpec(generateKey(PASSPHRASE, SALT.getBytes(StandardCharsets.UTF_8)).getEncoded(), "AES");
} catch (Exception e) {
Log.e("encryptFIN128AES", "AES key generation error");
}
// Encode the original data with AES
byte[] encodedBytes = null;
try {
Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding");
c.init(Cipher.ENCRYPT_MODE, sks);
encodedBytes = c.doFinal(fin.getBytes(StandardCharsets.UTF_8));
} catch (Exception e) {
Log.e("encryptFIN128AES", "AES encryption error");
}
return encodedBytes;
}
/**
* Decrypts a string with AES (128 bit key)
* #param encodedBytes
* #return the decrypted String
*/
private String decryptFIN128AES(byte[] encodedBytes) {
SecretKeySpec sks = null;
try {
sks = new SecretKeySpec(generateKey(PASSPHRASE, SALT.getBytes(StandardCharsets.UTF_8)).getEncoded(), "AES");
} catch (Exception e) {
Log.e("decryptFIN128AES", "AES key generation error");
}
// Decode the encoded data with AES
byte[] decodedBytes = null;
try {
Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding");
c.init(Cipher.DECRYPT_MODE, sks);
decodedBytes = c.doFinal(encodedBytes);
} catch (Exception e) {
Log.e("decryptFIN128AES", "AES decryption error");
}
//return Base64.encodeToString(decodedBytes, Base64.DEFAULT);
return new String(decodedBytes, StandardCharsets.UTF_8);
}
// generateKey(char[] passphraseOrPin, byte[] salt) remains the same
Error:
E/decryptFIN128AES: AES decryption error
E/AndroidRuntime: FATAL EXCEPTION: Thread-176
Process: testapp.ttyi.nfcapp, PID: 2920
java.lang.NullPointerException: Attempt to get length of null array
at java.lang.String.<init>(String.java:371)
at testapp.ttyi.nfcapp.DisplayQRActivity.decryptFIN128AES(DisplayQRActivity.java:254)
at testapp.ttyi.nfcapp.DisplayQRActivity.access$100(DisplayQRActivity.java:29)
at testapp.ttyi.nfcapp.DisplayQRActivity$1.run(DisplayQRActivity.java:77)
at java.lang.Thread.run(Thread.java:818)
**
[EDIT2] Resolved (But no Padding/Encryption Mode allowed)
**
I managed to resolve the issue. (Decodes to "pls") using Codo's solution ofreturn new String(decodedBytes, StandardCharsets.UTF_8);
Though it only works when the algorithm used is:
Cipher c = Cipher.getInstance("AES");
When I put Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding");
The "NullPointerException" as seen above will happen. My observation shows that during decryption:
try {
Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding");
c.init(Cipher.DECRYPT_MODE, sks);
decodedBytes = c.doFinal(encodedBytes);
} catch (Exception e) {
Log.e("decryptFIN128AES", "AES decryption error");
}
something will fail and it will always print out:
E/decryptFIN128AES: AES decryption error
And thus the NullPointerException will occur as decodedBytes is always initiated to NULL.
Your process is not balanced. For encryption you do:
Encode string using default charset (fin.getBytes()) to get binary data
Encrypt binary data to get encrypted data (doFinal)
For the decryption, you do:
Decrypt encrypted data to get unencrypted binary data (doFinal)
Encode binary data as a Base64 string
Instead of Base64 encoding, the last step should be the reverse of step 1 in the encryption, i.e. you should decode the binary data into a string:
return String(decodedBytes);
It strongly recommend, you do not use the default charset for encoding and decoding as it depends on the system's setting. So it could be different between the system where you encrypt and decyrpt.
So use:
fin.getBytes(StandardCharsets.UTF_8);
and:
return String(decodedBytes, StandardCharsets.UTF_8);
The same applies for the salt.
Also note that you should specify the padding and chaining mode. If you don't, provider-specific default values apply. See #Ryan's answer for more details.
You should research more on how to use AES correctly as you are missing some basic fundamentals of AES security: no IV (assuming using CBC), no mode specified (such as CBC), and no padding specified (such as PKCS5).
Looks like char encoding issue. With minor modifications it works.
in encryptFIN128AES:
encodedBytes = c.doFinal(Base64.getEncoder().encode(fin.getBytes()));
in decryptFIN128AES:
return new String(Base64.getDecoder().decode(decodedBytes));
I have done Encryption with ,
public static String encrypt(String plainText) {
try {
byte[] keyData = secret_key.getBytes();
SecretKeySpec secretKey = new SecretKeySpec(keyData, "AES/ECB/PKCS7Padding");
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS7Padding");
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
byte[] cipherText = cipher.doFinal(plainText.getBytes("UTF-8"));
String encryptedString = Base64.encodeToString(cipherText, Base64.NO_WRAP);
return encryptedString;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
it's working well.
but part of Decryption gives Error like,
W/System.err: javax.crypto.BadPaddingException: pad block corrupted
W/System.err: at com.android.org.bouncycastle.jce.provider.JCEBlockCipher.engineDoFinal(JCEBlockCipher.java:701)
W/System.err: at javax.crypto.Cipher.doFinal(Cipher.java:1111)
decrypt Code like,
public static String decrypt(String encryptedText) {
try {
byte[] keyData = secret_key.getBytes();
SecretKeySpec secretKey = new SecretKeySpec(keyData, "AES/ECB/PKCS7Padding");
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS7Padding");
cipher.init(Cipher.DECRYPT_MODE, secretKey);
byte[] cipherText = Base64.decode(encryptedText,Base64.NO_WRAP);
String decryptedString = new String(cipher.doFinal(cipherText),"UTF-8");
return decryptedString;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
here what is the problem? How can i solve this Issue?
It is likely that your secret_key value contains bytes which are not well represented in the ambiguous encoding you're using. When you call String#getBytes() without specifying an encoding, you get the system default, which can vary.
You should use hexadecimal encoding whenever you represent your key as a String. This will be consistent across serialization/deserialization on every platform. There are many standard implementations of this encoding/decoding process available (i.e. org.bouncycastle.util.encoders.Hex.decode("0123456789ABCDEFFEDCBA9876543210"); or org.apache.commons.codec.binary.Hex.decodeHex("0123456789ABCDEFFEDCBA9876543210".toCharArray()); which both return the raw byte[]).
Some side notes:
You are using ECB mode of operation, which is extremely susceptible to frequency analysis for cryptanalysis and is effectively deprecated aside from toy crypto demonstrations. I suggest you use CBC, CTR, or GCM.
You do not provide an initialization vector (IV), so the same message encrypted with the same key will always yield identical cipher text. Use a unique and non-predictable IV for every encryption operation by generating 16 bytes from SecureRandom and populating it into an IvParameterSpec. You can prepend the IV bytes to the cipher text and transport/store it in the clear.
Your cipher text is not authenticated, allowing for malicious users to both manipulate encrypted data and to attempt decryption via padding oracle/CCA attacks. Use an authenticated encryption with associated data (AEAD) mode like GCM, or use an HMAC/SHA-256 message authentication code (MAC) over the cipher text, and verify it using a constant-time equals method before attempting any decryption.
You do not need to provide the mode of operation or padding scheme when instantiating a key. SecretKey key = new SecretKeySpec(keyData, "AES"); is sufficient.
I have a private key file encripted with DES/ECB/PKCS5Padding (56 bit DES key generated by a secret phrase) and I want to decrypt it.
I don't know why, but everytime I try to decript, the method doFinal of my cipher class is throwing this error:
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.DESCipher.engineDoFinal(DashoA13*..) at
javax.crypto.Cipher.doFinal(DashoA13*..) at...
Here is my code:
public static PrivateKey readPrivateKeyFromFile(File file, String chaveSecreta) {
try {
SecureRandom r = new SecureRandom(chaveSecreta.getBytes());
KeyGenerator keyGen = KeyGenerator.getInstance("DES");
keyGen.init(56, r);
Key key = keyGen.generateKey();
byte[] privateKeyBytes = decryptPKFile(file, key);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec(privateKeyBytes);
PrivateKey privateKey = null;
try {
privateKey = keyFactory.generatePrivate(privateKeySpec);
} catch (InvalidKeySpecException e) {
JOptionPane.showMessageDialog(null, "Erro 01, tente mais tarde");
}
return privateKey;
} catch (NoSuchAlgorithmException e) {
JOptionPane.showMessageDialog(null, "Erro 02, tente mais tarde");
}
return null;
}
public static byte[] decryptPKFile(File file, Key key){
try{
Cipher cipher = Cipher.getInstance("DES/ECB/PKCS5Padding");
byte[] cipherText = readBytes(file);
cipher.init(Cipher.DECRYPT_MODE, key);
System.out.println(cipher);
System.out.println(cipherText);
byte[] text = cipher.doFinal(cipherText);
return text;
}catch(Exception e){
e.printStackTrace();
return null;
}
}
public static byte[] readBytes(File file) {
try {
FileInputStream fs = new FileInputStream(file);
byte content[] = new byte[(int) file.length()];
fs.read(content);
return content;
} catch (FileNotFoundException e) {
System.out.println("Arquivo não encontrado!");
e.printStackTrace();
} catch (IOException ioe) {
System.out.println("Erro ao ler arquivo!");
ioe.printStackTrace();
}
return null;
}
Any syggestions?
You're trying to decrypt ciphertext with a random number generator created using a specific seed. However, you don't specify the algorithm, and the algorithm may change internally as well. Android is even known to generate a fully random value instead for some versions.
You need to use a SecretKeyFactory not a KeyGenerator. And you will of course need the 8-byte key data. The only way to retrieve this in your case is to find the SecureRandom algorithm/implementation before and re-calculate the key.
Now any ciphertext will decrypt with any key. DES ECB only provides (some sort of) confidentiality, not integrity. The problem is that it will decrypt into garbage. Now if you try to remove the padding from garbage you will likely get a padding error.
If you're "lucky" - once in about 256 times - you will get a result. This happens when the decrypted block ends with 01 or 0202, that's valid padding. The result will - of course - be garbage as well, but it will not end with a BadPaddingException. In your case the SecureRandom instance is likely to return the same incorrect value over and over though, so this may never happen.
In the future, please use PBKDF2 and feed it the encoded password. Clearly note the character encoding used, Java SE uses the lowest 8 bits of the char array. Never ever use String.getBytes() as the default encoding may differ between systems.
In this code, this line is causing an exception:
clearText = c.doFinal(Base64.decode(encryptedText, Base64.DEFAULT));
javax.crypto.BadPaddingException: pad block corrupted
I got the code from:
http://www.techrepublic.com/blog/software-engineer/attention-android-developers-keep-user-data-safe/
Any ideas?
private String decrypt (String encryptedText) {
byte[] clearText = null;
try {
SecretKeySpec ks = new SecretKeySpec(getKey(), "AES");
Cipher c = Cipher.getInstance("AES");
c.init(Cipher.DECRYPT_MODE, ks);
clearText = c.doFinal(Base64.decode(encryptedText, Base64.DEFAULT));
return new String(clearText, "UTF-8");
} catch (Exception e) {
return null;
}
}
Details: I am encrypting it on the android as well
owlstead's advice was helpful, but for this case when using the code in
Attention Android developers: Keep user data safe
http://www.techrepublic.com/blog/software-engineer/attention-android-developers-keep-user-data-safe/
I made some changes to the code that might be helpful for other people in the future. I completely deleted the getkey method.
private static String seed;
/**
* Encrypts the text.
* #param clearText The text you want to encrypt
* #return Encrypted data if successful, or null if unsucessful
*/
protected String encrypt(String clearText) {
byte[] encryptedText = null;
try {
byte[] keyData = seed.getBytes();
SecretKey ks = new SecretKeySpec(keyData, "AES");
Cipher c = Cipher.getInstance("AES");
c.init(Cipher.ENCRYPT_MODE, ks);
encryptedText = c.doFinal(clearText.getBytes("UTF-8"));
return Base64.encodeToString(encryptedText, Base64.DEFAULT);
} catch (Exception e) {
return null;
}
}
/**
* Decrypts the text
* #param encryptedText The text you want to encrypt
* #return Decrypted data if successful, or null if unsucessful
*/
protected String decrypt (String encryptedText) {
byte[] clearText = null;
try {
byte[] keyData = seed.getBytes();
SecretKey ks = new SecretKeySpec(keyData, "AES");
Cipher c = Cipher.getInstance("AES");
c.init(Cipher.DECRYPT_MODE, ks);
clearText = c.doFinal(Base64.decode(encryptedText, Base64.DEFAULT));
return new String(clearText, "UTF-8");
} catch (Exception e) {
return null;
}
}
Java + Android + Encryption + Exception means just one thing normally, somebody is using the SecureRandom class again as a key derivation function. This fails when the SecureRandom implementation of "SHA1PRNG" does not behave as the one in Sun's implementation in Java SE. Especially if the seed is added to the state of the random number generator instead of the seed being used as a starting point of the PRNG.
Basically, simply use SecretKey aesKey = new SecretKeySpec(byte[] keyData, "AES") instead, or - if you start off with a password - try and generate the key using PBKDF2.
For me, the problem is in getKey()
Make sure that two invocation of getKey() return the same value.
I used new SecureRandom(password.getBytes()) to generate key. It worked on Windows, but on Android, it returned different value for different call.
I Reffred From this : https://androidfreetutorial.wordpress.com/2017/03/14/android-encryptiondecryption-with-aes-algorithm/
Change to "AES" From "AES/ECB/PKCS7Padding";
First of all I wanna say thank you...
I wrote a program which one is doing encryption and decryption with Enum.
Enum has AES,BlowFish,DESede. My program will support these 3 encryption algorithm.
Then I wanted to Generate a SecretKey with SecretKeyFactory.But I think,I made a mistake to generate a key. (Obviously I loose myself in code.I have no idea about what can I do...)
My Code is below. This program's purpose is;
Users will write encryption and decryption method parameters. (Text,Encryption Algorithm)
Algorithm type will choose in Enum type. (Enum has 3 algorithm format)
According to the entered Encryption Type,program will encrypt entered text.
I know my code is really terrible. It has lots of unnecessary declaration and logical mistakes.
Code is working fine sometimes,sometimes will crash.
EDIT = Question is my code doesnt work always. Sometimes gives error. Error is = javax.crypto.BadPaddingException: Given final block not properly padded
Thank you for answering.
import java.security.SecureRandom;
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.SecretKeySpec;
public class SymetricAlgorithms {
private static enum algorithms { //Enum declaration 3 encryption types here
AES, BlowFish, DESede;
}
private static String data = "HOWCANISOLVETHIS"; //this will be encrypt and decrypt
public static void main(String[] args) throws Throwable {
SecretKey kgen = GenerateKey(); // Create a key.
String encrypText = encrypt(kgen, data, algorithms.AES); //encrypt method calling here.
String decrypText = dencypt(kgen, encrypText, algorithms.AES);//decrypt method calling here.
System.out.println("plaintext = " + data + " key = " + kgen
+ "\nEncryptedText = " + encrypText
+ "\nDecryptedText = " + decrypText);
}
public static String dencypt(SecretKey inKey, String text, algorithms eValue)throws Throwable {//decryption
try {
byte[] text2 = text.getBytes(); //convert from parameters TEXT to Bytes
Cipher cipher = Cipher.getInstance("AES"); //Cipher initialize and choose encryption method (AES)
cipher.init(Cipher.DECRYPT_MODE, inKey); //cipher process
byte plainTextByte[] = new byte[20]; //Creating byte array
plainTextByte =cipher.doFinal(text2);//using byte array to assign ciphers result
System.out.println(plainTextByte);
return new String(plainTextByte);
} catch (Exception e) {
System.err.println("Data Cant Decrypted !");
e.printStackTrace();
}
return null;
}
public static String encrypt(SecretKey inKey, String text, algorithms eValue)
throws Throwable {
try {
Cipher cipher = null; //cipher declaration
switch (eValue) {//Enum. 3 types here and control structure for Users choosing encryption type is acceptable
case AES:cipher = Cipher.getInstance("AES");
break;
case BlowFish:Cipher cipher2 = Cipher.getInstance("BlowFish");
cipher = cipher2;
break;
case DESede:Cipher cipher3 = Cipher.getInstance("DESede");
cipher=cipher3;
break;
default:
System.out.println("Unexpectable value input.");
break;
}
System.out.println(inKey);
//Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, inKey);
byte[] ciphertext = cipher.doFinal(text.getBytes("UTF-8"));//cipher result is assign to byte array
System.out.println(ciphertext);
return new String(ciphertext);
} catch (Exception e) {
System.err.println("Unexpectable algorithm type !");
e.printStackTrace();
}
return null;
}
public static SecretKey GenerateKey() throws Throwable {//Generate a key for using crypt
//could sb explain these? =D I loose myself. I combined codes from finding internet...Failed...
try {
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
SecureRandom prng = SecureRandom.getInstance("SHA1PRNG");
byte bytes[] = new byte[20];
prng.nextBytes(bytes);
String passwordTemp = prng.toString();
String saltTemp = passwordTemp;
char[] password = passwordTemp.toCharArray();
byte[] salt = saltTemp.getBytes();
KeySpec spec = new PBEKeySpec(password, salt, 65536, 128);
SecretKey tmp = factory.generateSecret(spec);
SecretKey secret = new SecretKeySpec(tmp.getEncoded(), "AES");
return secret;
} catch (Exception e) {
System.err.println("Key cant be generated !");
e.printStackTrace();
}
return null;
}
}
The theme of the problem is misunderstanding of the relationship between Strings and bytes. At the end of the encrypt method, what do you think these two lines do:
byte[] ciphertext = cipher.doFinal(...
return new String(ciphertext);
The last line takes the encrypted bytes, which could be almost anything, and attempts to interpret those bytes as encoding some characters of a string. Using what encoding? String constructor with no character encoding argument uses system default encoding, which depends on JVM/OS/Locale. Lets say it is UTF-8. Are you guaranteed that there will actually be some character for the encrypted bytes? Answer: NO. Will you get the same bytes back, when you take the resulting string and call .getBytes("UTF-8"). Answer: No, there are mutliple byte sequences encoding the same characters, thus new String(bytes, "UTF-8").getBytes("UTF-8") is not guaranteed to return the bytes you started with.
In summary, don't attempt to interpret arbitrary bytes as a string. Make your encrypt method return byte[], and your decryp method take an array of bytes to decode-- then it will work.
It is not necessary to make your program work, but if you must represent the encrypted bytes as a string, consider base64 encoding, or hexadecimal encoding -- these encodings uniquely map every possible byte (or sequence of bytes) to a string.
UPDATE: here is a more concise generateKey() method. It allows you to pass the password in as an argument.
public static SecretKey generateKey(String password) {
try {
SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG");
byte saltBytes[] = new byte[20];
secureRandom.nextBytes(saltBytes);
KeySpec spec = new PBEKeySpec(password.toCharArray(), saltBytes, 65536, 128);
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
SecretKey secretKey = factory.generateSecret(spec);
return new SecretKeySpec(secretKey.getEncoded(), "AES");
} catch (Exception e) {
throw new IllegalStateException("Key cant be generated !");
}
}