Hello i'm new to Java and i have the following problem: i'm trying to encrypt the password of a user using the blowfish algorithm, but when i try to decrypt it back to check the authentication it fails to decrypt it for some reason.
public static String encryptBlowFish(String to_encrypt, String salt){
String dbpassword = null;
try{
SecretKeySpec skeySpec = new SecretKeySpec( salt.getBytes(), "Blowfish" );
// Instantiate the cipher.
Cipher cipher = Cipher.getInstance("Blowfish/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
//byte[] encrypted = cipher.doFinal( URLEncoder.encode(data).getBytes() );
byte[] encrypted = cipher.doFinal( to_encrypt.getBytes() );
dbpassword = new String(encrypted);
} catch (Exception e) {
System.out.println("Exception while encrypting");
e.printStackTrace();
dbpassword = null;
} finally {
return dbpassword;
}
}
public static String decryptBlowFish(String to_decrypt, String salt){
String dbpassword = null;
try{
SecretKeySpec skeySpec = new SecretKeySpec( salt.getBytes(), "Blowfish" );
// Instantiate the cipher.
Cipher cipher = Cipher.getInstance("Blowfish/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, skeySpec);
//byte[] encrypted = cipher.doFinal( URLEncoder.encode(data).getBytes() );
byte[] encrypted = cipher.doFinal( to_decrypt.getBytes() );
dbpassword = new String(encrypted);
} catch (Exception e) {
System.out.println("Exception while decrypting");
e.printStackTrace();
dbpassword = null;
} finally {
return dbpassword;
}
}
When i call the decrypt function it gives me the following error:
java.security.InvalidKeyException: Parameters missing
Any ideas? Thank you
you are doing multiple things wrong here:
you are converting an encrypted value to a string. not all bytes are valid strings. store bytes directly in a database as a binary blob, not as a string (or convert it to hex or base64 first).
you are confusing salt and key. the thing you are calling salt in your code is actually a private key. you don't appear to have a real salt, at all.
you are encrypting a password. that means you need to store a key somewhere (you cannot store it in the database or anyone that steals the database will be able to decrypt the passwords). instead, you should use a hash.
even then, you should not store passwords in this way. not even if you use a salt correctly. these days it is too easy to crack simply hashed passwords, even when salted. instead, use the bcrypt library or PBKDF2.
here are instructions for doing this correctly. note that if you follow those instructions it is ok to store the password as a string (it has been converted correctly for you).
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 recently used the AES CBC 128 algorithm in Java in order to cipher data. Now I need to rebuild that algorithm in PHP, but I have no idea how, because PHP algorithms on the internet return different results. Maybe you can help me.
This is the Java-code to encrypt:
private SecretKeySpec secretKey;
private IvParameterSpec ivSpec;
public void setKey(String myKey) {
MessageDigest sha = null;
try {
byte[] key = myKey.getBytes("UTF-8");
sha = MessageDigest.getInstance("SHA-1");
key = sha.digest(key);
key = Arrays.copyOf(key, 16);
secretKey = new SecretKeySpec(key, "AES");
byte[] iv = new String("1010101010101010").getBytes("UTF-8");
ivSpec = new IvParameterSpec(iv);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
public String encrypt(String strToEncrypt) {
try {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secretKey, ivSpec);
return Base64.encode(cipher.doFinal(strToEncrypt.getBytes("UTF-8")));
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public String decrypt(String strToDecrypt) {
try {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, secretKey, ivSpec);
return new String(cipher.doFinal(Base64.decode(strToDecrypt)));
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public static void main(String[] args) {
AESText aes = new AESText();
final String secretKey = "com.secure.test.projectjasdS/FjkGkGhkGjhG786Vjfg=tjGFGH";
aes.setKey(secretKey);
String originalString = "test set se ts et set s et se";
String encryptedString = aes.encrypt(originalString);
String decryptedString = aes.decrypt(encryptedString);
System.out.println("origin: " + originalString);
System.out.println("encrypted: " + encryptedString);
System.out.println("decrypted: " + decryptedString);
}
This is my php code:
protected $key;
protected $method = 'AES-128-CBC';
protected $iv = '1010101010101010';
protected $option = OPENSSL_CIPHER_AES_128_CBC;
function __construct($key)
{
$this->key = $key;
}
public function encrypt($data) {
if (is_null($data)) {
return "Error " . INVALID_PARAMS_ENCRYPTIONS . ": Data is null ";
}
$enc = openssl_encrypt($data, $this->method, $this->key, $this->option, $this->iv);
return base64_encode($enc);
}
public function decrypt($data) {
if (is_null($data)) {
return "Error " . INVALID_PARAMS_ENCRYPTIONS . ": Data is null ";
}
$data = base64_decode($data);
$dec = openssl_decrypt($data, $this->method, $this->key, $this->option, $this->iv);
return $dec;
}
When I encrypted data from java encryption, This result cannot decrypt on Php decryption.
Can you guys possibly help me with building a PHP script, that returns the same results with java encryption?
At first glance I see three issues here:
First: you are not using the same mode: in java you have AES/ECB/PKCS5Padding whereas your php uses AES-128-CBC.
Second: you probably aren't using the same IV's in the Java and PHP code (IV's are irrelevant for ECB, but once you switch your java to CBC you will need it):
You have $iv = '1010101010101010' (which is then passed to openssl) in your php but nothing like that in your java.
At the very least, you will probably need something like that in your Java part as well:
cipher.init(Cipher.DECRYPT_MODE/ENCRYPT_MODE, secretKey, new IvParameterSpec(iv))
with iv being a byte[] containing your IV bytes.
Third: once the problems above are addressed, padding may be the next breaking thing: your java cipher specification mentions PKCS5Padding. You need to make sure that both of your counterparts use the same.
edit: Fourth: One more issue is the way you derive the key bits to be used. In java you take the first 16 bytes of a sha1-hash, and in php you just pass $key to openssl. openssl might be deriving the encryption key in a different way.
When building cryptography-related tools using block ciphers, it's always nice to revisit classics like Block cipher mode of operation and Padding on Wikipedia, to get a sense of what is going on under the hood.
I'm trying to create a simple encryption/decryption program, but i'm having problems when decrypting. The way the program works, I get string input from the user, then encrypt using DES, convert to Base64 and give the user the converted secret key. However, when I get the secret key from the user and I try to decrypt, I get either error:
java.security.InvalidKeyException: No installed provider supports this key: (null)
or
javax.crypto.BadPaddingException
I don't know if the fault is at the time of encryption or if it's the decryption. Here's the respective snippets of code:
import javax.xml.bind.DatatypeConverter;
public static boolean encrypt(byte[] text){
Boolean yorn = false;
try{
myDesKey = KeyGenerator.getInstance("DES").generateKey();
//myDeskKey = myDesKey.toString();
Cipher desCipher = Cipher.getInstance("DES/CBC/PKCS5Padding");
desCipher.init(Cipher.ENCRYPT_MODE, myDesKey);
//I felt it would be better seeing the secret key as "woatDnBJLAg=" instead of "com.sun.crypto.provider.DESKey#18765"
if (myDesKey != null) {
stringKey = DatatypeConverter.printBase64Binary(myDesKey.getEncoded());
System.out.println("actual secret_key:" + myDesKey);
byte[] encodedKey = DatatypeConverter.parseBase64Binary(stringKey);
myDesKey = new SecretKeySpec(encodedKey, 0, encodedKey.length,
"DES");
System.out.println("after encode & decode secret_key:"
+ DatatypeConverter.printBase64Binary(myDesKey.getEncoded()));
}
textEncrypted = desCipher.doFinal(text);
yorn = true;
JTextArea textArea = new JTextArea(2,50);
textArea.setText("Your encryption key is: " + stringKey + " . Ensure you store it in a safe place" );// + DatatypeConverter.printBase64Binary(myDesKey.getEncoded()));
textArea.setEditable(false);
JOptionPane.showMessageDialog(null, new JScrollPane(textArea), "RESULT", JOptionPane.INFORMATION_MESSAGE);
}catch(Exception e)
{
System.out.println("There has been an error encrypting the file");
yorn = false;
}
return yorn;
Decryption
public static String decrypt(byte[] cipherText, SecretKey key,String key1)
{
String plainText = "";
try{
SecretKey myDesKey = key;
if(key == null){
JOptionPane.showMessageDialog(null, "We were unable to find your decryption key. Please enter your decryption key below: ");
JTextArea textBox = new JTextArea(1,15);
JOptionPane.showMessageDialog(null, new JScrollPane(textBox),"Enter your decryption key ",JOptionPane.PLAIN_MESSAGE);
//myDesKey = textBox.toSecretKey;
}
Cipher desCipher;
desCipher = Cipher.getInstance("DES");
desCipher.init(Cipher.DECRYPT_MODE, myDesKey);
byte[] textDecrypted = desCipher.doFinal(cipherText);
plainText = new String(textDecrypted);
JOptionPane.showMessageDialog(null, plainText, "DECRYPTED MESSAGE", 0);
}catch(Exception e)
{
System.out.println("There has been an error decrypting the file");
System.out.println(e);
}return plainText;
}
}
I know that I'm probably getting the errors because i've combined so many jumbled bits of code from all over stack and I seem to have lost the plot, but any help will be greatly appreciated.
Thanks!!
You're using two different ciphers. Cipher.getInstance("DES") is not fully specified, so it probably defaults to Cipher.getInstance("DES/ECB/PKCS5Padding") which is different from the CBC mode you're using during encryption.
Secondly, since you're using CBC mode, you need to manage the initialization vector (IV), but you don't do this at all. You can either provide the IV as a third parameter for Cipher#init or let it automatically be generated as you currently do, but then you have to send the IV (desCipher.getIV()) along with the ciphertext to the receiver. The IV doesn't have to be secret. A common way is to prepend it to the ciphertext and slice it off before decryption.
Lastly, the key you're using during decryption is not the same key that you've used during encryption, because you've re-encoded the key after encryption, but didn't decode it back.
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 !");
}
}