Asymmetrische Verschlüsselungsalgorithmen

Mujahiddin

Top Contributor
Ich habe ungefähr das Prinzip von asymmetrischen Verschlüsselungsprinzipen verstanden (anhand von RSA)
Dazu hätte ich mal einige Fragen:

Es gibt ein KeyPair, Public und Private Key. Wie ist das nun zu verstehen? Ich verschlüssele mit dem Public Key und entschlüssele mit dem Private Key. Das bedeutet doch aber, dass der Public Key im Private Key irgendwie drinsteckt.
Und inwiefern ist es zu verstehen, dass man den Public Key "nicht geheimhalten" braucht? Den kann also jeder einsehen, es hilft ihm bloß nichts, die Informationen zu entschlüsseln (zumindest nicht ohne enormen Aufwand)
Wo wir beim nächsten Punkt wären:
Angenommen, jemand hat einen Algorithmus, der brute force Private Keys generiert, bis irgendwann die Information erfolgreich entschlüsselt ist. 1. in welchem Zeitfenster befinden wir uns (abhängig von der Länge der Keys)

2. ist es möglich, die Keys in regelmäßigen Zeitabständen zu ändern? Ich habe nämlich folgendes Problem: 1 Server, n Clients. Ich dachte erst, dass jeder Client die Public Keys bekommt und der Server (ich) hat den private Key. Wenn nun der Client mir irgendwelche Informationen geben will, verschlüsselt er mittels Public Key und ich kann die Information einlesen, da ich ja den private Key habe.

Nun stellt sich aber die Frage: Wie schicke ich Informationen zurück? Das beschäftigt mich schon ziemlich lange. Eine Möglichkeit wäre ja, dass ich für jeden Client ein eigenes KeyPair habe [erfordert Registrierung]. Das fände ich optimal, jedoch stellt sich die Fragen, wie findet der Austausch dieser Information statt? Unverschlüsselt? Das wäre ja wieder anfällig.

Eine andere Möglichkeit wäre, dass ich zwei KeyPairs habe, eine für Server-Client und eine für Client-Server - das wäre aber total absurd. Dann wäre der Private Key vom zweiten KeyPair ja wieder für jeden zugänglich und somit hätte die Verschlüsselung keinen Sinn.

Ich hoffe, mir kann jemand mehr darüber sagen.

Vielen Dank im Voraus.
 
G

Gast2

Gast
Ich habe ungefähr das Prinzip von asymmetrischen Verschlüsselungsprinzipen verstanden (anhand von RSA)
Dazu hätte ich mal einige Fragen:

Es gibt ein KeyPair, Public und Private Key. Wie ist das nun zu verstehen? Ich verschlüssele mit dem Public Key und entschlüssele mit dem Private Key. Das bedeutet doch aber, dass der Public Key im Private Key irgendwie drinsteckt.
Und inwiefern ist es zu verstehen, dass man den Public Key "nicht geheimhalten" braucht? Den kann also jeder einsehen, es hilft ihm bloß nichts, die Informationen zu entschlüsseln (zumindest nicht ohne enormen Aufwand)
Wo wir beim nächsten Punkt wären:
Angenommen, jemand hat einen Algorithmus, der brute force Private Keys generiert, bis irgendwann die Information erfolgreich entschlüsselt ist. 1. in welchem Zeitfenster befinden wir uns (abhängig von der Länge der Keys)

2. ist es möglich, die Keys in regelmäßigen Zeitabständen zu ändern? Ich habe nämlich folgendes Problem: 1 Server, n Clients. Ich dachte erst, dass jeder Client die Public Keys bekommt und der Server (ich) hat den private Key. Wenn nun der Client mir irgendwelche Informationen geben will, verschlüsselt er mittels Public Key und ich kann die Information einlesen, da ich ja den private Key habe.

Nun stellt sich aber die Frage: Wie schicke ich Informationen zurück? Das beschäftigt mich schon ziemlich lange. Eine Möglichkeit wäre ja, dass ich für jeden Client ein eigenes KeyPair habe [erfordert Registrierung]. Das fände ich optimal, jedoch stellt sich die Fragen, wie findet der Austausch dieser Information statt? Unverschlüsselt? Das wäre ja wieder anfällig.

