# JaSypt - Verschlüsselung



## Sebastian F (28. August 2007)

Guten Tag,

ich habe heute auf http://www.jasypt.org/ ein Packet gefunden mit dem es möglich ist Text sowie Passwörter zu verschlüsseln.Soweit so gut, also habe ich das Paket geladen und weitere Lib´s die für die Verwendung importiert werden müssen in mein Projekt eingebunden.

Jetzt habe ich das Beispiel auf der Seite genommen und bei mir getestet.

String bla = "blubb";
BasicPasswordEncryptor passwordEncryptor = new BasicPasswordEncryptor();
String encryptedPassword = passwordEncryptor.encryptPassword(bla);

Normalerweise sollte ich nun in meiner Variablen "encryptedPassword" den verschlüsselten String haben.Leider bekomme ich folgende Exception:

Exception in thread "AWT-EventQueue-0" org.jasypt.exceptions.EncryptionInitializationException: Password not set for Password Based Encryptor
        at org.jasypt.encryption.pbe.StandardPBEByteEncryptor.initialize(StandardPBEByteEncryptor.java:542)
        at org.jasypt.encryption.pbe.StandardPBEStringEncryptor.initialize(StandardPBEStringEncryptor.java:488)
        at org.jasypt.encryption.pbe.StandardPBEStringEncryptor.encrypt(StandardPBEStringEncryptor.java:541)
        at org.jasypt.util.text.BasicTextEncryptor.encrypt(BasicTextEncryptor.java:89)

Und laut Netbeans soll der vermeindliche Fehler in dieser Zeile liegen:
String encryptedPassword = passwordEncryptor.encryptPassword(bla);

Aber ehrlich gesagt bin ich völlig ratlos und weiß auch nicht was da falsch sein soll.


----------



## schnuffie (28. August 2007)

Das sieht danach aus, als wenn Du vorher nicht initialisiert hättest...

Auf der Webseite http://www.jasypt.org/encrypting-texts.html steht dazu:


> Before it is ready to encrypt, an object of this class has to be _initialized_. Initialization happens:
> 
> When initialize() is called.
> When encrypt(...) or decrypt(...) are called for the first time, if initialize() has not been called before


----------



## Sebastian F (28. August 2007)

Aber das bezieht sich doch mehr auf das encrypten von Text oder?
In meinen Versuch versuche ich ja ein Passwort zu entcrypten und ich rufe dort ja 
auch die encryptPassword() Methode auf also müsste es doch auch initialisiert sein,oder bin ich da falsch?


----------



## schnuffie (28. August 2007)

Du verwendest *org.jasypt.util.password.BasicPasswordEncryptor*. Lt. Javadoc steht dort Folgendes:



> This class internally holds a StandardStringDigester configured this way


 
Diese Klasse hat diese besagte "initialize()"-Methode.


----------



## Sebastian F (28. August 2007)

Ich bins nochmal,

Kollege hat ebend geschaut, durch den BasicPasswordEncryptor wird der StandardStringDigester doch schon vorinitialisiert oder?

http://www.jasypt.org/api/jasypt/apidocs/org/jasypt/util/password/BasicPasswordEncryptor.html


----------



## tha_specializt (28. August 2007)

Kinnings, ihr macht es wieder mal viel zu komplex:
http://www.tutorials.de/forum/1470891-post4.html

Wieso erst dubiose Third-Party-Klasse einbacken, wenn alles bereits da ist? (JDK 1.6)

Wenn man mein Beispiel direkt so übernehmen will, so benötigt man die *Java Unlimited Strength Jurisdiction Policy Files* für eine Verschlüsselungsstärke > 192 bit, ansonsten gehts auch ohne (einfach die Stärke in meinem Beispiel runterschrauben)


----------



## schnuffie (28. August 2007)

Ich hab' vor lauter Neugier das Beispiel auch mal getestet, jedoch ohne zufriedenstellenden Erfolg:


