I found this code on a website. I can't understand how to decode this. Can you help me?
import java.util.Base64;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
public class AES {
public static String encrypt(String strToEncrypt) throws Exception {
byte[] plaintext = strToEncrypt.getBytes("UTF-8");
KeyGenerator keygen = KeyGenerator.getInstance("AES");
keygen.init(256);
SecretKey key = keygen.generateKey();
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] ciphertext = cipher.doFinal(plaintext);
return Base64.getEncoder().encodeToString(ciphertext);
}
}
Welcome to Stackoverflow. Below you find a full working example of an AES CBC String en-/decryption. Please note that you need to store the randmly created key & initialization vectore securely to (later) encrypted data because otherwise there is (realy) NO way to recover your data. The same key and iv needs to be used for encryption and decryption.
As the key & iv are byte arrays I encoded them to Base64 for a better storage.
Security warning: This is a simple example to demonstrate AES CBC en-/decryption without any proper exception handling.
The code is for educational purposes only and should not be used in production!
result:
AES CBC String Encryption with random key + iv
This is a simple example to demonstrate AES CBC en-/decryption without any proper exception handling.
The code is for educational purposes only and should not be used in production.
save the key and iv securely, without the data it will be NOT possible to decrypt !!
key in Base64-format: Nf41yG0F+MdFQnp3p3mIrWOk+2kxQ/LmyVcHKEKi5sQ=
iv in Base64-format: yICmqsMaIdwsYsUDUsLWnA==
plaintext: The quick brown fox jumps over the lazy dog
ciphertext: PJNEV3H3Zh3TQx7B9jpg29gV59LgJ6baOpNM82dMOpPClJouYnq+hKVUQTDEkkdI
decryptedtext: The quick brown fox jumps over the lazy dog
code:
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Base64;
public class AesCbcTextEncryptionRandomKeyIv {
public static void main(String[] args) throws NoSuchPaddingException, InvalidKeyException, NoSuchAlgorithmException, IllegalBlockSizeException, BadPaddingException, InvalidAlgorithmParameterException {
System.out.println("AES CBC String Encryption with random key + iv");
System.out.println("This is a simple example to demonstrate AES CBC en-/decryption without any proper exception handling.");
System.out.println("The code is for educational purposes only and should not be used in production.\n");
String plaintext = "The quick brown fox jumps over the lazy dog";
// generate a random key & initialization vector
byte[] key = new byte[32]; // key for aes 256 encryption, 32 byte length
byte[] iv = new byte[16]; // initialization vector with 16 byte length
SecureRandom secureRandom = new SecureRandom();
secureRandom.nextBytes(key);
secureRandom.nextBytes(iv);
System.out.println("save the key and iv securely, without the data it will be NOT possible to decrypt !!");
// convert key & iv in base64 format for storage reasons
String keyBase64 = Base64.getEncoder().encodeToString(key);
String ivBase64 = Base64.getEncoder().encodeToString(iv);
System.out.println("key in Base64-format: " + keyBase64);
System.out.println("iv in Base64-format: " + ivBase64);
// encryption
String ciphertext = encrypt(keyBase64, ivBase64, plaintext);
System.out.println("plaintext: " + plaintext);
System.out.println("ciphertext: " + ciphertext);
// decryption
String decryptedtext = decrypt(keyBase64, ivBase64, ciphertext);
System.out.println("decryptedtext: " + decryptedtext);
}
public static String encrypt(String keyBase64, String ivBase64, String plaintext)
throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException,
InvalidKeyException, BadPaddingException, IllegalBlockSizeException {
SecretKeySpec secretKeySpec = new SecretKeySpec(Base64.getDecoder().decode(keyBase64), "AES");
IvParameterSpec ivParameterSpec = new IvParameterSpec(Base64.getDecoder().decode(ivBase64));
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec);
return Base64.getEncoder().encodeToString(cipher.doFinal(plaintext.getBytes(StandardCharsets.UTF_8)));
}
public static String decrypt(String keyBase64, String ivBase64, String ciphertext)
throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException,
InvalidKeyException, BadPaddingException, IllegalBlockSizeException {
SecretKeySpec secretKeySpec = new SecretKeySpec(Base64.getDecoder().decode(keyBase64), "AES");
IvParameterSpec ivParameterSpec = new IvParameterSpec(Base64.getDecoder().decode(ivBase64));
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec);
return new String(cipher.doFinal(Base64.getDecoder().decode(ciphertext)), StandardCharsets.UTF_8);
}
}
Related
I am getting the following error when trying to decrypted:
javax.crypto.IllegalBlockSizeException: Input length must be multiple of 16 when decrypting with padded cipher
Here is the encryption class I have implemented:
import java.io.UnsupportedEncodingException;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.SecretKeySpec;
public class StringEncrypter {
public static String encrypt(String key, String string, String algorithm) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException, UnsupportedEncodingException {
Key aesKey = new SecretKeySpec(key.getBytes("UTF-8"), algorithm);
Cipher cipher = Cipher.getInstance(algorithm);
cipher.init(Cipher.ENCRYPT_MODE, aesKey);
byte[] encrypted = cipher.doFinal(string.getBytes());
return encrypted.toString();
}
public static String decrypt(String key, String encryptedString, String algorithm) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException, UnsupportedEncodingException {
Key aesKey = new SecretKeySpec(key.getBytes("UTF-8"), algorithm);
Cipher cipher = Cipher.getInstance(algorithm);
cipher.init(Cipher.DECRYPT_MODE, aesKey);
String decrypted = new String(cipher.doFinal(encryptedString.getBytes()));
return decrypted;
}
}
This is how I encrypted a string:
StringEncrypter.encrypt("0306868080306868", "ddd", "AES"); // [B#e19957c
When I attempt to decrypt the above encrypted string like this:
String decrypted = StringEncrypter.decrypt("0306868080306868", "[B#e19957c", "AES");
I get the illegalBlockSizeException.
What am I doing wrong above? How do I correctly decrypt an encrypted String?
You need to perform base 64 encoding decoding for your key and your ciphertext. There is a new Base64 class for this in Java 8. You cannot just store any byte in a string, not all bytes represent printable or even valid characters, and the output of a cipher is indistinguishable from random.
Besides that, the byte array "class" (represented by [B in Java) doesn't implement the toString method, which means you just get the print out of Object.toString, i.e. the class name [B and a human readable identifier to the object instance instead of the actual ciphertext.
You cannot use bytes (binary) as a String. it is not equivalent
You should convert it . Several manners. Base64 or Hexa, for example
You
with base64, it gives this:
import javax.xml.bind.DatatypeConverter ;
byte[] bt= ... // what you get
// Conversion B64
String encodedb64=DatatypeConverter.printBase64Binary(bt);
// CONVERSION base 64 => byte
// base 64 => byte
byte [] byteArrayreverse=DatatypeConverter.parseBase64Binary(encodedb64);
I'm having trouble creating an encrypted string using AES/CBC/PKCS5Padding with a 128-bit key. I have code to decrypt an encrypted string. I have an example encrypted string from another system that decrypts successfully, but when I try to create my own encrypted string it is not padded properly for some reason. When I decrypt my encrypted string it only shows the characters after the 16 byte.
All of the examples I find either assume the encryption happens first then decryption happens right after that with variables set during encryption or they are randomly generating a key, but in my case i want to use a known key.
I am really stuck so any help would be greatly appreciated, thank you very much for your time and efforts!
Example:
Original Text: 01234567891234565
Encrypted: zmb16qyYrdoW6akBdcJv7DXCzlw0qU7A2ea5q4YQWUo=
Key length: 16
Decrypted: 5 (this is the last digit in the Original Text String)
Sample Code:
package com.company.encrypt.tests;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.binary.Base64;
public class TestEncryptDecrypt {
private static final String characterEncoding = "UTF-8";
private static final String cipherTransformation = "AES/CBC/PKCS5Padding";
private static final String aesEncryptionAlgorithm = "AES";
public static void main(String[] args) throws Exception {
String key1 = "1234567812345678";
String text = "01234567891234565";
System.out.println("Original Text: " + text);
String encrypted = encrypt(text, key1);
System.out.println("Encrypted: " + encrypted);
String decrypted = decrypt(encrypted, key1);
System.out.println("Decrypted: " + decrypted);
}
public static String decrypt(String encryptedText, String key) throws Exception {
String plainText = null;
int keyLength = key.length();
System.out.println("Key length: " + String.valueOf(keyLength));
byte[] encryptedTextBytes = Base64.decodeBase64(encryptedText.getBytes());
byte[] keyBytes = key.getBytes();
byte[] initialVector = Arrays.copyOfRange(encryptedTextBytes, 0, keyLength);
byte[] trimmedCipherText = Arrays.copyOfRange(encryptedTextBytes, keyLength, encryptedTextBytes.length);
try {
Cipher cipher = Cipher.getInstance(cipherTransformation);
SecretKeySpec secretKeySpec = new SecretKeySpec(keyBytes, aesEncryptionAlgorithm);
IvParameterSpec ivParameterSpec = new IvParameterSpec(initialVector);
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec);
byte[] clearText;
clearText = cipher.doFinal(trimmedCipherText);
plainText = new String(clearText, characterEncoding);
} catch(NoSuchAlgorithmException | NoSuchPaddingException | IllegalBlockSizeException | BadPaddingException
| InvalidKeyException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return plainText;
}
public static String encrypt(String plainText, String encryptionKey) throws Exception {
SecretKeySpec key = new SecretKeySpec(encryptionKey.getBytes("UTF-8"), aesEncryptionAlgorithm);
Cipher cipher = Cipher.getInstance(cipherTransformation);
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] plainTextBytes = plainText.getBytes("UTF-8");
byte[] encrypted = cipher.doFinal(plainTextBytes);
return new String(Base64.encodeBase64(encrypted));
}
}
I've noticed that in the decrypt() function, you separated the encrypted array into two parts: first 16 bytes, and the rest. You used the first 16 bytes as the IV for decryption, however, you did not prepend the 16 byte IV to the beginning of the encrypted message in encrypt(). This results in the first 16 bytes of the plaintext to be lost. I presume you assumed that doFinal() automatically does that for you, but it doesn't.
To fix this, before returning the encrypted message, prepend the IV, which can be retrieved using cipher.getIV(). You can accomplish this using the ArrayUtils.addAll() from Apache Commons Lang library, or simply write your own function to do it. Another thing to note is that the IV will always be the block size, which is 16 bytes for AES, no matter the key size.
Hope this answer helps!
How to Encrypt and Decrypt in Java using AES
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.SecretKeySpec;
public class Encryption {
public static void main(String args[]) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException{
String t = "Testing";
byte[] dataToSend = t.getBytes();
byte[] key = new byte[16];
Cipher c = Cipher.getInstance("AES");
SecretKeySpec k = new SecretKeySpec(key, "AES");
c.init(Cipher.ENCRYPT_MODE, k);
byte[] encryptedData = c.doFinal(dataToSend);
System.out.println(encryptedData);
byte[] key2 = new byte[16];
byte[] encryptedData2 = encryptedData;
Cipher c2 = Cipher.getInstance("AES");
SecretKeySpec k2 =
new SecretKeySpec(key2, "AES");
c2.init(Cipher.DECRYPT_MODE, k2);
byte[] data = c.doFinal(encryptedData);
System.out.println(data);
}
}
Since the problem is not specified explicitly, I am pointing out the obvious errors with the presumption that 'Testing' encrypted should print 'Testing' once decrypted.
AES is a symmetric key algorithm, hence you have to use the same key for encrypting and decrypting (k2 should not be there at all)
c.doFinal of the encrypted data doesn't make much sense, this should be reference to your Cipher DECRYPT_MODE -> c2
Hence, change:
c2.init(Cipher.DECRYPT_MODE, k2);
To:
c2.init(Cipher.DECRYPT_MODE, k);
And change:
byte[] data = c.doFinal(encryptedData);
To:
byte[] data = c2.doFinal(encryptedData);
To print the final outcome:
Change:
System.out.println(data);
To:
System.out.println(new String(data));
I'm trying to develop a simple encryption/decryption program. The problem I am running into is when I attempt to decrypt the encrypted message, I get an error message stating that the Input length must be multiple of 16 when decrypting with cipher. I read somewhere that the encrypted message might need to be encoded before converting it to a string. I'm not sure how to do this? Or if there is an alternative way can someone please help me out?
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.KeyGenerator;
import javax.crypto.NoSuchPaddingException;
public class Cryption {
public static void cryption(String[] args, String message) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException, InvalidAlgorithmParameterException {
byte[] encodedKey = "ADBSJHJS12547896".getBytes();
KeyGenerator keyGen = KeyGenerator.getInstance("AES");
Key aesKey = keyGen.generateKey();
System.out.println("CheckType: "+ Global.checkType);
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, aesKey);
byte[] input = Global.message.getBytes();
// Check if clicked Encrypted
if(Global.checkType==true) {
// Encrypt
byte[] messageEncrypted = cipher.doFinal(input);
System.out.println("Encrypted Text: " + messageEncrypted);
Global.encValue = messageEncrypted.toString();
}
// Check if clicked Decrypted
if(Global.checkType==false) {
//String mes = message;
System.out.println(Global.message);
System.out.println("Char lenght " + Global.message.length());
byte[] mesByte = Global.message.getBytes();
// Decrypt
cipher.init(Cipher.DECRYPT_MODE, aesKey);
byte[] messageDecrypted = cipher.doFinal(mesByte);
System.out.println("Text Decrypted: " + new String(messageDecrypted));
}
}
}
Global.encValue = messageEncrypted.toString();
This is completely wrong, as it just calls byte[].toString(), which doesn't give you the contents, just a thing with a classname and a hashcode in it. It is also wrong semantically, as String is not a container for binary data in the first place. Don't turn encrypted text into a String. Use the byte[] array that the API gave you.
Have a look at http://docs.oracle.com/javase/tutorial/i18n/text/string.html
It probably means that it doesn't know if it is ASCII or UTF8 or some other byte encoding...
So I'm writing a program to encrypt and decrypt text files but I seem to be always getting this error when I use an encrypthion other than "Blowfish" (e.g. "Blowfish/CBC/PKCS5Padding"). The excepthiong I get is:
Exception in thread "main" java.security.NoSuchAlgorithmException: Blowfish/CBC/PKCS5Padding KeyGenerator not available
at javax.crypto.KeyGenerator.<init>(DashoA13*..)
at javax.crypto.KeyGenerator.getInstance(DashoA13*..)
at Encryptor.<init>(Encryptor.java:87)
at Encryptor.main(Encryptor.java:30)
A portion of my code:
import java.security.MessageDigest;
import java.security.SecureRandom;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
public class Encryptor2 {
private IvParameterSpec ivSpec;
private SecretKeySpec keySpec;
private Cipher cipher;
public static void main(String[] args) throws Exception {
Encryptor2 e = new Encryptor2(
"averylongtext!#$##$##$#*&(*&}{23432432432dsfsdf");
String enc = e.encrypt("john doe");
String dec = e.decrypt(enc);
}
public Encryptor2(String pass) throws Exception {
// setup AES cipher in CBC mode with PKCS #5 padding
cipher = Cipher.getInstance("AES/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);
ivSpec = new IvParameterSpec(iv);
// hash keyString with SHA-256 and crop the output to 128-bit for key
MessageDigest digest = MessageDigest.getInstance("SHA-256");
digest.update(pass.getBytes());
byte[] key = new byte[16];
System.arraycopy(digest.digest(), 0, key, 0, key.length);
keySpec = new SecretKeySpec(key, "AES");
}
public String encrypt(String original) throws Exception {
cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec);
byte[] encrypted = cipher.doFinal(original.getBytes("UTF-8"));
System.out.println("encrypted: `" + new String(encrypted) + "`");
return new String(encrypted);
}
public String decrypt(String encrypted) throws Exception {
cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
byte[] decrypted = cipher.doFinal(encrypted.getBytes("UTF-8"));
System.out.println("decrypted: `" + new String(decrypted, "UTF-8")
+ "`");
return new String(decrypted, "UTF-8");
}
}
But now it fails with Input length must be multiple of 16 when decrypting with padded cipher
The additional parameters that you're specifying with the algorithm are meant for Cipher. For KeyGenerator and SecretKeySpec you only specify the algorithm. The other parameters are for the cipher mode of operation and padding to be used. For example, if you're using Blowfish in CBC mode with PKCS #5 padding you want:
KeyGenerator keyGen = KeyGenerator.getInstance("Blowfish");
Cipher cipher = Cipher.getInstance("Blowfish/CBC/PKCS5Padding");
See Encrypting and Decrypting Using Java: Unable to get same output for an example. It uses the same mode and padding as you have. The only difference is that uses AES instead of Blowfish, however it works exactly the same.