Eine andere Möglichkeit wäre, dass ich zwei KeyPairs habe, eine für Server-Client und eine für Client-Server - das wäre aber total absurd. Dann wäre der Private Key vom zweiten KeyPair ja wieder für jeden zugänglich und somit hätte die Verschlüsselung keinen Sinn.

Ich hoffe, mir kann jemand mehr darüber sagen.

Vielen Dank im Voraus.

zu 1) In der Realität sind das meist sehr große Primzahlen. Aus diesen kann man Private und Public KEy erstellen. Wenn man nun den Public Key hat muss man immer noch die Primzahlen knacken und das ist extrem zeitintensiv (Wir sprechen von wirtklich sehr großen Zahlen). SOlltest du ein Verfahren zum Errechnen von Primzahlen entwicklen, dass einen linearen Zeitaufwand hat sind nahezu alle gängigen Verschlüsselungsvarianten die darauf basieren nichts mehr Wert ;)

zu 2) Ja man braucht dann in der Tat 2 Key Pairs. Man verwendet aber idR hybride Verfahren. Heisst: Der Client generiert einen normalen Synchronen Schlüssel. Dieser wird mit dem Public Key des Server verschlüsselt und übertragen. Der Server entschlüsselt den synchronen Schlüssel und somit kennen nur die beiden Teilnehmer diesen. Mit diesem Schlüssel wird dann der rest der Kommunikation verschlüsselt. Ist auch wesentlich schneller, da asynchrone Verfahren langsamer sind.
 

FArt

Top Contributor
Eine mit dem privaten Schlüssel verschlüsselte Nachricht kann jeder mit dem öffentlichen Schlüssel entschlüsseln. Er kann aber sicher sein, dass die Nachricht vom Besitzer des privaten Schlüssels stammt.
Wenn eine Nachricht mit einem öffentlichen Schlüssel verschlüsselt wird, kann der Sender sicher sein, dass nur der Besitzer des privaten Schlüssels die Nachricht verarbeiten kann.

Wenn beide sicher sein wollen, benötigt jeder ein Schlüsselpaar.
 

Mujahiddin

Top Contributor
zu 1) In der Realität sind das meist sehr große Primzahlen. Aus diesen kann man Private und Public KEy erstellen. Wenn man nun den Public Key hat muss man immer noch die Primzahlen knacken und das ist extrem zeitintensiv (Wir sprechen von wirtklich sehr großen Zahlen). SOlltest du ein Verfahren zum Errechnen von Primzahlen entwicklen, dass einen linearen Zeitaufwand hat sind nahezu alle gängigen Verschlüsselungsvarianten die darauf basieren nichts mehr Wert ;)

zu 2) Ja man braucht dann in der Tat 2 Key Pairs. Man verwendet aber idR hybride Verfahren. Heisst: Der Client generiert einen normalen Synchronen Schlüssel. Dieser wird mit dem Public Key des Server verschlüsselt und übertragen. Der Server entschlüsselt den synchronen Schlüssel und somit kennen nur die beiden Teilnehmer diesen. Mit diesem Schlüssel wird dann der rest der Kommunikation verschlüsselt. Ist auch wesentlich schneller, da asynchrone Verfahren langsamer sind.

Okay grob hab ich das verstanden.
Synchron = symmetrisch? Also DES statt RSA (anhand von Algorithmen)

Ich lass mir das später noch genauer durch den Kopf gehen, aber zwei Sachen sind mir noch aufgefallen:

Dieser Schlüssel, der vom Client generiert wurde, kann der geändert werden oder bleibt der für alle Zeiten gleich? [meine Vermutung ist, dass bei jedem Applikationstart der Client einen neuen Schlüssel generiert und verschlüsselt überträgt, also jeder Socket hat einen eigenen Schlüssel]

Naja ich meine wir befinden uns im Bereich von ~65536 (ist ja Richtlinie, und Standardprovider von javax.crypto benutzt diese Primzahl), kann man diese ganzen Primzahlen nicht in ein Array hinterlegen, somit spart man sich das erneute Rechnen und hat enorm kurze Zeiten, die Information zu entschlüsseln?
 

