Java Umwandlung BigInteger zu String nur bei UTF-8 möglich?

Knobi-Wan

Lt. Junior Grade
Registriert
März 2008
Beiträge
355
Hallo,
folgendes Problem: Ich soll eine Klasse schreiben, die ein JFrame erstellt und mit RSA verschlüsseln kann. So weit, so gut. Den Text, der in dem JTextField-Eingabefenster steht, wird an folgende Methode übergeben:

Code:
private String encodeInput(String input){
    int maxblocklaenge=(mod.bitLength()-1)/16;
    System.out.println("maxblocklänge "+maxblocklaenge);
    int blockzahl=input.length()/maxblocklaenge;
    System.out.println("blockzahl "+blockzahl);
    String s="";
    try{
      for(int a=0; a<blockzahl; a++){ //konvertiert jeden Block zu einem BigInteger, verschlüsselt es und fügt es dem String s hinzu
        BigInteger block=new BigInteger(input.substring(maxblocklaenge*a+a, maxblocklaenge*(a+1)+a).getBytes("UTF-16BE"));
        block.modPow(publicKey, mod);
        s+=new String(block.toByteArray(), "UTF-16BE");
        System.out.println(s);
      }
      BigInteger block=new BigInteger(input.substring(maxblocklaenge*blockzahl+blockzahl, input.length()).getBytes("UTF-16BE"));
      System.out.println("block "+block);
      
      block=block.modPow(publicKey, mod);
      System.out.println("encoded block "+block);
      
      block=block.modPow(privateKey, mod);
      System.out.println("decoded block "+block);
      
      String ss=new String(block.toByteArray(), "UTF-16BE");
      System.out.println("ss "+ss);
      
      s+=ss;
    }
    catch(UnsupportedEncodingException ex){
      ex.printStackTrace();
    }
    return s;
  }

Output in der Konsole ist hier aber für input = "k":
maxblocklänge 15
blockzahl 0
block 107
encoded block 118904571616129635632380305823567246620
decoded block 107
ss ?

läuft also nicht besonders gut. Auffallend ist, dass es funktioniert, wenn ich statt UTF-16BE UTF-8 benutze:

maxblocklänge 15
blockzahl 0
block 107
encoded block 20594889241748384884808005460238237006
decoded block 107
ss k

Frage jetzt:

Wie kann das sein und wie lässt sich das lösen, da ich schon ganz gerne UTF-16 benutzen würde.
 
Zuletzt bearbeitet:
klar, aber s enthält im Ausgabetextfeld auch nur "�", obwohl es ja mit UTF-16 aus dem Bytearray rekodiert wird.
 
Ehrlich gesagt versteh ich den Code nicht ganz...

was spricht gegen diese Version?
gut die funktioniert auch nicht, find ich aber zumindest etwas "übersichtlicher"...

Code:
	private byte[] getBytes(byte[] bytes, int start, int end) {
		final int length = end - start;
		final byte[] result = new byte[length];
		for (int i = 0; i < length; i++) {
			result[i] = bytes[start + i];
		}
		return result;
	}

	private String encodeInput(String input) {
		final int maxblocklaenge = mod.bitLength() / 8;
		final byte[] bytes = input.getBytes();
		final int blockzahl = bytes.length / maxblocklaenge + ((bytes.length % maxblocklaenge > 0) ? 1 : 0);
		
		System.out.println("maxblocklänge " + maxblocklaenge);
		System.out.println("blockzahl " + blockzahl);
		
		final StringBuilder result = new StringBuilder();
		int start = 0;
		int end = maxblocklaenge;
		while (start < bytes.length) {
			final byte[] currentBytes = getBytes(bytes, start, end);
			final BigInteger block = new BigInteger(currentBytes);
			result.append(block);
			System.out.println("block " + block);

			BigInteger encoded = block.modPow(publicKey, mod);
			System.out.println("encoded block " + encoded);

			BigInteger decoded = encoded.modPow(privateKey, mod);
			System.out.println("decoded block " + decoded);

			System.out.println("decoded String " + new String(decoded.toByteArray()));

			start = start + maxblocklaenge;
			end = end + maxblocklaenge;
			if (end > bytes.length) {
				end = bytes.length;
			}
		}
		return result.toString();
	}

Edit: ne passt glaub schon, hab nur statt mod nicht n sondern bei mir phi(n) genommen....
 
Zuletzt bearbeitet:
Für Textfelder gilt dasselbe wie für die Konsole: falsche Codierung, Ausgabe mit UTF-8 (wenn ich das richtig in Erinnerung habe - jedenfalls aber nicht mit UTF-16).

Mir ist ohnehin nicht ganz klar, warum du da unbedingt die Codierung mit reinbringen willst...
 
Aber die Frage, die man sich stellen muss: Wie soll das Ganze wieder decodiert werden, wenn die BigInteger im Ergebnis als String geschrieben werden? Da müsste man einen Separator einbauen oder immer entsprechend mit Nullen auffüllen - abhängig vom Modul n...

@xbrtll: Ich versteh das mit der Codierung auch nicht so ganz :-) also warum sie so wichtig zu sein scheint...
 
Zuletzt bearbeitet:
naja, bei UTF8 sind ja meines Wissens nach keine Umlaute enthalten. Die zu haben finde ich persönlich schon wichtig. Aber wenn es nur mit UTF8 funktioniert, muss das reichen.
Danke übrigens für das Codebeispiel.
 