```
import java.io.UnsupportedEncodingException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.KeyGenerator;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
public class CodeDecode {
  
  public static String getText(String codeText) {
    if (codeText == null || codeText.length() < 16) {
      return "";
    }
    try {
      byte[] code = codeText.substring(0, codeText.length() - 16).getBytes();
      byte[] seed = codeText.substring(codeText.length() - 16).getBytes();
      SecretKeySpec key = new SecretKeySpec(seed, "AES");
      Cipher chiffre = Cipher.getInstance("AES");
      chiffre.init(Cipher.DECRYPT_MODE, key);
      byte[] text = chiffre.doFinal(code);
      return new String(text, "UTF-16");
    } catch (NoSuchAlgorithmException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    } catch (NoSuchPaddingException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    } catch (InvalidKeyException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    } catch (IllegalBlockSizeException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    } catch (BadPaddingException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    } catch (UnsupportedEncodingException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }
    
    return null;
  }
  
  public static String getCodeText(String text) {
    if (text == null) {
      text = String.valueOf(null);
    }
    try {
      KeyGenerator keyGen = KeyGenerator.getInstance("AES");
      keyGen.init(128);
      SecretKey sKey = keyGen.generateKey();
      byte[] raw = sKey.getEncoded();
      SecretKeySpec key = new SecretKeySpec(raw, "AES");
      Cipher chiffre = Cipher.getInstance("AES");
      chiffre.init(Cipher.ENCRYPT_MODE, key);
      byte[] code = chiffre.doFinal(text.getBytes("UTF-16"));
      StringBuffer sb = new StringBuffer();
      sb.append(new String(code));
      sb.append(new String(raw));
      return sb.toString();
    } catch (NoSuchAlgorithmException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    } catch (NoSuchPaddingException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    } catch (InvalidKeyException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    } catch (IllegalBlockSizeException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    } catch (BadPaddingException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    } catch (UnsupportedEncodingException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }
    
  return null;
  }
  
  
  public static void main(String[] args) {
    //String s1 = CodeDecode.getCodeText("Soll ich mal sagen, was das für ein Riesenquatsch hier ist? Irgendwas ist ziemlich dumm verschlüsselt, oder vielleicht nach Straßennamen gestrickt.");
    String s1 = CodeDecode.getCodeText("Soll ich mal sagen, was das für ein Riesenquatsch hier ist? Straßennamen fehlen hier.");
    System.out.println(s1.length() + ": " + s1);
    String s2 = CodeDecode.getText(s1);
    System.out.println(s2.length() + ": " + s2);
  }
 
}
```
 
Heraus kommt mein String immer mal anders und bei ca. jedem 10. Versuch kommt diese Exception:


```
javax.crypto.BadPaddingException: Given final block not properly padded
at com.sun.crypto.provider.SunJCE_h.b(DashoA12275)
at com.sun.crypto.provider.SunJCE_h.b(DashoA12275)
at com.sun.crypto.provider.AESCipher.engineDoFinal(DashoA12275)
at javax.crypto.Cipher.doFinal(DashoA12275)
at code.CodeDecode.getText(CodeDecode.java:27)
at code.CodeDecode.main(CodeDecode.java:97)
Exception in thread "main" java.lang.NullPointerException
at code.CodeDecode.main(CodeDecode.java:98)
```
 
Weiß leider nicht, warum es nicht richtig funktioniert, zumal meine gemeinsame Basis "UTF-16" ist. Bei "UTF-8" kommt nur Murx raus.


----------



## tha_specializt (28. August 2007)

Das hat nichts mehr mit meinem Beispiel zu tun .... Und was du mit UTF am schaffen hast versteh ich schon zweimal nicht .... Du willst einer KRYPTOGRAPHIE-FUNKTION charsets zuteilen?  Ähm .... Dazu sag ich jetzt besser nichts.


