Egal ob WhatsApp, Sony Playstation Network oder Twitter alle haben ihre Schwierigkeiten mit der Sicherheit. Dabei ist es sooo einfach in Java Nachrichten oder Streams zu verschlüsseln. Um nicht jedes Mal das Rad neu zu erfinden, habe ich mir eine Klasse „EasyCrypt“ geschrieben. Diese Klasse kapselt die Ver- und Entschlüsslung von Strings und Streams. Einzig die Schlüsselerzeugung und -verwaltung muss man selbst managen.
In meinem Beispiel zeige ich die Ver- und Entschlüsselung von einem String mit AES und RSA.
import java.io.InputStream; import java.io.OutputStream; import java.security.Key; import javax.crypto.Cipher; import javax.crypto.CipherInputStream; import javax.crypto.CipherOutputStream; import sun.misc.BASE64Decoder; import sun.misc.BASE64Encoder; /** * @author Alexander Gr * @see http://blog.axxg.de * */ public class EasyCrypt { private Key key = null; private String verfahren = null; /** * @param Key verwendeter Schluessel * @param verfahren bestimmt das verwendete Verschluesselungsverfahren "RSA", "AES", .... * @throws Exception */ public EasyCrypt(Key k, String verfahren) throws Exception { this.key = k; this.verfahren = verfahren; } /**Verschluesselt einen Outputstream * @param os Klartext-Outputstream * @return verschluesselter Outputstream * @throws Exception */ public OutputStream encryptOutputStream(OutputStream os) throws Exception { // integritaet pruefen valid(); // eigentliche Nachricht mit RSA verschluesseln Cipher cipher = Cipher.getInstance(verfahren); cipher.init(Cipher.ENCRYPT_MODE, key); os = new CipherOutputStream(os, cipher); return os; } /** Entschluesselt einen Inputstream * @param is verschluesselter Inputstream * @return Klartext-Inputstream * @throws Exception */ public InputStream decryptInputStream(InputStream is) throws Exception { // integritaet pruefen valid(); // Daten mit AES entschluesseln Cipher cipher = Cipher.getInstance(verfahren); cipher.init(Cipher.DECRYPT_MODE, key); is = new CipherInputStream(is, cipher); return is; } /** Verschluesselt einen Text in BASE64 * @param text Klartext * @return BASE64 String * @throws Exception */ public String encrypt(String text) throws Exception { // integritaet pruefen valid(); // Verschluesseln Cipher cipher = Cipher.getInstance(verfahren); cipher.init(Cipher.ENCRYPT_MODE, key); byte[] encrypted = cipher.doFinal(text.getBytes()); // bytes zu Base64-String konvertieren BASE64Encoder myEncoder = new BASE64Encoder(); String geheim = myEncoder.encode(encrypted); return geheim; } /** Entschluesselt einen BASE64 kodierten Text * @param geheim BASE64 kodierter Text * @return Klartext * @throws Exception */ public String decrypt(String geheim) throws Exception { // integritaet pruefen valid(); // BASE64 String zu Byte-Array BASE64Decoder myDecoder = new BASE64Decoder(); byte[] crypted = myDecoder.decodeBuffer(geheim); // entschluesseln Cipher cipher = Cipher.getInstance(verfahren); cipher.init(Cipher.DECRYPT_MODE, key); byte[] cipherData = cipher.doFinal(crypted); return new String(cipherData); } //++++++++++++++++++++++++++++++ // Validierung //++++++++++++++++++++++++++++++ private boolean valid() throws Exception{ if(verfahren == null){ throw new NullPointerException("Kein Verfahren angegeben!"); } if(key == null){ throw new NullPointerException("Keinen Key angegeben!"); } if(verfahren.isEmpty()){ throw new NullPointerException("Kein Verfahren angegeben!"); } return true; } //++++++++++++++++++++++++++++++ // Getter und Setter //++++++++++++++++++++++++++++++ public Key getKey() { return key; } public void setKey(Key key) { this.key = key; } public String getVerfahren() { return verfahren; } public void setVerfahren(String verfahren) { this.verfahren = verfahren; } }
try { // zufaelligen Schluessel erzeugen KeyGenerator keygen = KeyGenerator.getInstance("AES"); keygen.init(128); SecretKey aesKey = keygen.generateKey(); // Klasse erzeugen EasyCrypt ec = new EasyCrypt(aesKey, "AES"); // Text ver- und entschluesseln String text = "Hallo AxxG-Leser"; String geheim = ec.encrypt(text); String erg = ec.decrypt(geheim); System.out.println("Normaler Text:" + text); System.out.println("Geheimer Text:" + geheim); System.out.println("decrypt Text:" + erg); } catch (Exception e) { e.printStackTrace(); }
Normaler Text:Hallo AxxG-Leser
Geheimer Text:jEPLPYRjsY9cj+3BVv71V5GtIYTQLZXH4iRVk8JnzmM=
decrypt Text:Hallo AxxG-Leser
try { // zufaelligen Schluessel erzeugen KeyPairGenerator keygen = KeyPairGenerator.getInstance("RSA"); keygen.initialize(1024); KeyPair rsaKeys = keygen.genKeyPair(); // Klasse erzeugen EasyCrypt ecPri = new EasyCrypt(rsaKeys.getPrivate(), "RSA"); EasyCrypt ecPub = new EasyCrypt(rsaKeys.getPublic(), "RSA"); // Text ver- und entschluesseln String text = "Hallo AxxG-Leser"; String geheim = ecPri.encrypt(text); String erg = ecPub.decrypt(geheim); System.out.println("Normaler Text:" + text); System.out.println("Geheimer Text:" + geheim); System.out.println("decrypt Text:" + erg); // oder text = "Hallo AxxG-Leser"; geheim = ecPub.encrypt(text); erg = ecPri.decrypt(geheim); System.out.println("Normaler Text:" + text); System.out.println("Geheimer Text:" + geheim); System.out.println("decrypt Text:" + erg); } catch (Exception e) { e.printStackTrace(); }
Normaler Text:Hallo AxxG-Leser
Geheimer Text:UkJIpvnaojK7Xds2i6B9hEZsql37MEnWG06ameLhN7H223dZWdLlvxJMoafBXE2klUkvumBjjIGb
RWNepHskFcsgH7L6DRHybLqJwS8QH3RVEzA/WeVI3bJWx+ADTos4Sa9E6QPFIGb2CcY4u7DKdWZh
2WqW/DkIJ13/Wn0mdLk=
decrypt Text:Hallo AxxG-Leser
Normaler Text:Hallo AxxG-Leser
Geheimer Text:W0QYY2X0GPaSNUEK7my7WEm1mzRmn29pZB8KioAhWitLcxR0yR4eOfyfnXs3whHGZ1zCGZ6ZRxgi
DcASdqn2LONDbAryyF8HzogqrsIeyDwVNpIaUTHHCeWbXPjoZ1cCQv2Ui6n2Pf9gJCStBXMirHoE
7w8BFStodwOOVzsOb34=
decrypt Text:Hallo AxxG-Leser
Coole Klasse oder? Ihr könnt natürlich die Klasse weglassen oder umschreiben, aber achtet bitte auf die BASE64 Kodierung! Denn der Output von Cipher ist ein Byte-Array. Meine Meinung nach ist die Koderiung in BASE64 die eleganteste Lösung, aber ich habe auch schon andere Lösungen gesehen. Außerdem muss noch der entsprechende Schlüssel gesichert werden. Bei meinem Beispiel wird jedes Mal zufällig ein neuer Schlüssel erzeugt!
Wer nicht immer einen neuen Key erzeugen will, sollte sich unbediengt diesen Artikel von mir noch durchlesen:
Java: AES / RSA Keys lesen und schreiben (Datei)
Copyright © 2013 AxxG – Alexander Gräsel
Super Anleitung. Habe aber noch eine Frage dazu:
Um die Verschlüsselung für den Austausch zwischen mehreren Systemen zu verwenden, muss der Key aber irgendwie gespeichert werden können. Wie kann man das realisieren?
Gruß Bastian
Meine Antwort: http://blog.axxg.de/java-aes-rsa-key-file/
diese Base64 Decodierung und Encodierung macht auf Android große Probleme :/ kann man die nicht irgendwie weglassen… dabei hast du alles so gut Codiert und sogar ein ausführ Beispiel gezeigt echt Schade….
Tag, ich bin ziemlich neu in Java und habe leider nicht verstanden wie ich den Key festlegen kann. Ich würde mich über eine Antwort freuen 😀
Hey Lars,
Es gibt viele verschiedene Wege einen Schlüssel zu erzeugen.
Man kann zum Beispiel einen zufälligen Schlüssel erzeugen
// zufaelligen Schluessel erzeugen
KeyGenerator keygen = KeyGenerator.getInstance(„AES“);
keygen.init(128);
SecretKey aesKey = keygen.generateKey();
oder man kann aus einem String einen Schlüssel erzeugen:
// Das Passwort bzw der Schluesseltext
String keyStr = „geheimesPasswort“;
// byte-Array erzeugen
byte[] key = (keyStr).getBytes(„UTF-8“);
// aus dem Array einen Hash-Wert erzeugen mit MD5 oder SHA
MessageDigest sha = MessageDigest.getInstance(„MD5“);
key = sha.digest(key);
// nur die ersten 128 bit nutzen
key = Arrays.copyOf(key, 16);
// der fertige Schluessel
SecretKey aesKey = new SecretKeySpec(key, „AES“);
Ich hoffe, dass das dir weiterhilft:)
Viele Grüße
Alexander
Hey,
ich bin wirklich sehr begeistert von diesem Beitrag! Übersichtlich, knapp und leicht zu verstehen.
Eine Sache wundert mich jedoch ….
Bei dem RSA Verfahren wird doch mit dem PubKey verschlüsselt und mit dem PrivKey wieder entschlüsselt.
Im Code:
String text = „Hallo AxxG-Leser“;
String geheim = ecPri.encrypt(text);
String erg = ecPub.decrypt(geheim);
Wieso funktioniert das auch?
Gruß
Nachtrag:
text = „Hallo AxxG-Leser“;
geheim = ecPub.encrypt(text);
erg = ecPri.decrypt(geheim);
So hätte ich es auch gemacht. Aber warum geht beides?
Das ist eine gute Frage. Aber die eigentliche Frage ist doch: was ist der Unterschied zwischen den beiden Keys? Und genau das ist der Punkt, es gibt Keinen^^
Generell funktioniert RSA ja so, dass zwei Primzahlen „a“ und „b“ ein vielfaches Produkt „c“ bilden. Der eine Schlüssel besteht aus „ac“ und der andere aus „bc“. Daher ist es egal was der Privat- und was der Public-Schlüssel ist. Nur zur Vereinfachung des Verfahrens wurden die Schlüssel sogenannt. ABER zur ENTSCHLÜSSELUNG brauchst du unbedingt den anderen Schlüssel…….
Hi Alexander,
evtl. sollte man hier beim verschlüsseln eines Textes Mittels RSA noch sagen, dass die Zeichenlänge limitiert ist.
Ich habe mir dein Tutorial durch gelesen und finde es sehr einfach zu verstehen, wenn man sich das erste mal mit dieser Materie befasst! Coole Sache!
So und nochmal auf das oben genannte Problem zu kommen.
Ich möchte eine Text Datei mittels RSA verschlüsseln. Allerdings ist die länge des Textes limitiert. Es hängt entsprechend davon ab, wie lang der Schlüssel ist.
Mehr dazu habe ich auf StackOverflow gefunden.
http://stackoverflow.com/questions/10007147/getting-a-illegalblocksizeexception-data-must-not-be-longer-than-256-bytes-when
Dort wird empfohlen, bei einem langen Text, diesen zu erst via AES zu verschlüsseln und dann anschließend mit RSA.
Vielen Dank für dein Tutorial =)
Liebe Grüße
Xearox
Muss mich korrigieren.
Der Text wird mit AES verschlüsselt und der AES Key mit RSA Verschlüsselt.
Anschließend kann man den verschlüsselten AES Key mit dem verschlüsselten Text versendet werden. Der Empfänger kann nun den AES Key mit seinem RSA Key entschlüsseln und dann anschließend kann der Text Entschlüsselt werden.
@Alexander, falls es eine einfachere und kürzere Methode geben sollte, wäre ich Dankbar, wenn du diesen als Beitrag schreiben könntest =)
Hey Christopher,
danke für den Kommentar und die Ergänzung:)
Ich werde nach meiner Masterarbeit mal einen Artikel über die Performance von Verschlüsselungsalgorithmen schreiben und dich „zitieren“:)