Hallo,
ich bin soeben auf ein mir bisher unbekanntes Problem gestoßen und mir ist leider auch nach 1 1/2 Stunden Recherche immer noch unklar, wie ich dieses am besten lösen kann.
Mein Ziel ist es, ein Message Objekt im Internet zu verschicken. Das an sich ist kein Problem. Wo es aber hapert, ist die Verschlüsselung. Denn ich möchte dieses Objekt, welches später sensible Daten enthalten wird eben logischerweise nicht im Klartext durch die halbe Welt schicken, sondern mit AES und einem Passwort verschlüsseln.
Die beteiligten Komponenten sind folgende: ClientHandler, EncryptionUtility, Message, ReadWriteAES, Testclient
Hier der entsprechende Quellcode:
Testclient.java
ClientHandler.java
Hierbei ist der auskommentierte Teil im try-catch Block die alte Klartext-Kommunikation, welche ich nicht mehr nutzen möchte, sondern nun eben auf die Kommunikation über das EncryptionUtility umsteigen will.
EncryptionUtility.java
Wie man sieht, ist diese Klasse dafür zuständig, Methoden zum Lesen und Schreiben von verschlüsselten Message Objekten bereitzustellen.
ReadWriteAES.java
Diese Klasse übernimmt das eigentliche Verschlüsseln der Streams und ich denke hier liegt auch irgendwo das Problem, auch wenn die Exception etwas anderes besagt.
Message.java
Diese Klasse enthält die Informationen, welche zwischen Server und Client übertragen werden sollen. Momentan nur ein Kommando und entsprechende Parameter, jedoch wird hier noch erweitert. Dies ist jedoch im Sachzusammenhang erst einmal unwichtig.
Wenn ich nun den Server starte, initialisiert sich dieser wie gewünscht, stellt auch die Datenbankverbindung her und tut erst einmal alles wie geplant. Anschließend starte ich den Testclient, um eben die Kommunikation zu testen. Folgende Exception wird serverseitig in der Konsole ausgegeben, sobald der Client das Broadcast Kommando abgesendet hat:
Dies deutet laut meinen Recherchen darauf hin, dass ich irgendwie das Objekt clientseitig falsch in den Stream schreibe bzw. den Stream falsch öffne. Aber ich finde den Fehler einfach nicht.
Wie oben bereits kurz erwähnt könnte ich mir gut vorstellen, dass der Fehler hier irgendwo in der Verschlüsselung sitzt.
Ich wäre euch sehr dankbar, wenn ihr mir bei meinem Problem weiterhelfen könntet.
Bei Fragen zum Code stehe ich jederzeit gerne zur Verfügung, ich habe jedoch versucht, alles so genau wie möglich zu eröffnen.
Euer UNIX
ich bin soeben auf ein mir bisher unbekanntes Problem gestoßen und mir ist leider auch nach 1 1/2 Stunden Recherche immer noch unklar, wie ich dieses am besten lösen kann.
Mein Ziel ist es, ein Message Objekt im Internet zu verschicken. Das an sich ist kein Problem. Wo es aber hapert, ist die Verschlüsselung. Denn ich möchte dieses Objekt, welches später sensible Daten enthalten wird eben logischerweise nicht im Klartext durch die halbe Welt schicken, sondern mit AES und einem Passwort verschlüsseln.
Die beteiligten Komponenten sind folgende: ClientHandler, EncryptionUtility, Message, ReadWriteAES, Testclient
Hier der entsprechende Quellcode:
Testclient.java
Java:
package de.mrpixeldream.dreamcode.im.client.test;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStreamReader;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.Socket;
import org.jasypt.util.text.StrongTextEncryptor;
import de.mrpixeldream.dreamcode.im.server.io.Message;
public class Testclient
{
static final String globalPassword = "mein_passwort";
static Socket testserver;
static String serverResponse = "";
static StrongTextEncryptor encryptor = new StrongTextEncryptor();
public static void main(String[] args)
{
try
{
encryptor.setPassword(globalPassword);
System.out.println("Connecting to test server...");
testserver = new Socket("127.0.0.1", 22558);
System.out.println("Initializing streams...");
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream objectOut = new ObjectOutputStream(bos);
System.out.println("Waiting for ACK flag from server...");
serverResponse = new BufferedReader(new InputStreamReader(testserver.getInputStream())).readLine();
System.out.println("Response: " + serverResponse);
if (serverResponse.equals("ACK"))
{
System.out.println("Logging in with test account...");
System.out.println("Out: LOGIN MrPixelDream hier_steht_ein_passwort");
PrintWriter writer = new PrintWriter(new OutputStreamWriter(testserver.getOutputStream()));
writer.println("LOGIN MrPixelDream hier_steht_ein_passwort");
writer.flush();
serverResponse = new BufferedReader(new InputStreamReader(testserver.getInputStream())).readLine();
System.out.println("Response: " + serverResponse);
System.out.println("Broadcasting test message...");
System.out.println("Out: BROADCAST test");
Message testBroadcast = new Message("BROADCAST", new String[] { "test" });
objectOut.writeObject(testBroadcast);
objectOut.flush();
ReadWriteAES.encode(bos.toByteArray(), testserver.getOutputStream(), globalPassword);
ByteArrayInputStream bis = new ByteArrayInputStream(ReadWriteAES.decode(testserver.getInputStream(), globalPassword));
ObjectInputStream objectIn = new ObjectInputStream(bis);
Message testResponse = (Message) objectIn.readObject();
System.out.println("Response: " + testResponse);
}
else
{
System.out.println("Something went wrong, server was not answering as expected!");
}
}
catch (Exception ex)
{
ex.printStackTrace();
}
}
}
ClientHandler.java
Java:
package de.mrpixeldream.dreamcode.im.server;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Scanner;
import de.mrpixeldream.dreamcode.im.server.io.EncryptionUtility;
public class ClientHandler extends Thread {
Socket client;
String id;
Scanner input;
PrintWriter output;
EncryptionUtility encryptionUtil;
IMServer parent;
public ClientHandler(Socket client, IMServer parent)
{
this.client = client;
this.parent = parent;
try
{
this.input = new Scanner(client.getInputStream());
this.output = new PrintWriter(client.getOutputStream());
}
catch (IOException e)
{
e.printStackTrace();
}
}
public void setEncryptionUtility(EncryptionUtility encUtil)
{
this.encryptionUtil = encUtil;
}
@Override
public void run()
{
String msg = "";
do
{
msg = "";
try
{
msg = encryptionUtil.receiveEncrypted(client);
//System.out.println(msg);
//msg = input.nextLine();
this.parent.getCommandHandler().handleCommand(msg, client, this);
}
catch (Exception e)
{
this.parent.log("Failed to read line from client! Logging out...", LogLevel.ERROR);
e.printStackTrace();
msg = "LOGOUT";
}
} while (!msg.equalsIgnoreCase("LOGOUT"));
this.logout();
}
public void setID(String id)
{
this.id = id;
}
public String getID()
{
return this.id;
}
public EncryptionUtility getEncryptionUtility()
{
return this.encryptionUtil;
}
public void logout()
{
//this.output.println("Logging you out!");
this.parent.doLogout(client.getInetAddress());
}
}
Hierbei ist der auskommentierte Teil im try-catch Block die alte Klartext-Kommunikation, welche ich nicht mehr nutzen möchte, sondern nun eben auf die Kommunikation über das EncryptionUtility umsteigen will.
EncryptionUtility.java
Java:
package de.mrpixeldream.dreamcode.im.server.io;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
import org.jasypt.util.text.StrongTextEncryptor;
import de.mrpixeldream.dreamcode.im.server.thirdparty.ReadWriteAES;
public class EncryptionUtility {
String password = "";
StrongTextEncryptor encryptor;
public EncryptionUtility(String masterPassword) {
this.password = masterPassword;
this.encryptor = new StrongTextEncryptor();
this.encryptor.setPassword(password);
}
public void sendEncrypted(String message, Socket target) {
try
{
byte[] buffer;
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream objectStream = new ObjectOutputStream(bos);
String[] splitMsg = message.split(" ");
String command = splitMsg[0];
String[] args = new String[splitMsg.length - 1];
for (int i = 0; i < args.length; i++)
{
args[i] = splitMsg[i + 1];
}
Message messageObject = new Message(command, args);
objectStream.writeObject(messageObject);
buffer = bos.toByteArray();
ReadWriteAES.encode(buffer, target.getOutputStream(), password);
objectStream.close();
}
catch (Exception ex)
{
ex.printStackTrace();
}
}
public String receiveEncrypted(Socket target) throws Exception {
String plainCommand = "";
byte[] buffer = ReadWriteAES.decode(target.getInputStream(), password);
ByteArrayInputStream bis = new ByteArrayInputStream(buffer);
ObjectInputStream objectStream = new ObjectInputStream(bis);
Message messageObject = (Message) objectStream.readObject();
plainCommand += messageObject.getCommand();
for (String arg : messageObject.getArgs())
{
plainCommand += arg;
}
return plainCommand;
}
}
Wie man sieht, ist diese Klasse dafür zuständig, Methoden zum Lesen und Schreiben von verschlüsselten Message Objekten bereitzustellen.
ReadWriteAES.java
Java:
package de.mrpixeldream.dreamcode.im.server.thirdparty;
import java.io.*;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;
import javax.crypto.*;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
public class ReadWriteAES
{
public static void encode( byte[] bytes, OutputStream out, String pass ) throws Exception
{
Cipher c = Cipher.getInstance( "AES" );
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
KeySpec spec = new PBEKeySpec(pass.toCharArray(), "5500".getBytes(), 65536, 256);
SecretKey tmp = factory.generateSecret(spec);
SecretKey secret = new SecretKeySpec(tmp.getEncoded(), "AES");
Key k = new SecretKeySpec( secret.getEncoded(), "AES" );
//System.out.println(new String(k.getEncoded()));
c.init( Cipher.ENCRYPT_MODE, k );
OutputStream cos = new CipherOutputStream( out, c );
cos.write( bytes );
cos.flush();
cos.close();
}
public static byte[] decode( InputStream is, String pass ) throws Exception
{
Cipher c = Cipher.getInstance( "AES" );
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
KeySpec spec = new PBEKeySpec(pass.toCharArray(), "5500".getBytes(), 65536, 256);
SecretKey tmp = factory.generateSecret(spec);
SecretKey secret = new SecretKeySpec(tmp.getEncoded(), "AES");
Key k = new SecretKeySpec( secret.getEncoded(), "AES" );
//System.out.println(new String(k.getEncoded()));
c.init( Cipher.DECRYPT_MODE, k );
ByteArrayOutputStream bos = new ByteArrayOutputStream();
CipherInputStream cis = new CipherInputStream( is, c );
for ( int b; (b = cis.read()) != -1; )
{
bos.write( b );
}
System.out.println("miau");
bos.flush();
bos.close();
cis.close();
return bos.toByteArray();
}
public static void encode(byte[] bytes, FileOutputStream out, byte[] secKey) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeySpecException, InvalidKeyException, IOException
{
Cipher c = Cipher.getInstance( "AES" );
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
KeySpec spec = new PBEKeySpec(new String(secKey).toCharArray(), "5500".getBytes(), 65536, 256);
SecretKey tmp = factory.generateSecret(spec);
SecretKey secret = new SecretKeySpec(tmp.getEncoded(), "AES");
Key k = new SecretKeySpec( secret.getEncoded(), "AES" );
c.init( Cipher.ENCRYPT_MODE, k );
OutputStream cos = new CipherOutputStream( out, c );
cos.write( bytes );
cos.close();
}
public static byte[] decode(InputStream is, byte[] secKey) throws InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException, IOException, InvalidKeySpecException
{
Cipher c = Cipher.getInstance( "AES" );
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
KeySpec spec = new PBEKeySpec(new String(secKey).toCharArray(), "5500".getBytes(), 65536, 256);
SecretKey tmp = factory.generateSecret(spec);
SecretKey secret = new SecretKeySpec(tmp.getEncoded(), "AES");
Key k = new SecretKeySpec( secret.getEncoded(), "AES" );
c.init( Cipher.DECRYPT_MODE, k );
ByteArrayOutputStream bos = new ByteArrayOutputStream();
CipherInputStream cis = new CipherInputStream( is, c );
for ( int b; (b = cis.read()) != -1; )
{
bos.write( b );
}
cis.close();
return bos.toByteArray();
}
}
Diese Klasse übernimmt das eigentliche Verschlüsseln der Streams und ich denke hier liegt auch irgendwo das Problem, auch wenn die Exception etwas anderes besagt.
Message.java
Java:
package de.mrpixeldream.dreamcode.im.server.io;
import java.io.Serializable;
public class Message implements Serializable
{
private static final long serialVersionUID = 1L;
String command;
String[] args;
public Message(String command, String[] args)
{
this.command = command;
this.args = args;
}
public String getCommand()
{
return this.command;
}
public String[] getArgs()
{
return this.args;
}
}
Diese Klasse enthält die Informationen, welche zwischen Server und Client übertragen werden sollen. Momentan nur ein Kommando und entsprechende Parameter, jedoch wird hier noch erweitert. Dies ist jedoch im Sachzusammenhang erst einmal unwichtig.
Wenn ich nun den Server starte, initialisiert sich dieser wie gewünscht, stellt auch die Datenbankverbindung her und tut erst einmal alles wie geplant. Anschließend starte ich den Testclient, um eben die Kommunikation zu testen. Folgende Exception wird serverseitig in der Konsole ausgegeben, sobald der Client das Broadcast Kommando abgesendet hat:
Code:
ERROR: Failed to read line from client! Logging out...
java.io.StreamCorruptedException: invalid stream header: 9CC03627
at java.io.ObjectInputStream.readStreamHeader(ObjectInputStream.java:802)
at java.io.ObjectInputStream.<init>(ObjectInputStream.java:299)
at de.mrpixeldream.dreamcode.im.server.io.EncryptionUtility.receiveEncrypted(EncryptionUtility.java:67)
at de.mrpixeldream.dreamcode.im.server.ClientHandler.run(ClientHandler.java:53)
Dies deutet laut meinen Recherchen darauf hin, dass ich irgendwie das Objekt clientseitig falsch in den Stream schreibe bzw. den Stream falsch öffne. Aber ich finde den Fehler einfach nicht.
Wie oben bereits kurz erwähnt könnte ich mir gut vorstellen, dass der Fehler hier irgendwo in der Verschlüsselung sitzt.
Ich wäre euch sehr dankbar, wenn ihr mir bei meinem Problem weiterhelfen könntet.
Bei Fragen zum Code stehe ich jederzeit gerne zur Verfügung, ich habe jedoch versucht, alles so genau wie möglich zu eröffnen.
Euer UNIX