Leichter kann man es eigentlich nicht mehr haben: man kopiert mein Beispiel, implementiert die notwendigen Klassen, und schon hat man per 

```
byte[][] crypt = verschluesseln("meinganzgeheimerText");
byte[] crypttext = crypt[0];
byte[] seed = crypt[1];
System.out.println(entschluesseln(crypttext, seed));
```
ultraleicht die besprochene Aufgabe erledigt.


> Ich hab' vor lauter Neugier das Beispiel auch mal getestet,


Nö. Das "testen" meines Beispiels würde exakt 4 Zeilen umfassen, wenn man gerne kryptisch programmiert sogar nur eine einzige


----------



## schnuffie (29. August 2007)

@tha_specializt: Da hat sich aber Jemand mächtig auf den Schlips getreten gefühlt...

Ich habe mich da wohl nicht ganz korrekt ausgedrückt. Gemeint habe ich, daß ich die Logik Deines Beispiels verwendet habe.

Das Problem liegt dann bei mir warscheinlich an:


> Wenn du bestimmte Texte nicht verwenden kannst, so deutet das entweder auf falsche Handhabung der Interfaces, fehlerhaftes bzw. unangebrachtes casten oder anwendung von nicht zugelassenen Modis (zb. CBC wo es ECB o.ä. sein sollte) hin


 
Die Char-Set-Vorgabe macht hier Probleme, das habe ich inzwischen auch schon festgestellt. Wenn man mit unterschiedlichen Zeichensätzen arbeitet, könnte das Verschlüsselte dann in etwas ganz anderes entschlüsselt werden, oder sehe ich das falsch?


----------



## schnuffie (29. August 2007)

Das Problem liegt an der String-Umwandlung:


```
CodeDecodeOriginaler cd = new CodeDecodeOriginaler();
    byte[][] crypt = cd.verschluesseln("Soll ich mal sagen, was das für ein Riesenquatsch hier ist? Irgendwas ist ziemlich dumm verschlüsselt, oder vielleicht nach Straßennamen gestrickt.");
    
    String sCode = new String(crypt[0]);
    String sKey = new String(crypt[1]);
    
    byte[] crypttext = sCode.getBytes();
    byte[] seed = sKey.getBytes();
    System.out.println(cd.entschluesseln(crypttext, seed));
```
 

```
public synchronized byte[][] verschluesseln(String klartext) throws Exception {
    byte[] raw;
    KeyGenerator kgen = KeyGenerator.getInstance("AES");
    kgen.init(128);
    SecretKey skey = kgen.generateKey();
    raw = skey.getEncoded();
    SecretKeySpec key = new SecretKeySpec(raw, "AES");
    Cipher chiffre = Cipher.getInstance("AES");
    chiffre.init(Cipher.ENCRYPT_MODE, key);
    byte[] text2 = klartext.getBytes();
    byte[] verschluesselt = chiffre.doFinal(text2);
    byte[][] rueckgabe = { verschluesselt, raw };
    return (rueckgabe);
  }
  public synchronized String entschluesseln(byte[] chiffre, byte[] seed) throws Exception {
    SecretKeySpec key = new SecretKeySpec(seed, "AES");
    Cipher chiffre2 = Cipher.getInstance("AES");
    chiffre2.init(Cipher.DECRYPT_MODE, key);
    byte[] entschluesselt = chiffre2.doFinal(chiffre);
    String ende = new String(entschluesselt);
    return (ende);
  }
```
 
Ich vermute, mein Charset kennt bestimmte Zeichen nicht und wertet sie daher falsch.
Ergebnis ist dann folgender Fehler oder ein falscher Text:


