Итак, я пытаюсь создать веб-сокет, который отправляет данные, которые будут закодированы в base64, а затем зашифрованы с помощью AES, полученный байтовый массив будет отправлен через поток между сокетами и сервером. Это работает нормально, пока я не пытаюсь сделать определенную команду, которая дает мне BadPaddingException
. Все фрагменты кода, использующие класс Cryptographer
, используют один и тот же класс с одинаковым секретом.
В веб-сокетах есть потоки для каждого соединения для чтения и записи данных. Все эти потоки используют один и тот же cyptographer.
Функция, которая дает мне исключение, когда результат расшифровывается в потоке соединения, записывается так в классе MessageSender
, Исключение будет происходят при расшифровке результата sendFile () :
package com.company.client.workers;
import com.company.security.Cryptographer;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.nio.file.Files;
import java.util.Base64;
public class MessageSender {
private PrintWriter writer;
private OutputStream outputStream;
private Cryptographer cryptographer;
public MessageSender(OutputStream outputStream) {
this.outputStream = outputStream;
this.writer = new PrintWriter(this.outputStream);
cryptographer = new Cryptographer();
}
/**
* Sends a message to the PrintWriter.
* @param message to send.
*/
public void send(String message) {
try {
String b64 = Base64.getEncoder().encodeToString(message.getBytes());
byte[] bytes = cryptographer.getData(b64.getBytes(), 0);
outputStream.write(bytes, 0, bytes.length);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* Send a file over the server.
* @param receiver to send to
* @param path location of file
*/
public void sendFile(String receiver, String path) {
File file = new File(path);
try {
byte[] bytes = Files.readAllBytes(file.toPath());
// The encrypted result of this will throw the exception once the server tries to decrypt it.
String message = "SFC " + receiver + " " + bytes.length + " " + getFileName(path);
send(message);
outputStream.write(cryptographer.getData(bytes, 0), 0, bytes.length);
} catch (IOException e) {
e.printStackTrace();
}
}
private String getFileName(String path) {
String[] parts = path.split("/");
return parts[parts.length-1];
}
}
Следующая функция запуска будет считывать данные из входного потока (это сокет). И он также вызовет класс cryptographer для шифрования и дешифрования данных, это тот же класс, который также будет использоваться на стороне клиента с отдельным экземпляром. В случае этого исключения IsReceiving
по-прежнему имеет значение false
. Вот класс Cryptographer
:
package com.company.security;
import javax.crypto.*;
import javax.crypto.spec.SecretKeySpec;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
public class Cryptographer {
private Key secretKey;
public Cryptographer() {
byte[] secret = new byte[16]; // 128 bit is 16 bytes, and AES accepts 16 bytes, and a few others.
byte[] secretBytes = "secret".getBytes();
System.arraycopy(secretBytes, 0, secret, 0, secretBytes.length);
secretKey = new SecretKeySpec(secret, "AES");
}
/**
* Get data from either encryption and decryption.
* @param data to encrypt or decrypt
* @param mode 0 to encrypt en other numbers to decrypt
* @return result data as byte array format.
*/
public byte[] getData(byte[] data, int mode) {
Cipher c;
try {
c = Cipher.getInstance("AES");
} catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
e.printStackTrace();
return null;
}
try {
if(mode == 0) { // 0 is encrypt mode.
c.init(Cipher.ENCRYPT_MODE, secretKey);
} else { // other numbers are decrypt mode.
c.init(Cipher.DECRYPT_MODE, secretKey);
}
} catch (InvalidKeyException e) {
e.printStackTrace();
return null;
}
try {
return c.doFinal(data);
} catch (IllegalBlockSizeException | BadPaddingException e) {
e.printStackTrace();
return null;
}
}
/**
* Decode base64 byte array.
* @param encoded encoded byte array
* @return decoded string
*/
public String decodeBaseToString(byte[] encoded) {
return new String(Base64.getDecoder().decode(encoded));
}
/**
* Decode base64 byte array.
* @param encoded encoded byte array
* @return decoded byte array
*/
public byte[] decodeBaseToBytes(byte[] encoded) {
return Base64.getDecoder().decode(encoded);
}
/**
* Encode byte array to base64.
* @param source array to encode
* @return encoded base64 byte array
*/
public byte[] encodeBase(byte[] source) {
return Base64.getEncoder().encode(source);
}
}
Трассировка стека исключений:
javax.crypto.BadPaddingException: Given final block not properly padded. Such issues can arise if a bad key is used during decryption.
at java.base/com.sun.crypto.provider.CipherCore.unpad(CipherCore.java:975)
at java.base/com.sun.crypto.provider.CipherCore.fillOutputBuffer(CipherCore.java:1056)
at java.base/com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:853)
at java.base/com.sun.crypto.provider.AESCipher.engineDoFinal(AESCipher.java:446)
at java.base/javax.crypto.Cipher.doFinal(Cipher.java:2208)
at com.company.security.Cryptographer.getData(Cryptographer.java:48)
at com.company.client.Reader.run(Reader.java:45)
at java.base/java.lang.Thread.run(Thread.java:835)
Exception in thread "Thread-3" java.lang.NullPointerException
at java.base/java.lang.String.<init>(String.java:623)
at com.company.client.Reader.run(Reader.java:47)
at java.base/java.lang.Thread.run(Thread.java:835)
Шифрование отлично работает с любой другой командой и также работает, когда Я не включаю имя файла в sendFile()
. Что я тут не так делаю?