# Verschlüsseln, aber welcher Algo



## freez (11. Okt 2011)

Hallo,

ich werde einfach nicht fündig bei der Suche nach meinem Problem. Evtl. kann mir jemand mit Stichwörtern helfen.

Ich habe einen String, welcher nur aus Zahlen besteht. Er ist immer 8 Zeichen lang. Nun möchte ich den String verschlüsseln (synchron oder asynchron ist egal). Das Problem dabei ist, dass ich als Ergebnis lediglich A-Z und 0-9 erlauben kann und der verschlüsselte Teil wiederum 8 Zeichen lang sein soll.

Gibt es sowas überhaupt schon?


----------



## Jigga (11. Okt 2011)

also ich habe nur ein mal mit Verschlüsselung in Java zu tun gehabt und hatte dafür 
	
	
	
	





```
DesEncrypter
```
 genommen.
Weiß jetzt aber nicht, ob es da möglich ist die Anzahl der Ziffern zu limitieren bzw. ob dies standardmäßig beibehalten wird.


----------



## freez (11. Okt 2011)

Jigga hat gesagt.:


> Weiß jetzt aber nicht, ob es da möglich ist die Anzahl der Ziffern zu limitieren bzw. ob dies standardmäßig beibehalten wird.


Nein, leider nicht: aus '12345678' mit 'abcdefgh' als Key wird mit DES '21C60DA534248BCE241BC91E5E55B3D6'


```
final SecretKeySpec blowfishKey = new SecretKeySpec(key.getBytes(), "DES");
		final Cipher blowfishCipher = Cipher.getInstance("DES");
		blowfishCipher.init(Cipher.ENCRYPT_MODE, blowfishKey);
```
Blowfish habe ich so auch getestet ... mit demselben Erfolg.


----------



## Jigga (11. Okt 2011)

ansonsten: wie sicher muss die ganze Geschichte denn sein?
Könntest ja sonst so ein Cäser-Alog nehmen...


----------



## timbeau (11. Okt 2011)

Was soll das denn für ne Anwendung sein? Mit diesen Anforderungen kann es nicht sicher sein und wenns nur zu Testzwecken ist, kann man doch sowas wie One-Time-Pad nehmen, natürlich mehr Pseudo-OTP.


----------



## freez (11. Okt 2011)

Jigga hat gesagt.:


> Könntest ja sonst so ein Cäser-Alog nehmen...



Naja, wenn es sonst keine andere Möglichkeit gibt, dann würde ich wohl darauf zurückgreifen. Habe ich in ähnlicher Weise auch schon gemacht. Hier könnte man einfach einen String als key mit 10 Zeichen nehmen und jede Ziffer mit dem entsprechenden Zeichen aus dem String ersetzten, welches an der entsprechenden Position wäre.

Besser wäre es aber mit einer synchronen Verschlüsselung. Hier geht es um eine eindeutige Ziffernkombination, die aber verborgen sein soll, damit sie nicht von jemanden anders erzeugt werden kann. Dazu fällt mir spontan noch ein, dass das Ergebnis genauso eindeutig sein muss. Wobei das ja eigentlich durch das verschlüsseln schon gegeben sein sollte. Ist ja kein Hash.


----------



## Jigga (11. Okt 2011)

Mal eine genrelle Frage dazu in dem Raum gestellt:
Heißt es nicht, dass die neueren Verschlüsselungsmethoden Bitweise Funktionieren? In diesem Fall dürfte das Ergebnis ja nicht größer sein, als der Klartext? Oder ist das dann eine Schicht tiefer und hat nichts mehr mit Java zu tun?


----------



## freez (11. Okt 2011)

timbeau hat gesagt.:


> Mit diesen Anforderungen kann es nicht sicher sein und wenns nur zu Testzwecken ist, kann man doch sowas wie One-Time-Pad nehmen



Wieso kann das nicht sicher sein? Natürlich weiss ich es nicht, aber wenn es einen String mit 8 Zeichen 0-9 gibt, kann es theoretisch einen eindeutigen String geben, der auch 8 Zeichen hat und zusätzlich Buchstaben enthält. Das diese 8 Zeichen dann wieder mit entsprechenden Aufwand entschlüsselt werden können, ist mir auch klar. Wobei die Frage ist, ob der Aufwand trotz des kurzen Ergebnisses mit einem langen Key evtl. doch größer wird.