FArt

Top Contributor
Ihr werft zwei Sachen zusammen, die zwar was miteinander zu tun haben, aber nicht das selbe sind.

Das eine ist Public/Private Key. Das ander ist, was man damit anstellen kann, z.B. PKI oder verschlüsselte Kommunikation.

Für ein Zertifiakt bleibt der private Schlüssel immer der selbe, und somit auch sein Partner.

Bei manchen Verfahren werden aber keine festen Schlüssel benötigt. Da können (z.B. für eine verschlüsselte Kommunikation) Schlüssel on the fly generiert und ausgetauscht werden.

Konkret noch mal zu den Fragen oben:
1.) sehr lang mit heutigen technischen Möglichkeiten (bei starker Verschlüsselung). Die Schlüssellänge steigt mit der Zeit, da ja auch die technischen Möglichkeiten für Brute-Force mit besserer Hardware besser werden. Es gibt aber immer auch Unterschiede zu den verschiedenen Verfahren (Algorithmen) und evlt. zur Stärke der Passphrase.
2.) Das kann man mit Zertifikaten (Serverzertifikat) erreichen.
 
Zuletzt bearbeitet:
G

Gast2

Gast
Dieser Schlüssel, der vom Client generiert wurde, kann der geändert werden oder bleibt der für alle Zeiten gleich? [meine Vermutung ist, dass bei jedem Applikationstart der Client einen neuen Schlüssel generiert und verschlüsselt überträgt, also jeder Socket hat einen eigenen Schlüssel]

Genauso

Ihr werft zwei Sachen zusammen, die zwar was miteinander zu tun haben, aber nicht das selbe sind.

Ich werfe gar nichts zusammen denn ich habe doch darauf hingewiesen, dass man in der Praxis eben die Daten nicht mit dem asynchronen Verfahren überträgt, sondern das asynchron Verfahren genutzt wird um einen synchronen Session Key zu übertragen. Das nennt man wie gesagt Hybride Verschlüsselung.

Autentifizierung funktioniert auch mitels asynchroner Verschlüsselung aber hier brauchen eben beide Seiten ein Public/Private Key Pair. (So z.B. bei OpenSSH Authorization mittels Keys anstattt Username/Password).
 
G

Gast2

Gast
@kappes:
Bitte, es heißt asymmetrisch bzw. symmetrisch - nicht "synchron".

Sry klar ... manchmal tippt man und denkt gar nicht soviel drüber nach ^^
Typischer Fall vor sieht den Wald vor lauter Bäumen nicht. Wenn man ständig mit dem Kram arbeitet denkt man eben nicht mehr soviel drüber nach *g*
 

Mujahiddin

Top Contributor
Alles klar.

Noch zwei Fragen zum Schluss:

1. Was spricht dagegen, KeyPair in regelmäßigen Zeitabständen zu ändern? Der Private Key bleibt dann immer bei mir, die Applikation würde dann immer nach dem Public Key fragen und damit verschlüsseln.

2. Welcher Algorithmus wäre geeignet für symmetrische Verschlüsselung? Ich kenne DES, aber am besten wäre etwas, das 1. sicher und 2. performant/schnell ist.
 
G

Gast2

Gast
Weil der Private Key eigetnlich signiert sein sollte, damit dein gegenüber auch weiss, zu wem der Public Key gehört. Ich jkönnte ja auch hingehen und sagen das ist mein Public Key und ich bin Google...

Dazu gibt es Certification Authorities wie z.B: Thawte. bei denen bekommt man zertifitierte KeyPairs. Da kann dann jeder der deinen Public Key hat sicher sein, dass du der bist für den du dich ausgibst. Erst dann erstellt der Client den symetrischen Key und kommuniziert mit dir.
 

timbeau

Gesperrter Benutzer
Grundsätzlich spricht nichts dagegen und wird meines Wissens in etwa bei HTTPS(SSL) verwendet. Da wird auch jedes mal ein neues Paar generiert. Wenn du immer deinen PrivateKey behalten willst ist aber mit den üblichen Verfahren nur ein PublicKey möglich. Möglich wäre aber immer ein neuer PrivateKey, das kriegt ja keiner von außen mit.
 

