I'm trying to write a simple program to encrypt and decrypt files using the AES algortihm. I haven't problems with encryption, but decryption..
public static void main(String[] args) throws NoSuchAlgorithmException, FileNotFoundException, InvalidKeyException, NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException {
// Инициализация секретных ключей
KeyGenerator keyGenS = KeyGenerator.getInstance("AES");
keyGenS.init(128);
SecretKey sKey1 = keyGenS.generateKey();
SecretKey sKey2 = keyGenS.generateKey();
// Перевод секретных ключей в строку и запись в файл
String key1 = SecretKeyToString(sKey1);
String key2 = SecretKeyToString(sKey2);
spreader.write(fileName1, key1);
spreader.write(fileName2, key2);
spreader.write(fileNameS1, key1);
spreader.write(fileNameS2, key2);
// Чтение секретных ключей из файла и перевод обратно в тип SecretKey
key1 = spreader.read(fileName1);
System.out.println("Секретный ключ 1го пользователя: " +key1);
SecretKey seansKey1=getKeyInstance(key1);
key2 = spreader.read(fileName2);
System.out.println("Секретный ключ 2го пользователя: " +key2);
SecretKey seansKey2=getKeyInstance(key2);
//инициализация и зашифрование сеансового ключа с помощью секретных
Cipher aesCipher = Cipher.getInstance("AES");
aesCipher.init(Cipher.ENCRYPT_MODE,seansKey1);
KeyGenerator keyGen = KeyGenerator.getInstance("AES");
keyGen.init(128);
SecretKey secretKey = keyGen.generateKey();
String stringsecretKey = SecretKeyToString(secretKey);
byte[] byteKey = stringsecretKey.getBytes();
byte[] byteCipherKey1 = aesCipher.doFinal(byteKey);
String encryptedKey = new BASE64Encoder().encode(byteCipherKey1);
System.out.println("Зашифрованный сеансовый ключ с помощью секретного ключа 1: " +encryptedKey);
aesCipher = Cipher.getInstance("AES");
aesCipher.init(Cipher.ENCRYPT_MODE,SeansKey2);
byteKey = etringsecretKey.getBytes();
byte[] byteCipherKey2 = aesCipher.doFinal(byteKey);
encryptedKey = new BASE64Encoder().encode(byteCipherKey2);
System.out.println("Зашифрованный сеансовый ключ с помощью секретного ключа 2: " +encryptedKey);
spreader.write(fileNameEK2, encryptedKey);
//Чтение данных из файла
String text =spreader.read(fileName);
System.out.println(text);
// Зашифрование данных
aesCipher.init(Cipher.ENCRYPT_MODE,secretKey); // константная переменная
byte[] byteText = text.getBytes();
byte[] byteCipherText = aesCipher.doFinal(byteText);
encryptedText = new BASE64Encoder().encode(byteCipherText);
System.out.println("Зашифрованный текст: " +encryptedText);
spreader.write(fileNameOK, encryptedText);
}
Here's the decryption part:
public static void main(String[] args) throws NoSuchAlgorithmException, FileNotFoundException, InvalidKeyException, NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException, InvalidAlgorithmParameterException, UnsupportedEncodingException {
String encryptedText = user.read(fileNameOK);
String key1 = user.read(fileName1);
String key2 = user.read(fileName2);
String encryptedSeanceKey1 = user.read(fileNameEK1);
String encryptedSeanceKey2 = user.read(fileNameEK2);
SecretKey secretKey1=getKeyInstance(key1);
SecretKey secretKey2=getKeyInstance(key2);
Cipher aesCipher = Cipher.getInstance("AES");
aesCipher.init(Cipher.DECRYPT_MODE,secretKey1,aesCipher.getParameters());
//byte[] byteKey = encryptedSeanceKey1.getBytes();
byte[] byteDecryptedKey = aesCipher.doFinal(encryptedSeanceKey1.getBytes());
String decryptedKey1 = new String(byteDecryptedKey);
System.out.println("Расшифрованный сеансовый ключ с помощью секретного ключа 1: " +decryptedKey1);
aesCipher.init(Cipher.DECRYPT_MODE,secretKey2,aesCipher.getParameters());
byte[] byteKey2 = encryptedSeanceKey2.getBytes();
byteDecryptedKey = aesCipher.doFinal(byteKey2);
String decryptedKey2 = new String(byteDecryptedKey);
System.out.println("Расшифрованный сеансовый ключ с помощью секретного ключа 2: " +decryptedKey2);
// Расшифрование данных
aesCipher.init(Cipher.DECRYPT_MODE,getKeyInstance(decryptedKey1),aesCipher.getParameters());
byte[] byteText = encryptedText.getBytes();
byte[] byteDecryptedText = aesCipher.doFinal(byteText);
decryptedText = new String(byteDecryptedText);
System.out.println(" Расшифрованный текст " +decryptedText);
}
}
Now the problem is the decryption part is: Input length must be multiple of 16 when decrypting with padded cipher
I know that a mistake that I incorrectly keep a session key and bytes are lost. But how I can correctly do it?
There is a little bit of confusion in your code, maybe because some method you called are missing, or maybe because you are using your keys to encrypt...your keys(!!!)
Let's try to encrypt and decrypt the easy way, removing all the stuff that is not strictly needed in your code (like encode your key and save it to a file, and then restore the key without decoding it, etc..).
Let's take a look at the following simplified code based on your:
KeyGenerator keyGenS = KeyGenerator.getInstance("AES");
keyGenS.init(128);
SecretKey sKey1 = keyGenS.generateKey();
Cipher aesCipher = Cipher.getInstance("AES");
aesCipher.init(Cipher.ENCRYPT_MODE,sKey1);
byte[] byteText = "Insert here whatever you want to crypt".getBytes();
byte[] byteCipherText = aesCipher.doFinal(byteText);
We have generated our key with KeyGenerator, and then we have initialized our Cipher instance with that key. At this point we simply call doFinal() passing the plainText we want to encrypt.
That's all for the encryption part. Of course you can save your keys and byteCipherText to a file if you want, but all the other staff is (at least) useless.
The decryption part is easy as the encryption. The logic is the same.
If you saved your key on a file, just read it into a byte[], and use it for initialize your cipher instance. Something like this:
aesCipher.init(Cipher.DECRYPT_MODE, sKey1);
byte[] plainText = aesCipher.doFinal(byteCipherText);
If you put all the above code into a main() and run it, you should have into plainText the same text as in byteText.
You can verify it with a
System.out.println(new String(plainText));
Try to start from here, then add all other things you may need.
Hope this helps.
Regards
Related
I am working on secure chat application. I have AES key for ecrypt/decrypt message and users have own public/private RS keys for encrypt/decrypt AES key. My code give me error like this:
> javax.crypto.BadPaddingException: Decryption error
at sun.security.rsa.RSAPadding.unpadV15(RSAPadding.java:383)
at sun.security.rsa.RSAPadding.unpad(RSAPadding.java:294)
at com.sun.crypto.provider.RSACipher.doFinal(RSACipher.java:363)
at com.sun.crypto.provider.RSACipher.engineDoFinal(RSACipher.java:389)
at javax.crypto.Cipher.doFinal(Cipher.java:2164)
at org.fastchat.User.decryptAESKey(User.java:183)
at org.fastchat.User.readMessage(User.java:210)
at org.fastchat.WatchInbox.startWatch(WatchInbox.java:59)
at org.fastchat.WatchInbox.run(WatchInbox.java:32)
Error is in function for decrypting AES key with RSA private key. I found that problem could be with wrong key (keys stored in serialization, every user have once generated keys in profile object) or encode/decode problems. I decide to store encrypted AES key in some file and read that file for decrypt AES key. Is there some better way for store encypted AES key? Am I doing encode/decode in right way? Any help is welcome, I dont have any ideas now. My code is here and every part of proccess is in different function.
public SecretKey generateAESkey() throws NoSuchAlgorithmException {
System.out.println("Generating AES key...\n");
SecureRandom random = new SecureRandom();
KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
keyGenerator.init(128, random);
SecretKey aeSecretKey = keyGenerator.generateKey();
System.out.println("AES1 KEY " + new String(Base64.getEncoder().encodeToString(aeSecretKey.getEncoded())));
System.out.println("===========================\n");
return aeSecretKey;
// return keyGenerator.generateKey();
}
public byte[] encryptAESKey(SecretKey key, PublicKey pubKey) throws Exception {
System.out.println("Encrypt AES key...\n");
String aesKey = new String(Base64.getEncoder().encodeToString(key.getEncoded()));
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.ENCRYPT_MODE, pubKey);
byte[] aesEnc = cipher.doFinal(aesKey.getBytes());
System.out.println("AES ENCRYPTED: " + aesEnc);
System.out.println("==========================\n");
return aesEnc;
// return cipher.doFinal(aesToEnc);
}
public byte[] encryptMessage(String message, SecretKey aeSecretKey) throws Exception {
System.out.println("Encrypt message...\n");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, aeSecretKey);
byte[] msgBytes = message.getBytes(); // This will be encrypted
byte[] msgEnc = cipher.doFinal(msgBytes);
System.out.println("=============================\n");
return msgEnc;
// return cipher.doFinal(msgBytes); //This will be in .txt file
}
public void checkerUpdate(User receiver) throws IOException {
BufferedWriter check = new BufferedWriter(new FileWriter(receiver.getCheck() + "/" + getUsername() + ".txt"));
Random random = new Random();
Integer num = random.nextInt(100);
check.write(num);
check.close();
}
public void sendMessage(String message, User receiver) throws Exception {
// First generate AES key
SecretKey aesSecretKey = generateAESkey(); // keysize = 128
// Encrypt message with AES key, will be in file
byte[] encMsg = encryptMessage(message, aesSecretKey);
// Encrypt AES key, put in file
byte[] aesEnc = encryptAESKey(aesSecretKey, receiver.getPub());
// Send message and key to receiver
FileOutputStream msgWrite = new FileOutputStream(receiver.getInbox() + "/" + getUsername() + ".msg", true);
FileOutputStream keyWrite = new FileOutputStream(receiver.getInbox() + "/" + getUsername() + ".kgn", true);
keyWrite.write(Base64.getEncoder().encode(aesEnc));
msgWrite.write(Base64.getEncoder().encode(encMsg));
keyWrite.close();
msgWrite.close();
// Add file to CHECKER, temp operations for WatchInbox service
checkerUpdate(receiver);
}
// FUNCTIONS FOR DECRYPTION
public byte[] decryptAESKey(byte[] aesEnc) throws Exception {
System.out.println("Decrypt AES key...\n");
byte[] aesEncString = Base64.getDecoder().decode(aesEnc);
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.DECRYPT_MODE, getPriv());
byte[] decAes = cipher.doFinal(aesEncString);
System.out.println("===========================\n");
return decAes;
// return cipher.doFinal(Base64.getDecoder().decode(aesEnc));
}
public String decryptMessage(byte[] msg, SecretKeySpec aeskey) throws Exception {
System.out.println("Decrypt message...\n");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, aeskey);
String msgDec = cipher.doFinal(Base64.getDecoder().decode(msg)).toString();
System.out.println("============================\n");
return msgDec;
// return cipher.doFinal(Base64.getDecoder().decode(msg)).toString();
}
public String readMessage(User sender) throws Exception {
// Read file in inbox
File msgtoRead = new File(getInbox() + "/" + sender.getUsername() + ".msg");
FileInputStream keytoRead = new FileInputStream(getInbox() + "/" + sender.getUsername() + ".kgn");
// Read 128bits from filetoRead. That is encrypted key.
byte[] aesEnc = new byte[128];
keytoRead.read(aesEnc);
// Decrypted aes key for decode
byte[] aesDec = decryptAESKey(aesEnc);
SecretKeySpec aesKey = new SecretKeySpec(aesDec, "AES/CBC/PKCS5Padding");
// Read message
byte[] message = Files.readAllBytes(msgtoRead.toPath());
keytoRead.close();
// Decrypt message with loaded AES key (aesKey), return this
return new String(decryptMessage(message, aesKey));
}
I want to decrypt the EncryptedAssertion. I tried with OpenSaml Decrypter but its not working for me.I am getting Failed to decrypt EncryptedData
I have already ask that question - EncryptedAssertion Decryption failing
While I am waiting for any solution I am trying to decrypt it manually. Its a Hybrid encryption
I tried below code
CipherValue cv = encryptedAssertion.getEncryptedData().getKeyInfo().getEncryptedKeys().get(0).getCipherData().getCipherValue();
String cvalue = cv.getValue();
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, getPrivateKey());
String decryptedValue = new String(cipher.doFinal(DatatypeConverter.parseBase64Binary(cvalue)));
I am not sure if I am on the right path, but above decryptedValue is the decryption key for my Encrypted Data.This decryptedValue is not in readable format. Not sure what to do next.
getPrivateKey method
public PrivateKey getPrivateKey(){
Key key = null;
PrivateKey privateKey = null;
try {
KeyStore ks = KeyStore.getInstance("pkcs12", "SunJSSE");
ks.load(new FileInputStream("prvkey.pfx"),"".toCharArray());
Enumeration<String> aliases = ks.aliases();
while(aliases.hasMoreElements()){
String alias = aliases.nextElement();
key = ks.getKey(alias, "".toCharArray());
privateKey = (PrivateKey)key;
}
} catch (Exception e) {
e.printStackTrace();
}
}
Based on the suggestion I coded like below. Not sure if I am doing it correct also I am getting errors
`CipherValue cv = encryptedAssertion.getEncryptedData().getKeyInfo().getEncryptedKeys().get(0).getCipherData().getCipherValue();
String cvalue = cv.getValue();
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.UNWRAP_MODE, getPrivateKey());
Key decryptionKey = cipher.unwrap(DatatypeConverter.parseBase64Binary(cvalue), "RSA/ECB/PKCS1Padding", Cipher.SECRET_KEY);
CipherValue cdata = encryptedAssertion.getEncryptedData().getCipherData().getCipherValue();
String cdataValue = cdata.getValue();
byte[] iv = new byte[256 / 16];
IvParameterSpec ivParamSpec = new IvParameterSpec(iv);
Cipher cipher2 = Cipher.getInstance("AES/CBC/PKCS5PADDING");
SecretKeySpec spec = new SecretKeySpec(decryptionKey.getEncoded(), "AES");
cipher2.init(Cipher.DECRYPT_MODE, spec, ivParamSpec );
String decryptedValue = new String(cipher2.doFinal(DatatypeConverter.parseBase64Binary(cdataValue)));`
Error -
Exception in thread "main" javax.crypto.BadPaddingException: Given final block not properly padded
at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:966)
at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:824)
at com.sun.crypto.provider.AESCipher.engineDoFinal(AESCipher.java:436)
at javax.crypto.Cipher.doFinal(Cipher.java:2121)
UPDATE ::
hope I am doing it correctly based on the comments.
byte[] iv = new byte[256/16];
iv = Arrays.copyOfRange(DatatypeConverter.parseBase64Binary(cdataValue), 0, 16);
byte[] cipherBlock = Arrays.copyOfRange(DatatypeConverter.parseBase64Binary(cdataValue), 16, DatatypeConverter.parseBase64Binary(cdataValue).length);
IvParameterSpec ivParamSpec = new IvParameterSpec(iv);
Cipher cipher2 = Cipher.getInstance("AES/CBC/PKCS5PADDING");
SecretKeySpec spec = new SecretKeySpec(decryptionKey.getEncoded(), "AES");
cipher2.init(Cipher.DECRYPT_MODE, spec, ivParamSpec );
String decryptedValue = new String(cipher2.doFinal(cipherBlock)); // Same error - Given final block not properly padded
I won't provide you a complete answer but I hope to get you on the right track
You should not just simply decrypt the calue with the private key.
First decrypt the KeyInfo value (unwrap the aes key) using RSA/ECB/PKCS1Padding (according to the provided saml snippet)
It should give you a 256 bit (32 bytes) random key used to encrypt data itself
then use the AES key to decrypt the data . Please note that first bytes (128 bit / 16 bytes, aes block size) is used as IV.
further reading
https://www.w3.org/TR/2002/REC-xmlenc-core-20021210/Overview.html#sec-Processing-Encryption
https://gusto77.wordpress.com/2017/10/30/encryption-reference-project/
public static byte[] decrypt(byte[] cryptoBytes, byte[] aesSymKey)
throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException,
InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException {
// https://github.com/onelogin/java-saml/issues/23
String cipherMethod = "AES/CBC/ISO10126Padding"; // This should be derived from Cryptic Saml
AlgorithmParameterSpec iv = new IvParameterSpec(cryptoBytes, 0, 16);
// Strip off the the first 16 bytes because those are the IV
byte[] cipherBlock = Arrays.copyOfRange(cryptoBytes,16, cryptoBytes.length);
// Create a secret key based on symKey
SecretKeySpec secretSauce = new SecretKeySpec(aesSymKey, "AES");
// Now we have all the ingredients to decrypt
Cipher cipher = Cipher.getInstance(cipherMethod);
cipher.init(Cipher.DECRYPT_MODE, secretSauce, iv);
// Do the decryption
byte[] decrypedBytes = cipher.doFinal(cipherBlock);
return decrypedBytes;
}
ISO10126Padding should work....
In order for me to consume a webservice, I need to generate a value for an header named Authorization. The steps to generate the header is as follows:
1. Hash Generation
HashValue = SHA2(username, password, id)
2. Auth Key Generation
Authkey = AES(Salt + anotherId + "=" + HashValue)
These are the algorithms details:
Algorithm - AES
Mode - ECB
Padding - PKCS5Padding
Secret key - someString
Now I will be performing the AES encryption using the above details and the secret key which is a string.
After the encryption, I will use the above generated encrypted value as a header in my rest service call.
I have done this so far:
String username = "username";
String password = "password";
String id = "123456";
String toBeHashed = username + password + id;
MessageDigest sha256 = MessageDigest.getInstance("SHA-256");
byte[] hashed = sha256.digest(toBeHashed.getBytes("UTF-8"));
String hashString = "=" + Base64.encodeBase64String(hashed);
System.out.println(hashString);
String salt = "salt";
String anotherId = "123";
byte[] forAuth = (salt + orgId + hashString).getBytes("UTF-8");
//Mocked "secret key". Original key string is of size 16 bytes.
byte[] secKey = "secret key".getBytes("UTF-8");
SecretKey secretKey = new SecretKeySpec(secKey, 0, secKey.length, "AES");
Cipher aesCipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
aesCipher.init(Cipher.ENCRYPT_MODE, secretKey);
byte[] authorizationKey = aesCipher.doFinal(forAuth);
System.out.println("-------------------");
System.out.println("-------------------");
System.out.println(Base64.encodeBase64String(authorizationKey));
But still the backend service says that my authorization key is invalid. Please tell me if I am missing something.
**Encrypt and Decrypt using AES**
public static String encrypt(String value) throws UnsupportedEncodingException, NoSuchAlgorithmException, InvalidKeyException, NoSuchPaddingException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException {
IvParameterSpec iv = new IvParameterSpec("randomStringVec".getBytes("UTF-8"));
SecretKeySpec skeySpec = new SecretKeySpec("randomStringKey".getBytes("UTF-8"), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
cipher.init(1, skeySpec, iv);
byte[] encrypted = cipher.doFinal(value.getBytes());
return Base64.encodeBase64String(encrypted);
}
public static String decrypt(String encrypted) throws UnsupportedEncodingException, NoSuchAlgorithmException, InvalidKeyException, NoSuchPaddingException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException {
IvParameterSpec iv = new IvParameterSpec("randomStringVec".getBytes("UTF-8"));
SecretKeySpec skeySpec = new SecretKeySpec("randomStringKey".getBytes("UTF-8"), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
cipher.init(2, skeySpec, iv);
byte[] original = cipher.doFinal(Base64.decodeBase64(encrypted));
return new String(original);
}
**Encryption Using SHA256**
private static String encrypt256(String password) throws NoSuchAlgorithmException, UnsupportedEncodingException {
MessageDigest crypt = MessageDigest.getInstance("SHA-256");
crypt.reset();
crypt.update(password.getBytes("UTF-8"));
return new BigInteger(1, crypt.digest()).toString(16);
}
You need to change this:
String hashString = "=" + Base64.encodeBase64String(hashed);
System.out.println(hashString);
To:
String hashString = "=" + new String(hashed);
System.out.println(hashString);
Since the hashed key is getting base64encoded before the authorization key generation.
This question already has answers here:
IllegalBlockSizeException when trying to encrypt and decrypt a string with AES
(4 answers)
Closed 6 years ago.
I am trying to encode the passphase into a properties file so that I don't have to type in the passphase to make an SSH connection. But I am facing the following error :
javax.crypto.BadPaddingException: Given final block not properly padded
at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:966)
at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:824)
at com.sun.crypto.provider.AESCipher.engineDoFinal(AESCipher.java:436)
at javax.crypto.Cipher.doFinal(Cipher.java:2165)
at com.test.ssh.SSH_Public_Private.deCryptPwd(SSH_Public_Private.java:191)
at com.test.ssh.SSH_Public_Private.checkHostName(SSH_Public_Private.java:227)
at com.test.ssh.SSH_Public_Private.checkHostName(SSH_Public_Private.java:223)
at com.test.ssh.SSH_Public_Private.connectToSSH(SSH_Public_Private.java:64)
at com.test.ssh.SSH_Public_Private.main(SSH_Public_Private.java:124)
My code is as follows:
private String checkHostName(String hostUserName) throws IOException, InvalidKeyException, InvalidAlgorithmParameterException, NoSuchAlgorithmException, NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException {
String deCrypted = null;
FileInputStream is = new FileInputStream(new File("C:\\test\\SSH\\PrivateKey\\keystore.properties"));
Properties properties = new Properties();
properties.load(is);
ssh_Public_Private = new SSH_Public_Private();
boolean isHostNameExist = false;
if (properties.getProperty(hostUserName) == null) {
OutputStream outputStream = new FileOutputStream(
"C:\\test\\SSH\\PrivateKey\\keystore.properties");
String passPhraseStored = new String(enCryptPwd());
properties.setProperty(hostUserName,passPhraseStored );
properties.store(outputStream, null);
outputStream.close();
is.close();
return checkHostName(hostUserName);
}else{
System.out.println(properties.getProperty(hostUserName));
String passPhrase = properties.getProperty(hostUserName);
deCrypted = deCryptPwd(passPhrase); //isHostNameExist = true;
}
return deCrypted;
}
My encryption and decryption piece of code is as follow :
private static String enCryptPwd() throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException {
String decrypted = null;
byte[] encrypted = null;
try {
String text = "";
Scanner sc = new Scanner(System.in);
System.out.println("Enter your passphrase : " );
text = sc.next();
String key = "Bar12345Bar12345"; // 128 bit key
//String key = "AesSEcREtkeyABCD";
// Create key and cipher
Key aesKey = new SecretKeySpec(key.getBytes("UTF-8"), "AES");
System.out.println(aesKey.getFormat());
Cipher cipher = Cipher.getInstance("AES");
// encrypt the text
cipher.init(Cipher.ENCRYPT_MODE, aesKey);
encrypted = cipher.doFinal(text.getBytes("UTF-8"));
System.err.println(new String(encrypted));
System.err.println(encrypted.length);
} catch (Exception e) {
e.printStackTrace();
}
return new String(encrypted);
}
private static String deCryptPwd(String encrypted) throws InvalidKeyException, InvalidAlgorithmParameterException, NoSuchAlgorithmException, NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException {
String originalString = "";
try {
String key = "Bar12345Bar12345"; // 128 bit key
//String key = "AesSEcREtkeyABCD";
// Create key and cipher
Key aesKey = new SecretKeySpec(key.getBytes("UTF-8"), "AES");
Cipher cipher = Cipher.getInstance("AES");
// decrypt the text
cipher.init(Cipher.DECRYPT_MODE, aesKey);
byte[] encryptBytes = new byte[encrypted.length()];
encryptBytes = encrypted.getBytes();
byte[] decrypted = cipher.doFinal(encryptBytes);
originalString = new String(decrypted, "UTF-8");
System.out.println(originalString);
System.err.println(decrypted);
} catch (Exception e) {
e.printStackTrace();
}
return originalString;
}
I have been trying to read up and I have tried many other ways by including no padding Algorithms as well. My code writes the output into a property file as :
abced=Y\u201Eh\uFFFD\u00EC-:\u00F9\u00F8mC\u0160\u0002\u00F3#\u00DE
My console outputs are :
Enter your passphrase :
abc!##
After encryption >> Y„h?ì-:ùømCŠó#Þ
16
Read from property file >> Y„h?ì-:ùømCŠó#Þ
Generate key using KeyGenerator
KeyGenerator kg = KeyGenerator.getInstance("AES");
kg.init(128, new SecureRandom());
SecretKeySpec aesKey = new SecretKeySpec(kg.generateKey().getEncoded(), "AES");
Use AES/CBC/PKCS5Padding to get cipher instance.
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding")
This should work.
I want to learn the basics of AES encryption so I started to make a very simple Java program. The program loads a text file in to a String and asks for a key from the user. The program then uses AES to encrypt the text creating a new text file with the encrypted text. The program prints the Initialization Vector (IV) to the user.
The program also has the decryption function. The user specifies the encrypted text file along with the Initialization Vector and the key to decrypt it back to the original text in a new text file.
However I think I'm doing something wrong. Is it normal procedure in AES encryption that the user needs to have both key and IV to decrypt the file? I have browsed through the internet and almost in every example, the encrypted data can be decrypted by the user specifying only the key but in my case the user needs to have both the key and the IV. The program is working fine but I think it isn't efficient.
So should I use a constant, known IV which is used in all the encryptions and decryptions or what? Also some tutorials are using "salt", what is it and should I use it?
Here are my encrypt and decrypt methods:
public String encrypt(String stringToEncrypt, String userKey)
throws NoSuchAlgorithmException, NoSuchPaddingException,
InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
// User gives string key which is formatted to 16 byte and to a secret
// key
byte[] key = userKey.getBytes();
MessageDigest sha = MessageDigest.getInstance("SHA-1");
key = sha.digest(key);
key = Arrays.copyOf(key, 16);
SecretKeySpec secretKey = new SecretKeySpec(key, "AES");
// Cipher initialization
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
// Encryption and encoding
String encryptedData = new BASE64Encoder().encode(cipher
.doFinal(stringToEncrypt.getBytes()));
// IV is printed to user
System.out.println("\nENCRYPTION IV: \n"
+ new BASE64Encoder().encode(cipher.getIV()) + "\n");
// Function returns encrypted string which can be writed to text file
return encryptedData;
}
public String decrypt(String stringToDecrypt, String userKey, String userIv)
throws NoSuchAlgorithmException, IOException,
NoSuchPaddingException, InvalidKeyException,
InvalidAlgorithmParameterException, IllegalBlockSizeException,
BadPaddingException {
// User gives the same string key which was used for encryption
byte[] key = userKey.getBytes();
MessageDigest sha = MessageDigest.getInstance("SHA-1");
key = sha.digest(key);
key = Arrays.copyOf(key, 16);
SecretKeySpec secretKey = new SecretKeySpec(key, "AES");
// Decode string iv to byte
byte[] iv = new BASE64Decoder().decodeBuffer(userIv);
// Cipher initialization
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
cipher.init(Cipher.DECRYPT_MODE, secretKey, new IvParameterSpec(iv));
// Decryption and decoding
String decryptedData = new String(cipher.doFinal(new BASE64Decoder()
.decodeBuffer(stringToDecrypt)));
// Function returns decrypted string which can be writed to text file
return decryptedData;
}
UPDATE
I updated my code now to use "PBKDF2WithHmacSHA256" algorithm with salt and etc. I also combined the Initialization Vector (IV) byte array to the cipher text byte array as prefix so I can split them in decrypt method and get the IV there (That's working fine).
However there's now an issue with the key, because I'm generating new encrypted key also in decryption method which of course is a wrong key for encrypted data. I want to be able to close the program so I can't store the key as a class variable. It's very hard to explain the issue but I hope you understand the problem...
public static byte[] getEncryptedPassword(String password, byte[] salt,
int iterations, int derivedKeyLength)
throws NoSuchAlgorithmException, InvalidKeySpecException {
KeySpec mKeySpec = new PBEKeySpec(password.toCharArray(), salt,
iterations, derivedKeyLength);
SecretKeyFactory mSecretKeyFactory = SecretKeyFactory
.getInstance("PBKDF2WithHmacSHA256");
return mSecretKeyFactory.generateSecret(mKeySpec).getEncoded();
}
public String encrypt(String dataToEncrypt, String key) throws InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidParameterSpecException, IllegalBlockSizeException, BadPaddingException, InvalidKeySpecException {
byte[] mEncryptedPassword = getEncryptedPassword(key, generateSalt(),
16384, 128);
SecretKeySpec mSecretKeySpec = new SecretKeySpec(mEncryptedPassword, "AES");
Cipher mCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
mCipher.init(Cipher.ENCRYPT_MODE, mSecretKeySpec);
byte[] ivBytes = mCipher.getIV();
byte[] encryptedTextBytes = mCipher.doFinal(dataToEncrypt.getBytes());
byte[] combined = new byte[ivBytes.length+encryptedTextBytes.length];
System.arraycopy(ivBytes, 0, combined, 0, ivBytes.length);
System.arraycopy(encryptedTextBytes, 0, combined, ivBytes.length, encryptedTextBytes.length);
return Base64.getEncoder().encodeToString(combined);
}
public String decrypt(String dataToDecrypt, String key) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidKeySpecException, IllegalBlockSizeException, BadPaddingException, InvalidAlgorithmParameterException {
byte[] encryptedCombinedBytes = Base64.getDecoder().decode(dataToDecrypt);
byte[] mEncryptedPassword = getEncryptedPassword(key, generateSalt(),
16384, 128);
byte[] ivbytes = Arrays.copyOfRange(encryptedCombinedBytes,0,16);
SecretKeySpec mSecretKeySpec = new SecretKeySpec(mEncryptedPassword, "AES");
Cipher mCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
mCipher.init(Cipher.DECRYPT_MODE, mSecretKeySpec, new IvParameterSpec(ivbytes));
byte[] encryptedTextBytes = Arrays.copyOfRange(encryptedCombinedBytes, 16, encryptedCombinedBytes.length);
System.out.println(encryptedTextBytes.length);
byte[] decryptedTextBytes = mCipher.doFinal(encryptedTextBytes);
return Base64.getEncoder().encodeToString(decryptedTextBytes);
}
public byte[] generateSalt() {
SecureRandom random = new SecureRandom();
byte saltBytes[] = new byte[16];
random.nextBytes(saltBytes);
return saltBytes;
}}
I hope somebody knows how to make this better. Thanks!
Just save the IV in the file before the encrypted data.
You should never use the same IV more than once (it's ok-ish, if you roll a new IV every time, and it just so happens that you roll the same twice, so you don't have to store and check that). Using the same IV many times poses a great security risk, as encrypting the same content twice reveals that it's - in fact - the same content.
Storing IV alongside the encrypted data is a common, and secure procedure, as it's role is to introduce "randomness" to the encryption scheme, and it shouldn't be secret, just securely (and in some schemes randomly) generated.