Ich kann zwar keine Details verraten, aber stellen wir uns sowas wie eine Evaluation Seriennummer in Acronis True Image vor. In der Seriennummer ist das Ablaufdatum versteckt und Acronis möchte natürlich nicht, dass jemand das Datum erkennen kann und selbst wieder gültige Seriennummer produziert.


----------



## Gossi (11. Okt 2011)

2 Fragen:

1. Sind auch Kleinbuchstaben erlaubt?
2. (Denke ich kenne die Antwort) Muss es auch wieder entschluesselt werden?


----------



## freez (11. Okt 2011)

Gossi hat gesagt.:


> 1. Sind auch Kleinbuchstaben erlaubt?


Nein, nur A-Z und 0-9.



Gossi hat gesagt.:


> 2. (Denke ich kenne die Antwort) Muss es auch wieder entschluesselt werden?


ja, es soll wieder entschlüsselt werden können.


----------



## Gossi (11. Okt 2011)

Hab ne Möglichkeit, die is allerdings extrem bescheiden, unperformant, unübersichtlich etc.
Kurz gesagt sie is schlecht, aber sie Funktioniert ^^

PS:
Eben Quellcode kommentieren, dann kann ich ihn dir schicken, ich hoffe du bist dir bewusst, wenn das Hausaufgaben sind, würde ich den Quellcode den du gleich bekommst lieber net abgeben xD

PPS:

So, nu mal ans eingemachte:


```
public class Crypt {

	private final String cryptText;
	private final int[] abstand;

	public Crypt(final String text, final int[] abstand) {
		cryptText = text;
		this.abstand = abstand;
	}

	public String getCryptText() {
		return this.cryptText;
	}

	public int[] getAbstand() {
		return abstand;
	}

}
```

Und jetzt kommts:

```
import java.util.Random;

public class Verschluesselung {

	/**
	 * @param args
	 */
	public static void main(final String[] args) {
		Crypt c = verschluesseln("12345678");
		System.out.println(c.getCryptText());
		System.out.println(entschluesseln(c));
	}

	public static Crypt verschluesseln(final String nachricht) {
		// Prüfen ob der String die richtige länge hat
		if (nachricht.length() == 8) {
			Random r = new Random();

			// Abstand wird zum entschlüsseln benötigt
			int[] abstand = new int[] { 0, 0, 0, 0, 0, 0, 0, 0 };
			char[] cArray = nachricht.toCharArray();
			String verschluesselteNachricht = "";
			int i = 0;
			int nummer = 0;

			// Nachricht durchlaufen
			for (char c : cArray) {
				nummer = c;
				nummer += r.nextInt(123);
				boolean passend = false;

				// Solange es keine Zahl oder ein Großbuchstabe ist wiederholen
				while (!passend) {

					// Prüfung auf Zahl
					if (nummer < 58 && nummer > 47) {
						passend = true;
					}

					// Prüfung auf Großbuchstaben
					if (nummer < 91 && nummer > 64) {
						passend = true;
					}

					// Wenns nicht passt, Zahl verändern
					if (!passend) {
						if (nummer > 0) {
							nummer = r.nextInt(91) - nummer;
						} else {
							nummer += 91;
						}
					}
				}

				// Abstand abspeichern zum späteren entschlüsseln
				abstand[i] = c - nummer;
				c = (char) nummer;

				// Nachricht zusammenbauen
				verschluesselteNachricht = verschluesselteNachricht + c;
				i++;
			}
			return new Crypt(verschluesselteNachricht, abstand);
		} else {
			System.err.println("Ungueltige Nachrichtenlaenge");
			return new Crypt("", new int[] {});
		}
	}

	public static String entschluesseln(final Crypt crypt) {
		char[] cArray = crypt.getCryptText().toCharArray();
		int[] abstand = crypt.getAbstand();
		int i = 0;
		String ausgabe = "";

		// Nachricht durchlaufen und Entschlüsseln
		for (char c : cArray) {
			c = (char) (c + abstand[i]);
			ausgabe = ausgabe + c;
			i++;
		}
		return ausgabe;
	}

}
```