Mujahiddin

Top Contributor
Weil der Private Key eigetnlich signiert sein sollte, damit dein gegenüber auch weiss, zu wem der Public Key gehört. Ich jkönnte ja auch hingehen und sagen das ist mein Public Key und ich bin Google...

Dazu gibt es Certification Authorities wie z.B: Thawte. bei denen bekommt man zertifitierte KeyPairs. Da kann dann jeder der deinen Public Key hat sicher sein, dass du der bist für den du dich ausgibst. Erst dann erstellt der Client den symetrischen Key und kommuniziert mit dir.

Also wenn ich meine Applikation schreibe, baut diese erst eine Verbindung mit www.thawte.de auf und frägt nach dem Public Key von "Public Key von Mujahiddin"? Oder wie läuft das dann ab?
Und wie ich sehe, kostet das auch einiges.
Wenn sich der Client mit dem Server verbindet, kann doch am Anfang des Austausches der Public Key unverschlüsselt übertragen werden? Ich meine, meine URL bleibt ja gleich.. Ist ja nicht so, dass plötzlich www.google.de zu einer anderen URL führt und ich müsste sicherstellen, dass es auch wirklich DAS Google ist.
 

timbeau

Gesperrter Benutzer
Du machst dir zuviele Gedanken. Nutze eins der verwendeten Verfahren. Was hast du denn vor, dass die Verschlüsselung so wichtig zu sein scheint?
 

Mujahiddin

Top Contributor
Du machst dir zuviele Gedanken. Nutze eins der verwendeten Verfahren. Was hast du denn vor, dass die Verschlüsselung so wichtig zu sein scheint?

Ich denke mir nur, wenn ich was mache, dann will ichs richtig machen, und nicht, dass irgendwann die Erleuchtung kommt "Wow ich hätte es lieber so machen sollen... Jetzt muss ich 1000 Zeilen Code umschreiben!"
Ist mir schon oft passiert, Sachen wie
Code:
"" + someDouble;
statt
Code:
String.valueOf( someDouble );
(Beim ersten wird ein StringBuilder erzeugt)
Lieber gleich von Anfang an richtig, sodass später nur kleine Schönheitskorrekturen gemacht werden müssen.
 
G

Gast2

Gast
Erklär mal was du machen willst! Wenns nur um eine verschlüsselte Verbindung geht brauchst du kein signiertes Schlüsselpaar.

Geht es dir darum, dass ein Client nicht nur eine verschlüsselte verbindung mit deinem Server aufbauen will sondern die Identität des Servers automatisch mit belegt wird brauchst du ein signiertes Paar.

Das sind grundsätzlich 2 Dinge die man mit den Schlüsselpaaren machen kann!
 

Mujahiddin

Top Contributor
Ich will nur, dass Nachrichten verschlüsselt übertragen werden, also "Marley" keinen Zugriff darauf hat. Signiert muss es nicht sein, ich sehe darin auch keinen wirklichen Nutzen für mich.

Ich habe bereits einige Bücher zur Kryptografie, benutze großteils "Kryptografie" von Klaus Schmeh.
 

FArt

Top Contributor
Suchhifle für den Einstieg bzgl. Literatur und Tutorials:

1. public key / private key
2. signieren vs. verschlüsseln
2. Zertifikate
3. PKI
4. SSL Kommunikation

Es gibt unter den Themen Berührungspunkte. Du kannst "es" nur richtig machen, wenn du "es" überhaupt kennst. Bisher wir nur unzureichend auf die Gemeinsamkeiten und Berührungspunkte der einzelnen Punkte eingegangen.

Beispiel: eine SSL Verbindung kann mit zwei oder mit einem Zertifikat sicher aufgebaut werden. Es geht auch ganz ohne, dann ist die Verbindung aber verwundbar gegenüber man-in-the-middle Attacken.

Beispiel: eMails können mit einem persönlichen Zertifikat signiert werden, damit der Empfänger sicher sein kann, von wem die eMail stammt.

Beispiel: eine Nachricht kann verschlüsselt werden.

