У меня есть сервер, к которому подключаются сокеты для отправки данных через входные потоки, эти данные шифруются с помощью AES/GCM/NoPadding
в классе под названием Cryptographer. На сервере есть потоки, которые содержат функциональные возможности для подключенных клиентов, и каждый поток представлен в классе ConnectionThread, этот класс содержит ссылку на класс cryptograhper, который инициализируется в классе сервера.
Проблема:
Когда я посылаю свою первую команду, она работает просто отлично, никаких проблем нет. Но почему-то, когда я посылаю вторую команду, if дает следующую трассировку стека:
javax.crypto.AEADBadTagException: Tag mismatch!
at java.base/com.sun.crypto.provider.GaloisCounterMode.decryptFinal(GaloisCounterMode.java:595)
at java.base/com.sun.crypto.provider.CipherCore.finalNoPadding(CipherCore.java:1116)
at java.base/com.sun.crypto.provider.CipherCore.fillOutputBuffer(CipherCore.java:1053)
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.decrypt(Cryptographer.java:53)
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)
Это классы, упомянутые в трассировке стека
Криптограф
package com.company.security;
import javax.crypto.*;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
public class Cryptographer {
private Key secretKey;
private GCMParameterSpec gcmParameterSpec;
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();
byte[] IV = new byte[12];
gcmParameterSpec = new GCMParameterSpec(16 * 8, IV);
System.arraycopy(secretBytes, 0, secret, 0, secretBytes.length);
secretKey = new SecretKeySpec(secret, "AES");
}
/**
* Encrypt data.
* @param data to encrypt
* @return encrypted data
*/
public byte[] encrypt(byte[] data) {
try {
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
cipher.init(Cipher.ENCRYPT_MODE, secretKey, gcmParameterSpec);
byte[] encrypted = cipher.doFinal(data);
return encrypted;
} catch (InvalidKeyException | BadPaddingException
| IllegalBlockSizeException | NoSuchPaddingException
| NoSuchAlgorithmException | InvalidAlgorithmParameterException e) {
e.printStackTrace();
return null;
}
}
/**
* Decrypt data.
* @param data to decrypt
* @return decrypted data
*/
public byte[] decrypt(byte[] data) {
try {
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
cipher.init(Cipher.DECRYPT_MODE, secretKey, gcmParameterSpec);
return cipher.doFinal(data);
} catch (InvalidKeyException | BadPaddingException
| IllegalBlockSizeException | NoSuchPaddingException
| NoSuchAlgorithmException | InvalidAlgorithmParameterException e) {
e.printStackTrace();
return null;
}
}
}
Считыватель
package com.company.client;
import com.company.FileLoader;
import com.company.client.helpers.ClientFileHelper;
import com.company.client.workers.MessageSender;
import com.company.security.Cryptographer;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
public class Reader implements Runnable {
private InputStream inputStream;
private ClientFileHelper fileHelper;
private Cryptographer cryptographer;
private FileLoader fileLoader;
private BufferedReader bufferedReader;
private MessageSender messageSender;
private boolean isActive = true;
private boolean isReceivingFile = false;
public Reader(BufferedReader bufferedReader, MessageSender messageSender, InputStream inputStream) {
this.bufferedReader = bufferedReader;
this.messageSender = messageSender;
this.inputStream = inputStream;
cryptographer = new Cryptographer();
}
@Override
public void run() {
while (isActive) {
try {
int count;
byte[] buffer;
if(!isReceivingFile) {
buffer = new byte[inputStream.available()];
} else {
buffer = new byte[inputStream.available()];
}
while ((count = inputStream.read(buffer)) > 0)
{
byte[] decrypted = cryptographer.decrypt(buffer);
if(!isReceivingFile) {
handleInput(new String(decrypted));
} else {
if(fileHelper.getFileBytes().length == 0) {
fileHelper.setFileBytes(decrypted);
} else {
fileHelper.saveFile();
isReceivingFile = false;
}
}
}
} catch (IOException e) {
e.printStackTrace();
break;
}
}
}
/**
* Handle the user input form the console.
* @param input user input from console
*/
private void handleInput(String input) {
try {
if (input.equals("PING")) { // If we get a PING message we send back a PONG message.
messageSender.send("PONG");
} else if (input.contains("FILE")) {
setupFileAccept(input);
isReceivingFile = true;
} else {
System.out.println(input);
}
} catch (Exception ex) {
isActive = false;
}
}
/**
* Setup the file helper for the client that's going to receive a file.
* @param line command
*/
private void setupFileAccept(String line) {
String[] args = line.split(" ");
if(args[0].equals("FILE")) {
fileHelper = new ClientFileHelper(args[1], Integer.valueOf(args[2]));
}
}
}
ConnectionThread также имеет похожую функцию чтения, которая выглядит следующим образом:
while (isActive) {
try {
int count;
byte[] buffer;
if(!isReceivingFile) {
buffer = new byte[inputStream.available()];
} else {
buffer = fileHelper.getFileBytes();
}
while ((count = inputStream.read(buffer)) > 0)
{
byte[] decrypted = server.cryptographer.decrypt(buffer);
if(!isReceivingFile) {
getInput(new String(decrypted));
} else {
fileHelper.setFileBytes(decrypted);
// bytes received, now we can send the file!
if(fileHelper.sendToReceiver()) {
writeToClient(fileHelper.getReceiverName()
+ " received " + fileHelper.getFilename());
fileHelper = null;
}
}
}
} catch (IOException e) {
e.printStackTrace();
break;
}
}
В этом случае просто предположим, что класс Server имеет свойство cryptographer, правильно инициализированное, что всегда имеет место.
Я предполагаю, что где-то значение делает что-то не так, но я не уверен. Я совершенно не понимаю, что мне делать, чтобы решить эту проблему. Может кто-нибудь помочь мне указать на ошибки и предложить возможные решения, чтобы решить эту проблему? Моя java версия 12.0.1