Ich weiß, is kein schöner Stil, aber es funzt.....


----------



## kay73 (11. Okt 2011)

Das Ablaufdatum ist ja fuer den Anwender eh ersichtlich; vielleicht muss man es nicht unbedingt verschluesseln sondern konzentriert sich auf eine "faelschungssichere" Seriennummer. Etwas ganz einfaches waere etwas shared-secret Basierendes:

- Ueberlege dir eine secret phrase z. B. "ACRONIS"
- Zerlege das Datum in 4 2-Ziffernbloecke. (11.10.2011 = "11"-"10"-"20"-"11")
- Stelle jeden Ziffernblock mit Hilfe von Ziffern und allen Buchstaben dar: ("11"->"B", "10"->"A", "20"->"K", "11"->"B"), erhalte "BAKB" 
- Bilde irgendeinen Hash ueber "BAKB"+"ACRONIS", z. B. MD5, erhalte "c8e25fbeebf6514b17881b084c9ec22c"
- Konkateniere das Resultat geeignet, z. B. u. a. die letzten 4 Stellen des Hashes, erhalte "BAKBC22C"

[EDIT] bisschen unschoen: Das Jahr kodier man besser als 2000-BigInteger des Jahrhunderts", also 
	
	
	
	





```
final BigInteger v = BigInteger.valueOf(999); System.out.println(v.toString(36).toUpperCase());
```
Das Dumme ist natuerlich, dass Du die secret Phrase in den Code packen musst.

Wir hatten hier noch einen Seriennummerthread:
http://www.java-forum.org/allgemeine-java-themen/108274-seriennummer.html

in dem gab es eine aehnliche Idee basieren auf RSA. Vielleicht kann man die Chiffren/Schluessel usw. irgendwie zusammenquetschen.


----------



## freez (11. Okt 2011)

@Gossi: Danke. Ist keine Hausaufgabe  Aus dem Alter bin ich raus. Noch schlimmer. Ich suche eine Lösung für ein Projekt von mir. Ich bin gerade noch dabei zu verstehen, wie es funktioniert. Gibt es einen Namen für den Algo?
@kay73: den Hash nehme ich wohl zum prüfen? Aber die Verschlüsselung selbst ist ja doch eher simpel. Trotzdem ist das eine Variante die mir gefällt. Wenn es nicht noch sicherer geht, würde mir diese Version gut gefallen.


----------



## freez (11. Okt 2011)

Achso, ich vergass ... der Anwender wird kein Stück Software auf seinem Computer haben, wo irgendwelche Keys hinterlegt sind. D.h. dekompilieren kann er es sowieso nicht.


----------



## Gossi (11. Okt 2011)

freez hat gesagt.:


> @Gossi: Danke. Ist keine Hausaufgabe  Aus dem Alter bin ich raus. Noch schlimmer. Ich suche eine Lösung für ein Projekt von mir. Ich bin gerade noch dabei zu verstehen, wie es funktioniert. Gibt es einen Namen für den Algo?


Ähm, würde sagen das is nen umgewandelter Cäser-Chiffre ^^

Und das ganze Funktioniert durch die Ascii-Tabelle, die if-Abfragen die du siehst decken den Bereich der Buchstaben, bzw. Zahlen, in der Ascii-Tabelle ab.

Für dich wichtig sind die Abschnitte:

48 - 57: Das sind die Zahlen 0-9 und
65 - 90: Das sind die Großbuchstaben von A-Z

Das Programm bekommt also 8 Werte aus dem Ascii-Bereich 48-57 (Zahlen) und geht sie einzelnt durch, addiert sie mit einer Zufallszahl, prüft anschließend ob das Ergebnis in einem gültigen Bereich liegt (Zahlen und Großbuchstaben) und falls dem so ist speichert er die Differenz zwischen neuem und altem Char in einem Array, welches später beim entschlüsseln dazu dient, wieder die Ursprüngliche Zahl zu bekommen.

Is zwar net sicher, aber besser als nix ^^


----------



## timbeau (11. Okt 2011)