Beispiel: unverschlüsselte Nachrichten können über eine verschlüsselte, also abgesicherte Leitung verschickt werden.

...
 
T

troll

Gast
ich wollte es gestern eigentlich schon schreiben ... aber wie ich sehe wurde es ja doch noch etwas lustiger ...


1) wie kommt ihr vom einfachen CipherStream auf SSL-zertifikate ?
das hat so eher indirekt was mit ein ander zu tun ... außerdem ist SSL für das was TO will echt overkill ...
auch fällt das signieren und damit die kosten flach wenns nur für die eigene app sein soll

2) TO : DES ist lange veraltet und überholt ... und selbst TripleDES/DESede bietet trotz der größeren schlüsselbreite weniger sicherheit da es erfolgreiche angriffe gegen den DES-algo gibt ...
nutze hier lieber AES

3) alles was du brauchst kannst du mit java.security.* und javax.crypto.* zusammenbauen ... stichworte "Cipher", "CipherInput/OutputStream", "Cipher.update(byte[])" und "Cipher.doFinal(byte[])" ...

4) warum denkst du von wegen man müsste den RSA-schlüssel wechseln ? und wer oder was hindert dich daran es einfach zu tun ? anstatt den public key fest einzucoden kannst du dir diesen doch jedes mal vom server holen ... und der kann jedes mal einen neuen erzeugen ...
dafür gibt es das PKI verfahren : der client fragt beim verbindungs-aufbau den server direkt nach dessen public key ... ob dieser nun konstant ist oder bei jeder verbindung neu gewählt wird ist dabei egal ... nun generiert der client ein session-master , verschlüsselt es mit dem public key und sendet es an den server ... der entschlüsselt das wieder und nutzt nun den selben session-master wie der client woraus dann beide den eigentlichen key erzeugen und diese für die symetrische verbindung nutzen ... FERTIG AUS ENDE

5) warum es jetzt unbedingt SO WICHTIG sein soll irgendwelche banalen daten zu verschlüsseln bleibt mir verborgen ... ist aber in den meisten fällen bei kleineren hooby-apps völlig unwichtig da sich keiner die mühe machen wird da irgendwo groß was mitschneiden zu wollen ...
 

Mujahiddin

Top Contributor
AES funktioniert nicht, es wird ständig "InvalidKeyException" geworfen.

Hier ein KSKB:

Java:
import java.security.Key;
import java.util.Arrays;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.xml.bind.DatatypeConverter;


public class AESTest {
	
	public static void main(String[] args) throws Exception {
		aes();
	}
	
	static Cipher cipher;
	
	static void aes() throws Exception {
		KeyGenerator keygen = KeyGenerator.getInstance( "AES" );
		keygen.init( 256 );
		Key key = keygen.generateKey();
		System.out.println( DatatypeConverter.printBase64Binary( key.getEncoded() ) );
		
		cipher = Cipher.getInstance( "AES" );
		byte[] decrypted = "Hallo Welt!".getBytes();
		byte[] encrypted = encrypt( decrypted, key );
		byte[] decrypted2 = decrypt( encrypted, key ); // InvalidKeyException
		System.out.println( Arrays.equals( decrypted, decrypted2 ) );
	}
	
	static byte[] encrypt(byte[] decrypted, Key key) throws Exception {
		cipher.init( Cipher.ENCRYPT_MODE, key );
		return cipher.doFinal( decrypted );
	}
	
	static byte[] decrypt(byte[] encrypted, Key key) throws Exception {
		cipher.init( Cipher.DECRYPT_MODE, key );
		return cipher.doFinal( encrypted );
	}
}
 
T

troll

Gast
AES funktioniert nicht, es wird ständig "InvalidKeyException" geworfen.
...
[JAVA=19]keygen.init( 256 );[/code]

ist auch völlig klar warum : AES256 wird so nicht direkt von java unterstützt ...
das liegt an irgendwelchen US-export-rechten ... desshalb wird "von haus aus" auch nur AES128 voll garantiert
AES192 und AES256 werden nur nach installation der "unlimited crypto strength policy" verfügbar ... was aber zur folge hat das jeder der es nutzen will diese auch bei sich installiert ...

