#java #sockets #encryption #encoding #rsa
#java #сокеты #шифрование #кодирование #rsa
Вопрос:
Итак, я создаю приложение Java, которое использует безопасное соединение RSA для доставки данных (на данный момент я вручную кодирую методы шифрования / дешифрования, если есть библиотека или лучший способ сделать это, не стесняйтесь писать), я делаю это впервые, поэтому я нене знаю, что большая часть темы.
Это класс, который я использую как на стороне сервера, так и на стороне клиента для шифрования / дешифрования:
public class RSAEncryption extends AsymmetricalEncryption {
public RSAEncryption() {
super("RSA");
}
@Override
public byte[] encrypt(String data) {
Cipher cipher = null;
byte[] encryptedData = new byte[0];
try {
cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, super.sharedKey);
encryptedData = cipher.doFinal(data.getBytes());
encryptedData = Base64.getEncoder().encode(encryptedData);
} catch (Exception e) {
System.err.println("Error on Cipher initialization " e);
}
return encryptedData;
}
@Override
public byte[] decrypt(byte[] data) {
Cipher cipher = null;
byte[] encryptedData = null;
try {
cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, super.privateKey);
encryptedData = cipher.doFinal(Base64.getDecoder().decode(data));
} catch (Exception e) {
e.printStackTrace();
}
return encryptedData;
}
}
Класс RSAencryption асимметричный encryption, потому что я хотел бы в будущем реализовать разные протоколы шифрования.
Это соединение на стороне сервера:
public class Connection extends Thread {
private Socket connection;
private ObjectInputStream ois;
private ObjectOutputStream oos;
private AsymmetricalEncryption encryption;
public Connection(Socket connection) {
try {
this.connection = connection;
this.ois = new ObjectInputStream(connection.getInputStream());
this.oos = new ObjectOutputStream(connection.getOutputStream());
encryption = new RSAEncryption();
// sending the public key
oos.writeUTF(encryption.encodeBase64PublicKey(encryption.publicKey));
oos.flush();
// read sharedKey
String encodedKey = ois.readUTF();
encryption.setSharedKey(encryption.decodeBase64PublicKey(encodedKey, "RSA"));
this.start();
} catch (Exception e) {
e.printStackTrace();
}
}
public Connection(Socket connection, String encryptionMethod) {
try {
//this.connection = connection;
this.ois = new ObjectInputStream(connection.getInputStream());
this.oos = new ObjectOutputStream(connection.getOutputStream());
encryption = AsymmetricalEncryptionFactory.getAsymmetricalEncryption(encryptionMethod);
// sending the public key
oos.writeUTF(encryption.encodeBase64PublicKey(encryption.publicKey));
oos.flush();
// read sharedKey
String encodedKey = ois.readUTF();
encryption.setSharedKey(encryption.decodeBase64PublicKey(encodedKey, encryptionMethod));
this.start();
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void run() {
boolean isRunning = true;
while(isRunning){
try {
byte[] encryptedStringRequest = ois.readAllBytes();
System.out.println("Encrypted Request: " encryptedStringRequest);
byte[] stringRequest = encryption.decrypt(encryptedStringRequest);
System.out.println("Request: " new String(stringRequest));
System.out.println("When you fix the bug remeber to decomment the following two lines of Connection");
//Request request = Request.parse(this, new JSONObject(stringRequest));
//RequestQueue.RequestQueue().enqueue(request);
} catch (Exception e) {
e.printStackTrace();
isRunning = false;
}
}
}
public void response(Response response){
try {
byte[] encryptedStringResponse = encryption.encrypt(response.toJSONString().getBytes());
oos.write(encryptedStringResponse);
oos.flush();
} catch (IOException e) {
System.err.println("Error during response sending " e);
}
}
}
Это соединение на стороне клиента:
public class Connection {
private Socket connection;
private ObjectInputStream ois;
private ObjectOutputStream oos;
private AsymmetricalEncryption encryption;
public Connection(Socket connection) {
try {
this.connection = connection;
this.oos = new ObjectOutputStream(connection.getOutputStream());
this.ois = new ObjectInputStream(connection.getInputStream());
encryption = new RSAEncryption();
// sending the public key
oos.writeUTF(encryption.encodeBase64PublicKey(encryption.publicKey));
oos.flush();
// read sharedKey
String encodedKey = ois.readUTF();
encryption.setSharedKey(encryption.decodeBase64PublicKey(encodedKey, "RSA"));
} catch (Exception e) {
e.printStackTrace();
}
}
public Connection(Socket connection, String encryptionMethod) {
try {
//this.connection = connection;
this.ois = new ObjectInputStream(connection.getInputStream());
this.oos = new ObjectOutputStream(connection.getOutputStream());
encryption = AsymmetricalEncryptionFactory.getAsymmetricalEncryption(encryptionMethod);
// sending the public key
oos.writeUTF(encryption.encodeBase64PublicKey(encryption.publicKey));
oos.flush();
// read sharedKey
String encodedKey = ois.readUTF();
encryption.setSharedKey(encryption.decodeBase64PublicKey(encodedKey, encryptionMethod));
} catch (Exception e) {
e.printStackTrace();
}
}
public Response request(Request request){
try {
byte[] encryptedStringResponse = encryption.encrypt(request.toJSONString());
oos.write(encryptedStringResponse);
oos.flush();
byte[] response = ois.readAllBytes();
response = encryption.decrypt(response);
return new Response(new JSONObject(new String(response)));
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
На данный момент я не знаю, почему, но когда я отправляю данные с клиента на сервер, сервер сообщает, что установлено новое соединение, но зависает, ничего не делая, в то время как на стороне клиента говорится, что данные были отправлены.
Комментарии:
1. В
encrypt
методе зашифрованный текст преобразуется в строку с кодировкой символов. Это искажает данные. Кроме того, открытый текст кодируется в Base64 перед шифрованием, что не имеет особого смысла. Кодирование Base64 должно выполняться после шифрования, что также лучше подходит дляdecrypt
метода (где зашифрованный текст декодируется по Base64 перед расшифровкой).2. Итак, я должен отправить byte[] вместо string и переместить шифрование перед кодированием, верно? Знаете ли вы какой-либо другой лучший способ установления безопасных соединений в java?
3. Спасибо, но я уже устранил проблему и начал использовать
byte[] encryptedStringRequest = (byte[]) ois.readObject();
на сервере иoos.writeObject(encryptedStringResponse); oos.reset();
на клиенте, в любом случае в этом проекте может быть много разных клиентов, закодированных на разных языках, которые используют разные фреймворки, например nodejs, в текущем состоянии использованиеObjectOutputStream
не будет работать с разными языками, должен ли я использоватьBufferedReader
вместо?
Ответ №1:
Лучший способ сделать это — использовать TLS. В Java вы можете создавать серверные и клиентские фабрики сокетов с помощью SSLContext
.
Было бы неэтично пытаться разработать свою собственную криптосистему и применять ее к любым данным, которые другие люди доверяют вам защищать. Но если вы делаете это для развлечения, в качестве учебного упражнения и не собираетесь выполнять с ним какую-либо реальную работу, вы можете максимально использовать свои усилия, пытаясь узнать, как работают настоящие криптосистемы.
RSA работает относительно медленно, и объем данных, которые могут быть зашифрованы в каждой операции, ограничен выбранным вами размером ключа RSA. Его лучше всего использовать для шифрования другого ключа для симметричного шифра, такого как AES. Именно так RSA используется в любой широко принятой схеме шифрования. Затем большие объемы данных приложения могут быть быстро зашифрованы с помощью симметричного шифрования с использованием ключа, обмениваемого через RSA.
Комментарии:
1. Хорошо, проблема в том, что клиенты, которые подключаются к моему серверу, могут быть закодированы на разных языках, обычно javascript с node, используя ваш подход, могу ли я это сделать?
2. @AndreaDattero Да, каждая платформа будет поддерживать TLS, поэтому вам не нужно самостоятельно внедрять протокол для каждой клиентской платформы.
3. И сам TLS является примером, который использует криптографию с открытым ключом, такую как RSA, для обмена закрытым ключом для использования в симметричном шифре, таком как AES.