Dann versteh ich den Sinn immernoch nicht. Der Anwender hat keine Software? Also bekommt er nur eine Serialnummer und gibt sie übers Web irgendwo ein und du prüfst ob sie gültig ist? 

Warum kannst du nicht ins Detail gehen? Musst ja keinen Code deines Projekts posten. Da gibts doch bestimmt 100 andere Möglichkeiten. Warum nur A-Z/0-9?


----------



## Gossi (11. Okt 2011)

freez hat gesagt.:


> Achso, ich vergass ... der Anwender wird kein Stück Software auf seinem Computer haben, wo irgendwelche Keys hinterlegt sind. D.h. dekompilieren kann er es sowieso nicht.



Wenn er es nicht dekompilieren kann und du die Anmeldung gegen eine Datenbank prüfst, könntest du auch den Code von mir benutzen und es so einstellen, das pro Serial nur ein User angelegt werden kann.

Müsstest dann halt ne Datenbanktabelle machen, sowas z.B:



id|used|key|original|1|2|3|4|5|6|7|8 
01|0|3FQ5ATD3|22645143|2|-6|9|18|-2|1|4|-19Sind jez keine echten Daten ^^

Wobei 1 - 8 für die daten aus dem int[] stehen.


----------



## kay73 (11. Okt 2011)

freez hat gesagt.:


> @kay73: den Hash nehme ich wohl zum prüfen?


Genau.


freez hat gesagt.:


> Aber die Verschlüsselung selbst ist ja doch eher simpel.


Eigentlich ist es gar keine Verschluesselung. Die Idee ist wirklich nur dafuer zu sorgen, dass der Anwender keine Seriennummer faelschen kann. Bei 8 Zeichen fehlt einfach der Raum fuer eine potente Verschluesselung. Vielleicht ist der RSA-Code aus dem alten Thread was fuer Dich; die Problemstellung ist ja aehnlich.


----------



## Gossi (11. Okt 2011)

Vor der aktivierung:


id|used|key|original|Nr. 1|Nr. 2|Nr. 3|Nr. 4|Nr. 5|Nr. 6|Nr. 7|Nr. 8|Nr. 9|Nr. 10|Nr. 11|Nr. 12|Nr. 13|Nr. 14|Nr. 15|Nr. 16
01|0|WMJG5JIIQEX53MN1|8738518812236604|-31|-22|-23|-15|0|-25|-17|-17|-32|-19|-38|-2|3|-23|-30|3
Danach


id|used|key|original|Nr. 1|Nr. 2|Nr. 3|Nr. 4|Nr. 5|Nr. 6|Nr. 7|Nr. 8|Nr. 9|Nr. 10|Nr. 11|Nr. 12|Nr. 13|Nr. 14|Nr. 15|Nr. 16
01|1|WMJG5JIIQEX53MN1|8738518812236604|-31|-22|-23|-15|0|-25|-17|-17|-32|-19|-38|-2|3|-23|-30|3
Das wären jez echte Daten bei nem 16er Schlüssel


----------



## timbeau (11. Okt 2011)

Und wo steckt da der Sinn hinter?


----------



## Gossi (11. Okt 2011)

timbeau hat gesagt.:


> Und wo steckt da der Sinn hinter?



Du hast ne Simple Verschlüsselung die durch hinterlegung in der Datenbank geprüft wird.

Somit bekommt der Benutzer zwar den Key zu gesicht, weiß jedoch nicht, mit welchen Zahlen dieser "Kodiert" wurde, oder wie der Originalschlüssel lautet und man kann, wenn z.B. in nem Forum, nen Benutzer angelegt wird, einen Key einem bestimmten Nutzer zuweisen.

Wenn sich anschließend jemand versucht mit dem Key zu Registrieren ist der Key bereits vergeben und die Registrierung schlägt fehl.....


----------



## timbeau (11. Okt 2011)

Password -> SHA-256/RSA/BLOWFISH/WasAuchImmer -> Datenbank

Abfrage -> Password  -> SHA-256/RSA/BLOWFISH/WasAuchImmer = Datenbank-Eintrag? 

Fertig und vor allem auch sicher wenn die Datenbank mal abhanden kommt. Soll ja vorkommen. 