natürlich könntest du auch z.B. BouncyCastle als provider nehmen und diesen mitliefern ... aber nur um AES256 zu nutzen ist das dann doch wieder overkill ... AES128 reicht für eigene hobby-software dicke aus ...


ich arbeite zur zeit an einer kleinen crypto-suite mit der man einfach via RSA einen AES key austauschen und dann eine gesicherte verbindung mit AES nutzen kann ...
bin noch am arbeiten ... aber der RSA-key-exchange sieht so aus :

Java:
import java.io.*;
import java.net.*;
import java.security.*;
import javax.crypto.*;
public class RSAHelper
{
	private InputStream in;
	private OutputStream out;
	private PrivateKey privateKey;
	private PublicKey publicKey;
	private Cipher privateCipher;
	private Cipher publicCipher;
	public RSAHelper(InputStream in, OutputStream out)
	{
		this.in=in;
		this.out=out;
	}
	public boolean generateKeyPair()
	{
		try
		{
			KeyPairGenerator kpg=KeyPairGenerator.getInstance("RSA");
			kpg.initialize(2048);
			KeyPair kp=kpg.generateKeyPair();
			privateKey=kp.getPrivate();
			publicKey=kp.getPublic();
			return true;
		}
		catch(Exception e)
		{
			e.printStackTrace();
			return false;
		}
	}
	public boolean sendPublicKey()
	{
		try
		{
			ObjectOutputStream oos=new ObjectOutputStream(out);
			oos.writeObject(publicKey);
			return true;
		}
		catch(Exception e)
		{
			e.printStackTrace();
			return false;
		}
	}
	public boolean receivePublicKey()
	{
		try
		{
			ObjectInputStream ois=new ObjectInputStream(in);
			publicKey=(PublicKey)ois.readObject();
			return true;
		}
		catch(Exception e)
		{
			e.printStackTrace();
			return false;
		}
	}
	public boolean initCipher()
	{
		try
		{
			if(privateKey!=null)
			{
				privateCipher=Cipher.getInstance("RSA/ECB/PKCS1PADDING");
				privateCipher.init(Cipher.UNWRAP_MODE, privateKey);
			}
			publicCipher=Cipher.getInstance("RSA/ECB/PKCS1PADDING");
			publicCipher.init(Cipher.WRAP_MODE, publicKey);
			return true;
		}
		catch(Exception e)
		{
			e.printStackTrace();
			return false;
		}
	}
	public boolean sendSecretKey(SecretKey secretKey)
	{
		try
		{
			byte[] wrappedKey=publicCipher.wrap(secretKey);
			out.write((wrappedKey.length>>4));
			out.write(wrappedKey);
			return true;
		}
		catch(Exception e)
		{
			e.printStackTrace();
			return false;
		}
	}
	public SecretKey receiveSecretKey()
	{
		try
		{
			int length=in.read();
			byte[] wrappedKey=new byte[(length<<4)];
			in.read(wrappedKey);
			return (SecretKey)privateCipher.unwrap(wrappedKey, "AES", Cipher.SECRET_KEY);
		}
		catch(Exception e)
		{
			e.printStackTrace();
			return null;
		}
	}
}

verwendet wird die klasse wie folgt (beispiel mit PipedStreams)
Java:
import java.io.*;
import javax.crypto.*;
public class Test
{
	public static void main(String[] args) throws Exception
	{
		PipedInputStream pi1=new PipedInputStream();
		PipedInputStream pi2=new PipedInputStream();
		PipedOutputStream po1=new PipedOutputStream(pi1);
		PipedOutputStream po2=new PipedOutputStream(pi2);
		RSAHelper rsa1=new RSAHelper(pi1, po2);
		RSAHelper rsa2=new RSAHelper(pi2, po1);
		KeyGenerator keyGen=KeyGenerator.getInstance("AES");
		keyGen.init(128);
		SecretKey key1=keyGen.generateKey();

		rsa1.generateKeyPair();
		rsa1.sendPublicKey();
		rsa2.receivePublicKey();
		rsa1.initCipher();
		rsa2.initCipher();
		rsa2.sendSecretKey(key1);
		SecretKey key2=rsa1.receiveSecretKey();

		System.out.println(key1.equals(key2));
	}
}

