I am creating a program that encrypts data. De and encryption works perfectly fine. But when decrypting an image I get an error. I am very greatful for any help!
When trying to decrypt my image using the cat_encrypt method like this:
String encrypted = encrypt.cat_encrypt(getFileContent(jf.getSelectedFile()), pass);
Files.writeString(Path.of(jf.getSelectedFile().getPath()), encrypted);
I get the following error:
java.lang.IllegalArgumentException: Input byte array has incorrect ending byte at 54808
Here is my encryption method:
public static String cat_encrypt(String text, String pass) {
try {
MessageDigest messageDigest = MessageDigest.getInstance("MD5");
Key key = new SecretKeySpec(messageDigest.digest(pass.getBytes(UTF_8)), "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(ENCRYPT_MODE, key);
byte[] encrypted = cipher.doFinal(text.getBytes(UTF_8));
byte[] encoded = Base64.getEncoder().encode(encrypted);
return new String(encoded, UTF_8);
} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | IllegalBlockSizeException |
BadPaddingException e) {
throw new RuntimeException("Cannot encrypt", e);
}
}
And this is my decryption method:
public static String cat_decrypt(String text, String pass) {
try {
MessageDigest messageDigest = MessageDigest.getInstance("MD5");
Key key = new SecretKeySpec(messageDigest.digest(pass.getBytes(UTF_8)), "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(DECRYPT_MODE, key);
byte[] decoded = Base64.getDecoder().decode(text.getBytes(UTF_8));
byte[] decrypted = cipher.doFinal(decoded);
return new String(decrypted, UTF_8);
} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | IllegalBlockSizeException |
BadPaddingException e) {
try {
sendError((e.getClass().getSimpleName()));
}catch (Exception sE){e.printStackTrace();}
throw new RuntimeException("Cannot decrypt", e);
}
}
I call the decrypt method like this:
String decrypted = encrypt.cat_decrypt(getFileContent(jf.getSelectedFile()), pass);
The full error code:
Exception in thread "AWT-EventQueue-0" java.lang.IllegalArgumentException: Input byte array has incorrect ending byte at 54808
at java.base/java.util.Base64$Decoder.decode0(Base64.java:876)
at java.base/java.util.Base64$Decoder.decode(Base64.java:566)
Related
I'm using the RSA algorithm to encrypt some numbers, then store those numbers in a bit array within a text file. The numbers are encrypted and added to the text file just fine, but when I try and decrypt them using the method below, I get "BadPaddingException: Message is larger than modulus" this error in my console. I'm pretty new to encryption and this is really throwing me off.
Cipher cipher;
KeyPair pair;
public byte[] encryptData(String data) throws BadPaddingException, IllegalBlockSizeException {
try{
KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA");
pair = keyPairGen.generateKeyPair();
PublicKey publicKey = pair.getPublic();
cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
cipher.update(data.getBytes());
cipher.doFinal();
}
catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | BadPaddingException | IllegalBlockSizeException ex){
ex.printStackTrace();
}
return cipher.doFinal();
}
writing data into a file
String nums = request.getParameter("numbers");
String password = (String) session.getAttribute("password");
String filename = (password +".txt");
File dir = new File("/volume");
dir.mkdir();
File myfile = new File(dir, filename);
System.out.println("filename: " + filename);
FileOutputStream output;
try {
if (myfile.isFile()){
output = new FileOutputStream(myfile, true);
}
else{
output = new FileOutputStream(myfile);
}
byte[] encryptednums = encryptData(nums);
output.write(encryptednums);
output.close();
} catch (BadPaddingException | IllegalBlockSizeException | IOException e) {
e.printStackTrace();
}
Reading the file
public byte[] bytesFileReader(File filename){
try {
return Files.readAllBytes(Paths.get(String.valueOf(filename)));
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
Decryption
HttpSession session = request.getSession();
KeyPair pair = (KeyPair) session.getAttribute("keypair");
Cipher cipher = (Cipher) session.getAttribute("cipher");
String password = (String) session.getAttribute("password");
String filename = (password +".txt");
File dir = new File("/volume");
dir.mkdir();
File myfile = new File(dir, filename);
byte[] encryptedBytes = bytesFileReader(myfile);
try {
cipher.init(Cipher.DECRYPT_MODE, pair.getPrivate());
byte[] decipheredText = cipher.doFinal(encryptedBytes);
System.out.println(new String(decipheredText, StandardCharsets.UTF_8));
} catch (InvalidKeyException | BadPaddingException | IllegalBlockSizeException e) {
e.printStackTrace();
}
}
Probably worth noting that this is all taking place across two java servlets.
There is one main issue and some minor issues in your code. Let's start will a minor one: when converting a string to a byte array and vice versa all conversions have to use a fixed encoding (see your encryptData method):
cipher.update(data.getBytes());
The main issue is the plaintext size limitation when doing RSA encryption. You can calculate the maximum plaintext size
with this formula
1) RSA keysize divided by 8 - e.g. 2048 / 8 = 256 bytes
2) PKCS1 padding takes 11 bytes
3) maximum of plaintext bytes: 256 - 11 = 245 bytes
In my example code I'm using a RSA keypair with size of (UNSECURE) 512 bits = 64 bytes, minus 11 bytes for padding there are 53 bytes that can get encrypted. The first round will run like expected, the next encryption with 54 bytes of data runs into the
IllegalBlockSizeException: Data must not be longer than 53 bytes
As your code has only the methods but does not show what data is written I can only assume that you try to decrypt all written data in one run - there is a high chance for failure.
The reason why my code runs into the error (and yours not) is the update- and double (!) final-call (beware I left out the catching that you maybe need):
change:
cipher.update(data.getBytes());
cipher.doFinal();
return cipher.doFinal();
to:
return cipher.doFinal(data.getBytes(StandardCharsets.UTF_8));
output:
BadPaddingException: Message is larger than modulus
encrypting 12345678901234567890123456789012345678901234567890123
encryptedData length: 64
decryptedData: 12345678901234567890123456789012345678901234567890123
encrypting 123456789012345678901234567890123456789012345678901234
Exception in thread "main" javax.crypto.IllegalBlockSizeException: Data must not be longer than 53 bytes
Security warning: this code uses an UNSECURE 512 bit long RSA key (use minimum 2048 bit) and encrypts with RSA padding PKCS1 that is vulnerable - please use OEAP padding!.
code:
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import java.nio.charset.StandardCharsets;
import java.security.*;
public class Main {
static KeyPair keyPair;
public static void main(String[] args) throws NoSuchAlgorithmException, BadPaddingException, IllegalBlockSizeException, InvalidKeyException, NoSuchPaddingException {
System.out.println("BadPaddingException: Message is larger than modulus");
// https://stackoverflow.com/questions/64722370/why-do-i-get-badpaddingexception-message-is-larger-than-modulus-when-decrypti
keyPair = generateKeyPair(512); // ### do not use RSA keys with 512 bits length, minimum 2048 bits
// 512 bit = 64 byte - 11 byte for padding = maximal 53 bytes data to encrypt
System.out.println("\nencrypting 53 chars");
String data53Chars = "12345678901234567890123456789012345678901234567890123";
//String data53Chars = "12345678901234567890";
System.out.println("encrypting " + data53Chars);
byte[] encryptedData = encryptData(data53Chars);
System.out.println("encryptedData length: " + encryptedData.length);
String decryptedData = decryptData(encryptedData);
System.out.println("decryptedData: " + decryptedData);
// now 54 bytes
System.out.println("\nencrypting 54 chars");
String data54Chars = "123456789012345678901234567890123456789012345678901234";
System.out.println("encrypting " + data54Chars);
encryptedData = encryptData(data54Chars);
System.out.println("encryptedData length: " + encryptedData.length);
decryptedData = decryptData(encryptedData);
System.out.println("decryptedData: " + decryptedData);
}
public static byte[] encryptData(String data) throws BadPaddingException, IllegalBlockSizeException, NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException {
PublicKey publicKey = keyPair.getPublic();
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
return cipher.doFinal(data.getBytes(StandardCharsets.UTF_8));
}
public static String decryptData(byte[] encryptedBytes) throws InvalidKeyException, BadPaddingException, IllegalBlockSizeException, NoSuchPaddingException, NoSuchAlgorithmException {
byte[] decipheredText = new byte[0];
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.DECRYPT_MODE, keyPair.getPrivate());
decipheredText = cipher.doFinal(encryptedBytes);
return new String(decipheredText, StandardCharsets.UTF_8);
}
static KeyPair generateKeyPair(int keyLength) throws NoSuchAlgorithmException {
final String KEY_ALGORITHM = "RSA";
KeyPairGenerator keygen = KeyPairGenerator.getInstance(KEY_ALGORITHM);
keygen.initialize(keyLength, new SecureRandom());
KeyPair keyPair = keygen.generateKeyPair();
return keyPair;
}
}
I've followed a few examples and when trying to implement AES/GCM/NoPadding referenced here: https://www.strongauth.com/samplecode/GCM.java I am unable to encrypt any text that contains special characters (i.e. ø).
Ultimately it fails inside of doFinal with
javax.crypto.ShortBufferException: Output buffer must be (at least) 30 bytes long
but it seems like I must be doing something wrong. What am I missing?
Simple POC:
public class Example {
private static final String CIPHER_TRANSFORM = "AES/GCM/NoPadding";
public static void main(String[] args) {
String key = generateKey("AES", 256, "seed");
encryptText("text containing a ø character", key, "TOKENTOKENTOKENTOKEN", "AES");
}
private static String generateKey(String alg, int size, String seed) {
try {
SecureRandom securerandom = SecureRandom.getInstance("SHA1PRNG");
securerandom.setSeed(seed.getBytes("UTF-8"));
KeyGenerator kg = KeyGenerator.getInstance(alg);
kg.init(size, securerandom);
SecretKey sk = kg.generateKey();
return new String(Base64.getEncoder().encode(sk.getEncoded()), "UTF-8");
}
catch (UnsupportedEncodingException | NoSuchAlgorithmException ex) {
System.err.println(ex);
}
return null;
}
private static String encryptText(String PLAINTEXT, String PLAINTEXTKEY, String TOKEN, String alg) {
try {
// Create SecretKey & Cipher
SecretKeySpec sks = new SecretKeySpec(Base64.getDecoder().decode(PLAINTEXTKEY), alg);
Cipher cipher = Cipher.getInstance(CIPHER_TRANSFORM);
// Setup byte arrays
byte[] input = PLAINTEXT.getBytes("UTF-8");
byte[] tkb = TOKEN.getBytes("UTF-8");
byte[] iv = new byte[12];
System.arraycopy(tkb, 4, iv, 0, 12);
cipher.init(Cipher.ENCRYPT_MODE, sks, new GCMParameterSpec(128, iv));
cipher.updateAAD(tkb);
byte[] opbytes = new byte[cipher.getOutputSize(PLAINTEXT.length())];
// Perform crypto
int ctlen = cipher.update(input, 0, input.length, opbytes);
ctlen += cipher.doFinal(opbytes, ctlen);
byte[] output = new byte[ctlen];
System.arraycopy(opbytes, 0, output, 0, ctlen);
return new String(Base64.getEncoder().encode(output), "UTF-8");
}
catch (InvalidAlgorithmParameterException | UnsupportedEncodingException |
IllegalBlockSizeException | BadPaddingException | InvalidKeyException |
NoSuchAlgorithmException | NoSuchPaddingException | ShortBufferException ex) {
System.err.println(ex);
}
return null;
}
}
Your issue is this line:
byte[] opbytes = new byte[cipher.getOutputSize(PLAINTEXT.length())];
The length of a string in UTF-8 runes is not always the same as the length of the underlying byte array. You should be using the length of input here, not PLAINTEXT.
I have to interface with a system written in java that encrypts data using the following java method:
public final void rsaEncrypt(String data, String filePath) {
try {
Base64.Encoder encoder = Base64.getEncoder();
PublicKey pubKey = readKeyFromFile("/" + Constants.PUBLIC_KEY_FILE_NAME, filePath);
Cipher cipher = Cipher.getInstance(Constants.RSA_INSTANCE);
cipher.init(Cipher.ENCRYPT_MODE, pubKey);
byte[] cipherData = cipher.doFinal(data.getBytes("UTF-8"));
writeToFile(Constants.ENCRYPTED_STRING_FILE_NAME, filePath, encoder.encodeToString(cipherData));
} catch (BadPaddingException | InvalidKeyException | NoSuchPaddingException | NoSuchAlgorithmException
| IllegalBlockSizeException | UnsupportedEncodingException e) {
if (LOG.isErrorEnabled())
LOG.error("Error encrypting String.", e);
throw new EncryptionException("Error encrypting data", e);
}
}
My code is written in c++ using openssl:
std::string prv =
"-----BEGIN RSA PRIVATE KEY-----\n"
// cut key data
"-----END RSA PRIVATE KEY-----\n";
BIO *bio = BIO_new_mem_buf((void*)prv.c_str(), -1);
RSA* rsaPrivKey = PEM_read_bio_RSAPrivateKey(bio, NULL, NULL, NULL);
if (!rsaPrivKey)
printf("ERROR: Could not load PRIVATE KEY! PEM_read_bio_RSAPrivateKey FAILED: %s\n", ERR_error_string(ERR_get_error(), NULL));
BIO_free(bio);
// where enc[] holds the Base64 decoded data, but len becomes -1 and no data is decoded.
int len = RSA_private_decrypt(64,(unsigned char *)&enc, (unsigned char *)&dec, private_key_, RSA_PKCS1_PADDING);
I can decode data I encrypt myself with the public key, but don't seem to match the java options for this.
Any suggestions?
These is no answer here as like my comment said, it turns out the Java code was incorrect in creating their keys, which were not matching the .pem files I was given.
I am working on a Java application which requires I use two keys
generated from different Strings for encrypting and decrypting. One
String is coming from user and other is master key. I looked on net
and found a few references regarding it. I would really like some
help in knowing how to implement this. I will show what I have now.
So as you can see from code, I used some code from other stackoverflow post and modified it a bit. I just don't know how to generate the 2 keys from 2 Strings and from where I can get the SecretKey desKey used for decryption.
Code :
public class Encryption {
public void doStuff() {
String plaintext = "abc";
SecretKey k1 = generateDESkey();
SecretKey k2 = generateDESkey();
String firstEncryption = desEncryption(plaintext, k1);
String decryption = desDecryption(firstEncryption, k2);
String secondEncryption = desEncryption(decryption, k1);
System.out.println(firstEncryption);
System.out.println(decryption);
System.out.println(secondEncryption);
}
public static SecretKey generateDESkey() {
KeyGenerator keyGen = null;
try {
keyGen = KeyGenerator.getInstance("DESede");
} catch (NoSuchAlgorithmException ex) {
Logger.getLogger(Test.class
.getName()).log(Level.SEVERE, null, ex);
}
try {
assert keyGen != null;
keyGen.init(112); // key length 56
return keyGen.generateKey();
} catch (NullPointerException ex){
return null;
}
}
public static String desEncryption(String strToEncrypt, SecretKey desKey) {
try {
Cipher cipher = Cipher.getInstance("DESede/ECB/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, desKey);
return Base64.encode(cipher.doFinal(strToEncrypt.getBytes()));
} catch (NoSuchAlgorithmException | NoSuchPaddingException |
IllegalBlockSizeException | BadPaddingException |
InvalidKeyException ex) {
Logger.getLogger(Test.class
.getName()).log(Level.SEVERE, null, ex);
}
return null;
}
public static String desDecryption(String strToDecrypt, SecretKey desKey) {
try {
Cipher cipher = Cipher.getInstance("DESede/ECB/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, desKey);
return new String(cipher.doFinal(Base64.decode(strToDecrypt)));
} catch (NoSuchAlgorithmException | BadPaddingException | IllegalBlockSizeException
| InvalidKeyException | NoSuchPaddingException ex) {
Logger.getLogger(Test.class
.getName()).log(Level.SEVERE, null, ex);
}
return null;
}
}
If there is any confusion or doubts about it. Kindly let me know.
Answer for the code before it was changed.
You're trying to do DESede with only two keys instead of three.
That might generally work, but not as you've written. The problem is the padding. In the second step, you try to decrypt the ciphertext with an other key than with what it was encrypted, so the decryption will fail more than 255 out of 256 times, because the padding will be wrong (also because you use Base64 encoding where it is not necessary).
If you really want to do it, you will have to decrypt without padding and without Base64 encoding. The good thing is that the unencoded ciphertext is already a multiple of the blocksize, so there is no stopping you to use "DES/ECB/NoPadding".
public static void main(String[] args) {
// First I would like to create keys by giving Strings
SecretKey k1 = generateDESkey();
SecretKey k2 = generateDESkey();
// encryption
byte[] firstEncryption = desEncryption("plaintext".getBytes("UTF-8"), k1, false);
byte[] decryption = desDecryption(firstEncryption, k2, true);
byte[] secondEncryption = desEncryption(decryption, k1, true);
// decryption
byte[] firstDecryption = desDecryption(secondEncryption, k1, true);
byte[] encryption = desEncryption(firstDecryption, k2, true);
byte[] secondDecryption = desDecryption(encryption, k1, false);
System.out.println(new String(secondDecryption)); // plaintext
}
public static byte[] desEncryption(byte[] strToEncrypt, SecretKey desKey, boolean noPadding) {
try {
Cipher cipher = Cipher.getInstance(noPadding ? "DES/ECB/NoPadding" : "DES/ECB/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, desKey);
return cipher.doFinal(strToEncrypt);
} catch (Exception ex) {
ex.printStackTrace();
}
return null;
}
public static byte[] desDecryption(byte[] strToDecrypt, SecretKey desKey, boolean noPadding) {
try {
Cipher cipher = Cipher.getInstance(noPadding ? "DES/ECB/NoPadding" : "DES/ECB/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, desKey);
return cipher.doFinal(strToDecrypt);
} catch (Exception ex) {
ex.printStackTrace();
}
return null;
}
This is actually an equivalent implementation of DESede with two keys when the general key is constructed in this way:
SecretKey k1 = generateDESkey();
SecretKey k2 = generateDESkey();
byte[] edeKeyBytes = new byte[24];
System.arraycopy(k1.getEncoded(), 0, edeKeyBytes, 0, 8);
System.arraycopy(k2.getEncoded(), 0, edeKeyBytes, 8, 8);
System.arraycopy(k1.getEncoded(), 0, edeKeyBytes, 16, 8);
edeKey = new SecretKeySpec(edeKeyBytes, "DESede");
Cipher cipher = Cipher.getInstance("DESede/ECB/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, edeKey);
System.out.println(Base64.encode(cipher.doFinal("plaintext".getBytes("UTF-8"))));
DESede uses three keys which we will call k1, k2 and k3. All of them are concatenated into a single byte array. In your case k1 is used a second time in place of k3.
I am at my wits end with this problem. I am trying to implement an encrypted channel using RSA(4096) to deliver an AES 256 bit key using java (JRE7). The first message I send is the generated 256 AES key encrypted with the RSA public key. Upon decryption, I am getting the following error message: javax.crypto.BadPaddingException: Blocktype mismatch: 0
My decryption code:
private byte[] decryptMessage(Key key, byte[] data){
Cipher cipher;
System.out.println("Decrypting message...");
System.out.println("key: " + key.toString());
System.out.println("data: " + data);
System.out.println("data length: " + data.length);
if(key instanceof PublicKey){
//RSA public key
System.out.println("Found PublicKey");
try {
cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.DECRYPT_MODE, key);
return cipher.doFinal(data);
} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | IllegalBlockSizeException | BadPaddingException e) {
// TODO Auto-generated catch block
System.out.println("Failed to decrypt message");
e.printStackTrace();
}
}else if(key instanceof PrivateKey){
//RSA private key
System.out.println("Found PrivateKey");
try {
cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.DECRYPT_MODE, key);
byte[] results = cipher.doFinal(data);
System.out.println("Decrypted results: " + results);
return results;
} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | IllegalBlockSizeException | BadPaddingException e) {
// TODO Auto-generated catch block
System.out.println("Failed to decrypt message");
e.printStackTrace();
}
}else if(key instanceof SecretKey){
//AES secret key
try {
cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, key);
return cipher.doFinal(data);
} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | IllegalBlockSizeException | BadPaddingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}else{
System.out.println("Invalid key type");
}
return null;
}
My encryption code:
private byte[] encryptMessage(Key key, byte[] data){
Cipher cipher;
if(key instanceof PrivateKey || key instanceof PublicKey){
//RSA key
try {
cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.ENCRYPT_MODE, key);
return cipher.doFinal(data);
} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | IllegalBlockSizeException | BadPaddingException e) {
// TODO Auto-generated catch block
System.out.println("Failed to encrypt message");
e.printStackTrace();
}
}else if(key instanceof SecretKey){
//AES secret key
try {
cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, key);
return cipher.doFinal(data);
} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | IllegalBlockSizeException | BadPaddingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}else{
System.out.println("Invalid key type");
}
return null;
}
I googled and came to some similar stories(most on this website), but they were all problems with default Padding between to different devices, computer to android for example). I am getting the error when sending messages from the same computer and I have padding and ECB both set up. I went through the source code of com.sun.crypto.provider.RSACipher and sun.security.rsa.RSAPadding and found the error is being thrown in the sun.security.rsa.RSAPadding.unpadV15 method due to the second bit not being set properly. I am also using the new NIO2 classes in Java7 specifically the AsychronousSocketChannel and CompletionHandler objects so I don't know if they are causing threading errors though I highly doubt it. I found the source code at http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/com/sun/crypto/provider/RSACipher.java and http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/7-b147/sun/security/rsa/RSAPadding.java#RSAPadding.padV15%28byte[]%29 but again they didn't help me much. I have tried dropping the key size and changing the encoding/padding options of the cipher objects, but it always errors out even with padding disabled. Though it may not matter, but since I am using JRE7, there is no stable support for bouncycastle so I am using the default security provider.
Thanks in advance