Aber der TE hat ja noch nicht gesagt, warum er nur bestimmte Zeichen und eine bestimmte Länge haben muss.


----------



## freez (12. Okt 2011)

Gossi hat gesagt.:


> Müsstest dann halt ne Datenbanktabelle machen, sowas z.B:
> 
> 
> 
> ...


Hm, ich muss zugeben, dass ich noch nicht bis zu Ende gedacht habe, aber es ist unwahrscheinlich, dass eine Datenbank dahinter steckt. Fakt ist, dass man anhand des Codes auch ohne Datenbank die Daten, die dahinter stecken, entschlüsselt werden kann.


----------



## freez (12. Okt 2011)

timbeau hat gesagt.:


> Aber der TE hat ja noch nicht gesagt, warum er nur bestimmte Zeichen und eine bestimmte Länge haben muss.



Es gibt bereits diese Ziffernfolge mit 8 Zeichen und nun habe ich bedenken, dass diese jemand einfach selbst generieren kann, wenn er weiß, aus welchen Bestandteilen diese Ziffernfolge besteht (in meinem Fall ist es kein Datum). Um den Code fälschungssicher zu machen, dachte ich an eine Verschlüsselung. Dabei ist vorgegeben, dass der Code lediglich aus A-Z0-9 bestehen darf und nur 8 Zeichen betragen darf. Dieser verschlüsselte Code soll von einer bestimmten Nutzergruppe wieder entschlüsselt werden dürfen, um an die Informationen auch ohne eine Datenbank zu kommen.

Den letzten Teil bin ich aber noch unsicher, da durch eure Beiträge mir schon bewusst geworden ist, dass ein Datenbankzugriff vielleicht sinnvoller wäre. Ich müsste die Daten dann nicht verschlüsseln, sondern nur einen eindeutigen zufälligen Code generieren über den der User an die Informationen kommt. Aber das wäre die Komplizierte Variante.


----------



## timbeau (12. Okt 2011)

Ich bin ebenfalls kein Profi in solchen Sachen aber um Informationen zu übergeben, gibt es doch sicher bessere Methoden. PGP z.B. wobei hier nicht unbedingt realisierbar. 

Oder eben über einen Webservice bei dem die Clienten sich einloggen und einen String o.ä. zurückbekommen. Auch hier ist die sichere Übertragung zu überlegen.


----------



## TheRealSpikee (15. Okt 2011)

Auch hier werf ich mal AES in den Raum


```
import java.io.*;
import javax.crypto.*;
import java.security.*;
public class AESHelper
{
	private BufferedReader in=null;
	private PrintStream out=null;
	private Cipher encryptCipher=null;
	private Cipher decryptCipher=null;
	private SecretKey secretKey=null;
	public AESHelper(BufferedReader in, PrintStream out, SecretKey secretKey) { this.in=in; this.out=out; this.secretKey=secretKey; }
	public void initCipher() throws Exception
	{
		(encryptCipher=Cipher.getInstance("AES")).init(Cipher.ENCRYPT_MODE, secretKey);
		(decryptCipher=Cipher.getInstance("AES")).init(Cipher.DECRYPT_MODE, secretKey);
	}
	public static SecretKey generateKey() throws Exception
	{
		KeyGenerator keyGenerator=KeyGenerator.getInstance("AES");
		keyGenerator.init(new SecureRandom());
		return keyGenerator.generateKey();
	}
	public void sendMessage(String msg) throws Exception
	{
		out.println(ToolKit.byteArrayToHexString(crypt(msg.getBytes(), encryptCipher)));
	}
	public String receiveMessage() throws Exception
	{
		String line="";
		while((line=in.readLine())!=null)
		{
			if(!line.equals(""))
				break;
		}
		return new String(crypt(ToolKit.hexStringToByteArray(line), decryptCipher));
	}
	private byte[] crypt(byte[] dataInput, Cipher cipher) throws Exception
	{
		ByteArrayInputStream bais=new ByteArrayInputStream(dataInput);
		ByteArrayOutputStream baos=new ByteArrayOutputStream();
		int blockSize=cipher.getBlockSize();
		int outputSize=cipher.getOutputSize(blockSize);
		byte[] input=new byte[blockSize];
		byte[] output=new byte[outputSize];
		int readBytes=0;
		boolean finish=false;
		while(!finish)
		{
			readBytes=bais.read(input);
			if(readBytes==blockSize)
			{
				int outputLength=cipher.update(input, 0, blockSize, output);
				baos.write(output, 0, outputLength);
			}
			else
				finish=true;
		}
		if(readBytes>0)
			output=cipher.doFinal(input, 0, readBytes);
		else
			output=cipher.doFinal();
		baos.write(output);
		return (baos.toByteArray());
	}
}

class ToolKit
{
	public static String byteArrayToHexString(byte[] input)
	{
		StringBuffer sb=new StringBuffer(input.length*2);
		for(int i=0; i<input.length; i++)
		{
			int v=input[i]&0xFF;
			if(v<16)
				sb.append("0");
			sb.append(Integer.toHexString(v));
		}
		return sb.toString();
	}
	public static byte[] hexStringToByteArray(String input)
	{
		byte[] ret=new byte[input.length()/2];
		for(int i=0; i<ret.length; i++)
		{
			int index=i*2;
			int v=Integer.parseInt(input.substring(index, index+2), 16);
			ret[i]=(byte)v;
		}
		return ret;
	}
}
```