wie an der ausgabe zu erkennen ist stimmt der "empfange" AES-key "key2" mit dem erzeugten original "key1" überein und kann so in einem Cipher z.B. mit AES/CBC/PKCS5PADDING oder ähnlichem genutzt werden ...
wenn ich mit der AES-klasse fertig bin werde ich diese auch posten mit einem kleinen kompletten beispiel wie dann eine gesicherte verbindung aufgebaut wird und darüber sicher daten übertragen werden ...


hoffe das hilft soweit erstmal etwas weiter
 

Mujahiddin

Top Contributor
:) Ich habe schon die "Grundlagen" gesetzt. Das mit 256 wusste ich nicht. Er schmeißt halt eine "InvalidKeyException" beim cipher.init statt InvalidParameterException bei keygen.init, was ziemlich verwirrend war.

Ich mach das ohne ObjectStreams, ich mach das mit reinem PrintStream und Base64, also in etwa so:

(KeyPair beim Start des Servers initialisieren und als "Singleton" speichern)
Java:
KeyPairGenerator kpg = KeyPairGenerator.getInstance( "RSA" );
KeyPair keyPair = kpg.generateKeyPair();

(Decrypter Cipher initialisieren)
Java:
final Cipher rsaDecrypter = null;
try {
	rsaDecrypter = Cipher.getInstance( "RSA" );
	rsaDecrypter.init( Cipher.DECRYPT_MODE, getKeyPair().getPrivate() );
} catch( NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException e ) {
	e.printStackTrace();
	System.exit( -938 );
	return; // was hält eigentlich die Allgemeinheit hiervon? Ansonsten kommt Compile-Error "rsaDecrypter might not be initialized"
}

(Public Key senden)
Java:
PrintStream out = new PrintStream( socket.getOutputStream(), true, "UTF-8" );
byte[] pubKey = getKeyPair().getPublic().getEncoded();
String s = DatatypeConverter.printBase64Binary( pubKey );
out.println( s );

(Symmetric key auslesen und instantiieren)
Java:
Scanner sc = new Scanner( socket.getInputStream(), "UTF-8" );
String next = sc.nextLine();
byte[] symmetric = DatatypeConverter.parseBase64Binary( next );
synchronized( rsaDecrypter ) {
	symmetric = rsaDecrypter.doFinal( symmetric );
	System.out.println( symmetric.length );
}
Key key = new SecretKeySpec( symmetric, "AES" );



Client:

Java:
KeyGenerator keygen = KeyGenerator.getInstance( "AES" );
keygen.init( 128 );

Key secretKey = keygen.generateKey();

final Socket socket = new Socket( host, port );
final PrintStream out = new PrintStream( socket.getOutputStream(), true, "UTF-8" );
final Scanner sc = new Scanner( socket.getInputStream() );

String s = sc.nextLine();
byte[] publicKey = DatatypeConverter.parseBase64Binary( s );

Cipher c = Cipher.getInstance( "RSA" );
Key key = KeyFactory.getInstance( "RSA" ).generatePublic( new X509EncodedKeySpec( publicKey ) );

c.init( Cipher.ENCRYPT_MODE, key );

byte[] symmetric = c.doFinal( secretKey.getEncoded() );
String str = DatatypeConverter.printBase64Binary( symmetric );
out.println( str );
 
T

troll