```
Exception in thread "main" javax.crypto.BadPaddingException: Given final block not properly padded
at com.sun.crypto.provider.SunJCE_h.b(DashoA12275)
at com.sun.crypto.provider.SunJCE_h.b(DashoA12275)
at com.sun.crypto.provider.AESCipher.engineDoFinal(DashoA12275)
at javax.crypto.Cipher.doFinal(DashoA12275)
at code.CodeDecodeOriginaler.entschluesseln(CodeDecodeOriginaler.java:29)
at code.CodeDecodeOriginaler.main(CodeDecodeOriginaler.java:43)
```


```
Soll ich mal sagen, was das für ein Riesenquatsch hier ist? Irgendwas ist ziemli6£OÅÏVêaæ?%¯Á?
sselt, oder viel'C3-3Ø™R)§m¯œœ82ßennamen gestrickt.
```


----------



## tha_specializt (29. August 2007)

Du verstehst die Grundsätze der Kryptoanalyse nicht ...  ed is so:

der Algorithmus selbst benutzt keine Charsets, sowas ist für ihn völlig uninteressant. "Er" nimmt lediglich die eingehenden *Bytes* und substituiert nach einem bestimmten Muster. "Er" fummelt _NIE_ mit Strings herum, das macht lediglich der Anwender AUSSERHALB der eigentlichen Kryptographie.

Ich interpretiere in meinem Beispiel lediglich einen beliebigen String anhand eines frei gewählten Charsets (in diesem Fall: Default-Locale-charset) als entsprechendes Byte-Array. Dieses wird dann verschlüsselt wieder ausgegeben.  Das Ergebnis-Array enthält teilweise weitaus grössere Elemente als du in jedem Charset finden wirst. Wenn du nun das Ergebnis also in irgendeinen Charset zwängen willst, ohne es vorher wieder zu entschlüsseln, dann _MUSS_ das ja fehlerhaft sein. Vor dem entschlüsseln hat man lediglich das *nichtsaussagende* Byte-Array, es stellt keine Repräsentation von irgendetwas dar. Dass einige Zeichen tatsächlich korrekt sind ist lediglich darauf zurückzuführen dass die Entwickler von AES ihre Zeichenzuteilung anhand gängiger Charsets erstellt haben, und somit einige Zeichen gerne mal dem Ursprungs-String entsprechen. Das ist aber eher Glückssache, je nachdem wie hoch die Primzahl im aktuellen Vorgang war ;-).

Man sollte übrigens nicht glauben, dass das einzig und allein ein Symptom von AES ist, auch in allen anderen Algorithmen wirst du Probleme bekommen, wenn du die direkte crypto-Ausgabe in charsets zwängen willst ... Am besten bleiben lassen, das ist ein gern gemachter Fehler, der in produktiven Umgebungen zu nicht auffindbaren Fehlern führen kann und somit deinen Angestellten-Status ernsthaft gefährden kann

Was folgern wir daraus? Ganz einfach: Möchte man Texte verschlüsselt  ablegen, so muss man diese entweder als Zahlen-Sammelsurium speichern, oder eben eine eindeutige Rückführung zu den korrekten, ursprünglichen  Bytes ermöglichen


----------



## schnuffie (31. August 2007)

tha_specializt, da hast Du natürlich recht. In meinen Tests habe ich auch gemerkt, daß ein Teil der verschlüsselten Zeichen negativ sind, also (wie Du auch geschrieben hast) nicht in ein char überführt werden können. Somit erklärt sich auch meine Testausgabe. Die Bytes als Zahlen zu speichern, wäre eine Alternative. 

Interessant würde ich die Möglichkeit finden, dem Verschlüsselungsalgorythmus einen Satz gültiger Zeichen zur Verfügung zu stellen (z.B. A-Z und 0-9), aus denen er dann das codierte Ergebnis macht, aber sowas scheint nicht zu gehen, oder?
Hintergrund meiner Frage ist einfach die, daß mein Beispielprogramm den codierten Text per Copy-and-Paste zur Verfügung stellen soll und er dann außerdem die Möglichkeit hätte, diesen codierten Text per Copy-and-Paste wieder decodieren zu lassen.


----------