Hat als return zwar nur A-F0-9 / a-f0-9 und auch eine Blocklänge von 128Bit/16Byte ... aber mit sowas könntest du anfangen ... zumal es in Java schon fertig implementiert ist.


----------



## freez (15. Okt 2011)

TheRealSpikee hat gesagt.:


> Auch hier werf ich mal AES in den Raum



Der verschlüsselte String ist aber deutlich länger als 8 Zeichen


----------



## TheRealSpikee (16. Okt 2011)

ja natürlich ist der cipher-text länger als 8Byte ...

mal davon abgesehen das AES eine blockgröße von 128Bit hat ... also 16Byte ... wird es beim umwandeln in einen hex-string noch mal doppelt so lang ... also 32Byte ... die du als output-länge hast für input-längen von 0Byte-15Byte
wenn du dann als input-länge schon 16Byte hast hast du als output-länge schon 64Byte *2 blöcke a 16Byte = 32Byte ... x 2 *byte->hex-string* = 64Byte*

ich habe auch nie beahuptet das das return 8Byte hat ... ich hab es sogar noch geschrieben *blocklänge 128Bit / 16Byte* ... aber es ist die einfachste art und weise binär-daten in java symetrisch zu verschlüsseln


----------



## freez (17. Okt 2011)

TheRealSpikee hat gesagt.:


> ich habe auch nie beahuptet das das return 8Byte hat ... ich hab es sogar noch geschrieben *blocklänge 128Bit / 16Byte* ... aber es ist die einfachste art und weise binär-daten in java symetrisch zu verschlüsseln



Ich weiss. Ich danke dir auch für deinen Tipp. Aber ich habe nun mal die Anforderung, dass 8 Zeichen maximal dabei raus kommen müssen und somit hilft es mir nicht. 

Ich habe aber dein Codebeispiel gleich in meiner "Codebibliothek" abgelegt.


----------



## TheRealSpikee (17. Okt 2011)

Hmm ... was würde mir noch so einfallen ... DES vllt ? das sind nur 56Bit ... also 7Byte lang ... und wenn es jetzt genau 8 Byte sein müssen dann hängst du hinten noch n prüf-byte dran ... was du dann beim deciphern weglässt. Ob man mit DES auch so einfach Block-weise ciphern kann weis ich nicht da ich mich mit so schwachen algo's nicht befasse *in meiner original-klasse wo ich den bleistift rauskopiert habe verwende ich BC und AES-256 ... dazu noch key-exchange mit RSA-4096 ... ist schon sehr schwer sich da bei mir reinzuknacken ... zumindest ohne eine riesen server-farm und genug zeit ...* aber das geht deutlich über deine aufgabenstellung hinaus ...
wenn du willst kann ich dir das auch mal als bleistift posten ... sei dann aber nicht erschlagen von der masse an code ... hab da halt viel arbeit reingesteckt das es halbwegs performant ist ... darum ist es auch so lang ...


----------

