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...
Related
I am trying to make a little encryption/decryption software for my APCS-A final project. I have most of it done with various text ciphers and such, but I am trying to get DES encryption to work as like the big thing. I couldn't get it working in the main program so I created its own little thing to try to learn and test it first. I think I've gotten decently far, but I am getting stuck on an error that I can't figure out.
import java.util.Scanner;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.KeyGenerator;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.spec.*;
import java.util.Base64;
public class BigETest
{
public static void main(String[]args) throws NoSuchAlgorithmException,NoSuchPaddingException,InvalidKeyException,IllegalBlockSizeException,BadPaddingException
{
Scanner kbd = new Scanner(System.in);
System.out.print("Enter text: ");
String t = kbd.nextLine();
String[] rets = en(t);
System.out.println("Encrypted: "+ rets[0]);
System.out.println("Key: " + rets[1]);
String dec = de(rets[0],rets[1]);
System.out.println("Decrypted: "+dec);
}
public static String[] en(String inText) throws BadPaddingException,IllegalBlockSizeException,InvalidKeyException,NoSuchPaddingException,NoSuchAlgorithmException
{
KeyGenerator keyGen = KeyGenerator.getInstance("DES");
SecretKey key = keyGen.generateKey();
Cipher cipher = Cipher.getInstance("DES");
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] text = Base64.getUrlDecoder().decode(inText);
byte[] encryptedBytes = cipher.doFinal(text);
String encrypted = new String(Base64.getUrlEncoder().encodeToString(encryptedBytes));
byte[] rawKey = key.getEncoded();
String stringKey = new String(Base64.getUrlEncoder().encodeToString(rawKey));
String[] ret = {encrypted,stringKey};
return ret;
}
public static String de(String inText, String inKey) throws BadPaddingException,IllegalBlockSizeException,InvalidKeyException,NoSuchPaddingException,NoSuchAlgorithmException
{
byte[] keyB = Base64.getUrlDecoder().decode(inKey);
SecretKey key = new SecretKeySpec(keyB, "DES");
Cipher cipher = Cipher.getInstance("DES");
cipher.init(Cipher.DECRYPT_MODE, key);
byte[] text = Base64.getUrlDecoder().decode(inText);
byte[] fin = cipher.doFinal(text);
String dec = new String(Base64.getUrlEncoder().encodeToString(fin));
return dec;
}
}
The code works sometimes if the input is short and doesn't have spaces in it
Enter text: test
Encrypted: BOM9tFOUfWc=
Key: bq2ooSbc1n8=
Decrypted: test
but falls apart if the input is a bit longer
Enter text: BigTest
Encrypted: jme0nIAHXyA=
Key: 90UxVwetj2g=
Decrypted: BigTess=
And then gives me an error if there are any spaces
Enter text: bigger test
java.lang.IllegalArgumentException: Illegal base64 character 20
at java.base/java.util.Base64$Decoder.decode0(Base64.java:746)
at java.base/java.util.Base64$Decoder.decode(Base64.java:538)
at java.base/java.util.Base64$Decoder.decode(Base64.java:561)
at BigETest.en(BigETest.java:34)
at BigETest.main(BigETest.java:20)
I have tried changing things up and looked around both on here and just around anyplace I could find on Google for some answers, but nothing seems to be working. Any help would be greatly appreciated.
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);
}
}
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!
I am working in a simple example for DES implementation using Java built-in libraries. This is my code:
import it.sauronsoftware.base64.Base64;
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.SecretKey;
import javax.crypto.spec.SecretKeySpec;
public class DES {
public static void main(String [] args) throws NoSuchAlgorithmException, InvalidKeyException, NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException
{
String msg="This is a secret message";
byte [] msgBytes=msg.getBytes();
byte [] keyBytes = {(byte)0xFE, (byte)0xDC, (byte)0xBA, (byte)0x98, (byte)0x76, (byte)0x54, (byte)0x32, (byte)0x10};
SecretKeySpec myDesKey = new SecretKeySpec(keyBytes, "DES");
//to encrypt a message
String cipher=encryptMsg(msgBytes, myDesKey);
//to decrypt a message
String plain = decryptMsg(cipher.getBytes(), myDesKey);
System.out.println("Original Message: "+ msg);
System.out.println("Encrypted Message: "+ cipher);
System.out.println("Decrypted Message: "+ plain);
} //end main
//encryption function
public static String encryptMsg(byte [] msgBytes, SecretKey myDesKey) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException
{
Cipher desCipher;
// Create the cipher
desCipher = Cipher.getInstance("DES/ECB/NoPadding");
desCipher.init(Cipher.ENCRYPT_MODE, myDesKey);
byte[] textEncrypted = desCipher.doFinal(msgBytes);
// converts to base64 for easier display.
byte[] base64Cipher = Base64.encode(textEncrypted);
return new String(base64Cipher);
} //end encryptMsg
public static String decryptMsg(byte [] cipherBytes, SecretKey myDesKey) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException
{
Cipher desCipher;
desCipher = Cipher.getInstance("DES/ECB/NoPadding");
desCipher.init(Cipher.DECRYPT_MODE, myDesKey);
byte[] textDecrypted=desCipher.doFinal(cipherBytes);
// converts to base64 for easier display.
byte[] base64Plain = Base64.encode(textDecrypted);
return new String(base64Plain);
} //end decryptMsg
} //end class
The output I am getting is:
Original Message: This is a secret message
Encrypted Message: hNFgTAoz2TN9f6FcLdbjnEBe5DrsU4sm
Decrypted Message: RFdk1JK0gG0vv2zndHueS9rRe0Oux44ACGObsRHQ+4E=
I need my key to be a fixed (not random) value. That's why I defined it as array of bytes in the beginning.
My problem is that my decryption function does not return the original message. This means there is a problem in the code and probably the encryption is not proper. I am suspicious about the coding issue as the encryption / decryption is quite straight forward. Can you point the problems in my code?
EDIT:
In the decryption, I changed the encode to decode as suggested by one of the comments. That does not work. I get:
Exception in thread "main" java.lang.RuntimeException: Unexpected I/O error
at it.sauronsoftware.base64.Base64.decode(Unknown Source)
at DES.decryptMsg(DES.java:55)
at DES.main(DES.java:25)
Caused by: java.io.IOException: Bad base64 stream
at it.sauronsoftware.base64.Base64InputStream.acquire(Unknown Source)
at it.sauronsoftware.base64.Base64InputStream.read(Unknown Source)
at java.io.InputStream.read(Unknown Source)
at java.io.InputStream.read(Unknown Source)
at it.sauronsoftware.base64.Base64.copy(Unknown Source)
at it.sauronsoftware.base64.Base64.decode(Unknown Source)
... 3 more
You're not decoding the encryption of the original message; you're decoding the base64 encoding of the encryption original message.
Rather than passing cipher.getBytes(), you need Base64.decode(cipher).getBytes(), or, alternatively, have the method accept a base64 String and handle the decoding in the method.
Also, there's no need to base64 decode the decrypted content. It will already be in the original encoding. In other words, just return new String(textDecrypted).
The flow you're trying to do is: plaintext -> encrypted content -> base64 -> encrypted content -> plaintext. The flow you're doing is text -> encrypted content -> base64 -> decrypted base64 (nonsense) -> base64 (nonsense)