Gast
hier mal noch zusätzlich der code der AES-klasse
Java:
import java.io.*;
import java.security.*;
import javax.crypto.*;
import javax.crypto.spec.*;
public class AESHelper
{	
	private SecretKey key;
	private Cipher encryptCipher;
	private Cipher decryptCipher;
	public AESHelper(SecretKey key)
	{
		this.key=key;
	}
	public static SecretKey generateKey()
	{
		try
		{
			KeyGenerator keyGen=KeyGenerator.getInstance("AES");
			keyGen.init(128);
			return keyGen.generateKey();
		}
		catch(Exception e)
		{
			e.printStackTrace();
			return null;
		}
	}
	public boolean initCipher()
	{
		try
		{
			IvParameterSpec iv=new IvParameterSpec(key.getEncoded());
			encryptCipher=Cipher.getInstance("AES/CBC/PKCS5PADDING");
			encryptCipher.init(Cipher.ENCRYPT_MODE, key, iv);
			decryptCipher=Cipher.getInstance("AES/CBC/PKCS5PADDING");
			decryptCipher.init(Cipher.DECRYPT_MODE, key, iv);
			return true;
		}
		catch(Exception e)
		{
			e.printStackTrace();
			return false;
		}
	}
	public byte[] encryptRAW(byte[] input)
	{
		try
		{
			return crypt(input, encryptCipher);
		}
		catch(Exception e)
		{
			e.printStackTrace();
			return null;
		}
	}
	public String encryptString(String input)
	{
		byte[] rawinput=input.getBytes();
		byte[] rawoutput=encryptRAW(rawinput);
		if(rawoutput==null)
		{
			return null;
		}
		return binaryToHexString(rawoutput);
	}
	public byte[] decryptRAW(byte[] input)
	{
		try
		{
			return crypt(input, decryptCipher);
		}
		catch(Exception e)
		{
			e.printStackTrace();
			return null;
		}
	}
	public String decryptString(String input)
	{
		byte[] rawinput=hexStringToBinary(input);
		byte[] rawoutput=decryptRAW(rawinput);
		if(rawoutput==null)
		{
			return null;
		}
		return new String(rawoutput);
	}
	private byte[] crypt(byte[] data, Cipher cipher) throws Exception
	{
		ByteArrayInputStream bais=new ByteArrayInputStream(data);
		ByteArrayOutputStream baos=new ByteArrayOutputStream();
		int blockSize=cipher.getBlockSize();
		int outputSize=cipher.getOutputSize(blockSize);
		byte[] input=new byte[blockSize];
		byte[] output=new byte[outputSize];
		int inLength=0;
		boolean finished=false;
		while(!finished)
		{
			inLength=bais.read(input);
			if(inLength==blockSize)
			{
				int outLength=cipher.update(input, 0, blockSize, output);
				baos.write(output, 0, outLength);
			}
			else
			{
				finished=true;
			}
		}
		if(inLength>0)
		{
			output=cipher.doFinal(input, 0, inLength);
		}
		else
		{
			output=cipher.doFinal();
		}
		baos.write(output);
		return baos.toByteArray();
	}
	private String binaryToHexString(byte[] input)
	{
		StringBuilder stringBuilder=new StringBuilder(input.length*2);
		for(byte b : input)
		{
			int i=b&0xFF;
			if(i<16)
			{
				stringBuilder.append("0");
			}
			stringBuilder.append(Integer.toHexString(i));
		}
		return stringBuilder.toString();
	}
	private byte[] hexStringToBinary(String input)
	{
		byte[] output=new byte[input.length()/2];
		for(int i=0; i<output.length; i++)
		{
			output[i]=(byte)Integer.parseInt(input.substring(i*2, (i*2)+2), 16);
		}
		return output;
	}
}

verwendung ist auch hier wieder analog zur RSA-klasse (da kann übrigens das "import java.net.*;" komplett raus) ...
natürlich sollte man im gegensatz zu meinem beispiel die rückgaben der methoden prüfen anstatt einfach schritt für schritt durchzugehen ... und eventuell auch das exception-handling noch mal sauber neu machen ... aber so passt das alles in allem zusammen ...

ich hab bewusst einmal RAW-bytes und einmal String als möglichkeit implementiert mit selbstverständlich unterschiedlichen return-types und die String-variante auf die RAW-bytes oben draufgesetzt wie man es auch oft in der SE-api findet ...
man kann sich nun über diese entscheidung streiten und hätte sicher auch noch direkt das versenden und empfangen über die streams mit einbauen können (hatte dazu erst noch 2 methoden mehr drin) ... aber ich wollte lediglich die klasse zum verschlüsseln selbst bereitstellen und nicht in den netzwerk-layer eingreifen ... dann hätte ich gleich CipherIn/OutputStream nutzen können
 
Ähnliche Java Themen

Ähnliche Java Themen

Neue Themen


Oben