dann muss ich da was falsch verstanden haben :)

e: aber nichtsdestotrotz wird ein "ä" z.B. nicht richtig verarbeitet, der Wert von block ist da negativ.
 
Zuletzt bearbeitet:
Ich würde das ganze aber erstmal auch von ganz von Anfang neu testen und zwar einfach nur einen einzelnen Buchstaben zu ver- und entschlüsseln und das ohne deine Blocklängen und co. Wenn das klappt, steckt in dem doch recht unübersichtlichen Code das Problem.

Fernab von dem Problem ist dein Code sowieso falsch, wie du versuchst aus RSA eine Art Blockchiffre zu bauen. (Und wenn ich den Code richtig verstehe wird eh immer nur der letzte Block kodiert.) Du kannst nicht von einem Unicode String eine bestimmte Zeichenlänge nehmen und erwarten, dass sie zu deiner Blocklänge passt.
Unicode ist eine Zeichenkodierung mit variabler Länger, UTF-8/UTF-16/UTF-xy sagen dir nur, dass ein Zeichen in Byteketten von 8 oder 16 Bit zerlegen lassen. Ein ASCII Zeichen wird in UTF-8 z.B. wirklich nur 8 Bit haben, ein deutscher Umlaut besteht aber aus 2 Byteketten zu jeweils 8 Bit, also 16 Bit. Ein UTF-8 Zeichen kann nach der Spezifikation aus 4 Byteketten also 32 Bit bestehen.
 
aber irgendwie muss ich den Eingangsstring ja aufspalten, da die zu verschlüsselnde Nachricht weniger Stellen als der Modul haben muss.
 
Da ist schon was dran... der Punkt ist aber, dass eigentlich RSA selten für die Verschlüsselung von Nutzdaten verwendet wird, sonderrn eher
a) zum Verschlüsseln von Hash-Werten (= Signatur)
b) zum Verschlüsseln des Session-Keys, und die Daten dann mit z.B. AES verschlüsselt sind...
das sind dann Dinge, die sich mit einem "normalen" Modul von 1024 bis 2048 Bit eigentlich ohne Zerstückelung berechnen lassen sollten...

Aber noch mal zu dem Problem... woher willst du wissen bei einem verschlüsselten String 123, ob das ein Block oder zwei sind, und wenn zwei, ob der 1. Block 1 oder 12 ist usw?
 
naja, "k" wird zu einer 128-Bit Zeichenfolge verschlüsselt, bei mod=128 Bit.
den verschlüsselten Gesamtstring könnte man ja danach wieder auftrennen.

Aber der Einwand klingt plausibel. Es ist wohl das Beste, wenn ich es lasse mit der Blockbildung und einfach abbrechen lasse, wenn der Eingabestring zu groß ist.
 
1668mib schrieb:
Da ist schon was dran... der Punkt ist aber, dass eigentlich RSA selten für die Verschlüsselung von Nutzdaten verwendet wird, sonderrn eher

Man kann das ganze erweitern, dass RSA eigentlich nirgends in seiner reinen Form genutzt wird, da es diverse Schwächen hat. Es wird eigentlich nur PKCS#1 verwendet.

Knobi-Wan schrieb:
Aber der Einwand klingt plausibel. Es ist wohl das Beste, wenn ich es lasse mit der Blockbildung und einfach abbrechen lasse, wenn der Eingabestring zu groß ist.
Aber auch da musst du wissen, wie groß der String mit seinen Bytes wirklich ist ;)
Du kannst es also nur ganz simpel machen und sagst bei einer Blocklänge von x darf ich y Bytes verschlüsseln, ein Zeichen kann mit UTF-8 bis zu 4 Byte benötigen, also dürfen es maximal y/4 Zeichen im String sein.
Oder du findest eine Möglichkeit die wirkliche Bytegröße des Strings herauszufinden, möglicherweise mit einem großen Array und dem Zählen der leeren Stellen am Schluss - keine Ahnung ob das funktioniert.
 
Zuletzt bearbeitet:
hab das jetzt so gelöst:
Code:
 private String encodeInput(String input, String exp, String mod){
    BigInteger e, n;
    e=new BigInteger(exp);
    n=new BigInteger(mod);
    try{
      BigInteger b = new BigInteger(input.getBytes("UTF-8"));
      
      b = b.modPow(e, n);
      
      return ""+b;
    }
    catch(UnsupportedEncodingException ex){
      ex.printStackTrace();
    }
    catch(NumberFormatException ex){
      ex.printStackTrace();
    }
    return "";
  }

beim dekodieren dann eben umgekehrt:
Code:
private String decodeInput(String input, String exp, String mod){
    BigInteger d, n;
    d=new BigInteger(exp);
    n=new BigInteger(mod);
    try{
      BigInteger b = new BigInteger(input);
      
      b = b.modPow(d, n);
      
      return new String(b.toByteArray(), "UTF-8");
    }
    catch(UnsupportedEncodingException ex){
      ex.printStackTrace();
    }
    catch(NumberFormatException ex){
      ex.printStackTrace();
    }
    return "";
  }
 
Zuletzt bearbeitet:
da fällt mir gerade ein: Einige hatten sich ja beschwert, dass der Code zu unübersichtlich sei. Wie mach ich ihn denn übersichtlich? :)
 
Zurück
Oben