# Verschlüsselung mit Blowfish



## heinz86 (8. Sep 2011)

Hallo,

wollte mich nun ein wenig mit dem Verschlüsseln mit Blowfish unter Java befassen. Habe ein wenig gegooglet und den folgenden Code gefunden:


```
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;

public class Blowfish {

	public static String encryptBlowfish(String to_encrypt, String strkey) {
		try {
			SecretKeySpec key = new SecretKeySpec(strkey.getBytes(), "Blowfish");

			Cipher cipher = Cipher.getInstance("Blowfish/ECB/NoPadding");

			cipher.init(Cipher.ENCRYPT_MODE, key);
			
			return new String (cipher.doFinal(to_encrypt.getBytes()));

			//return new String(cipher.doFinal(to_encrypt.getBytes()));

		} catch (Exception e) {
			return null;
		}
	}

	public static String decryptBlowfish(String to_decrypt, String strkey) {
		try {
			SecretKeySpec key = new SecretKeySpec(strkey.getBytes(), "Blowfish");

			Cipher cipher = Cipher.getInstance("Blowfish/ECB/NoPadding");

			cipher.init(Cipher.DECRYPT_MODE, key);
			
		    return new String (cipher.doFinal(to_decrypt.getBytes(),0,8));

			//return new String(cipher.doFinal(to_decrypt.getBytes(), 0, 8));
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
	}
}
```


Nun wollte ich gerne eine Datei einlesen, diesen String dann verschlüsseln und wieder in eine Datei schreiben. Dann diese Öffnen und einlesen, anschließend dann wieder entschlüsseln. Aber irgendwie klappt das noch nicht so wie es soll. Ich schätze, dass es ein Problem mit der Codierung gibt...

Der Aufruf lautet

```
String ciphertext = Blowfish.encryptBlowfish(plaintext,key);
```
bzw.

```
String plaintext = Blowfish.decryptBlowfish(ciphertext, key);
```

Dabei steht in plaintext bzw. ciphertext der eingelesene Text und in Key ein beliebiger Schlüssel.


----------



## xerberuz (8. Sep 2011)

Die Verschlüsselung arbeitet auf byte und nicht auf text Ebene. Du musst das Ergebnis also irgendwie konvertieren.


```
public class BlowfishTest
{

   public static void main(final String[] args) throws InterruptedException, NoSuchAlgorithmException,
            InvalidKeyException, NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException,
            NoSuchProviderException
   {
      System.out.println(BlowfishTest.encryptBlowfish("test", "test1111"));
      System.out.println(BlowfishTest.decryptBlowfish(BlowfishTest.encryptBlowfish("test", "test111"), "test111"));
   }

   public static String encryptBlowfish(final String toEncrypt, final String key) throws NoSuchAlgorithmException,
            NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException,
            NoSuchProviderException
   {
      final byte[] plainText = toEncrypt.getBytes();

      final SecretKeySpec blowfishKey = new SecretKeySpec(key.getBytes(), "Blowfish");
      final Cipher blowfishCipher = Cipher.getInstance("Blowfish");
      blowfishCipher.init(Cipher.ENCRYPT_MODE, blowfishKey);
      final byte[] cipherText = blowfishCipher.doFinal(plainText);
      final String cipher = BlowfishTest.convertBinary2Hexadecimal(cipherText);
      return cipher;
   }

   private static final String HEX_STRING = "0123456789ABCDEF";

   private static String convertBinary2Hexadecimal(final byte[] binary)
   {
      final StringBuffer buf = new StringBuffer();
      int block = 0;

      for (final byte element : binary)
      {
         block = element & 0xFF;
         buf.append(BlowfishTest.HEX_STRING.charAt(block >> 4));
         buf.append(BlowfishTest.HEX_STRING.charAt(element & 0x0F));
      }

      return buf.toString();
   }

   public static byte[] convertHexadecimal2Binary(final byte[] hex)
   {
      int block = 0;
      final byte[] data = new byte[hex.length / 2];
      int index = 0;
      boolean next = false;

      for (final byte element : hex)
      {
         block <<= 4;
         final int pos = BlowfishTest.HEX_STRING.indexOf(Character.toUpperCase((char) element));
         if (pos > -1)
            block += pos;

         if (next)
         {
            data[index] = (byte) (block & 0xff);
            index++;
            next = false;
         }
         else
            next = true;
      }

      return data;
   }

   public static String decryptBlowfish(final String toDecrypt, final String key) throws NoSuchAlgorithmException,
            NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException,
            NoSuchProviderException
   {
      String plain = null;
      final byte[] cipherText = BlowfishTest.convertHexadecimal2Binary(toDecrypt.getBytes());

      final SecretKeySpec blowfishKey = new SecretKeySpec(key.getBytes(), "Blowfish");
      final Cipher blowfishCipher = Cipher.getInstance("Blowfish");
      blowfishCipher.init(Cipher.DECRYPT_MODE, blowfishKey);
      final byte[] plainText = blowfishCipher.doFinal(cipherText);
      plain = new String(plainText);
      return plain;
   }
```

Quelle:
Decrypt Using Blowflish And Java | Tech Tips Tricks
Encrypt Using Blowflish And Java | Tech Tips Tricks


----------



## xerberuz (8. Sep 2011)

Eine andere Möglichkeit ist auch Base64 Base64 ? Wikipedia


----------



## heinz86 (8. Sep 2011)

Hallo,

danke für die Antwort. Der Beispielcode läuft auch astrein, aber es gibt Probleme, wenn ich das ganze mit dem Einlesen meiner Datei machen möchte. Dort scheint einiges nicht zu funktioniert. So lieft eine Textdatei mit dem Inhalt "Das ist nur ein Test" beim Entschlüsseln nur "Das ist nur". Irgendwie wird dort was abgeschnitten.

Ebenso funktioniert dies nur, wenn man decrypt(encrypt(...),...) aufruft. Also nicht erst verschlüsselt und dann die verschlüsselte Datei wieder einliest...

Dies sind meine beiden Methoden zum Lesen und Schreiben der Datei:

```
public String readFromFile(String filename) {
		BufferedReader bR = null;
		StringBuilder builder = new StringBuilder();
		String inputtext = null;

		try {
			bR = new BufferedReader(new FileReader(filename));

			while ((inputtext = bR.readLine()) != null) {
				builder.append(inputtext);
			}
		} catch (FileNotFoundException ex) {
			ex.printStackTrace();
		} catch (IOException ex) {
			ex.printStackTrace();
		} finally {
			try {
				if (bR != null)
					bR.close();
			} catch (IOException ex) {
				ex.printStackTrace();
			}
		}
		return builder.toString();
	}

	public void writeToFile(String textToWrite, String filename) {

		BufferedWriter bW = null;
		
		try {
			bW = new BufferedWriter(new FileWriter(filename));
			bW.write(textToWrite);
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			try {
				if (bW != null)
					bW.close();
			} catch (IOException ex) {
				ex.printStackTrace();
			}
		}

	}
```


----------



## Ulathar (9. Sep 2011)

Ich habe für mein Programm zwar nicht mit BlowFish sondern mit AES verschlüsselt, hatte aber beim Speichern und anschließendem Laden aber das selbe Problem.

Gelöst habe ich das durch ein umwandeln in HEX beim Speichern und ein zurück Umwandeln nach byte beim Parsen.

Hier mal das umwandeln von und zu HEX:


```
private static String asHex (byte buf[]) {
    	StringBuffer buffer = new StringBuffer(buf.length * 2);
    	int i;
    	
    	for (i = 0; i < buf.length; i++) {
    		if(((int)buf[i] & 0xff) < 0x10) {
    			buffer.append("0");
    		}
    		buffer.append(Long.toString((int) buf[i] & 0xff, 16));
     }
     return buffer.toString();
    }
```

Den so erzeugten Hex String kann man normal in eine textdatei schreiben und später wieder parsen.
Den geparsten String dann einfach mit:


```
public static byte[] fromHex(String s) {
        int size = s.length();
        byte[] bytes = new byte[size / 2];

        for (int i = 0; i < size; i += 2) {
            bytes[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
                                  + Character.digit(s.charAt(i + 1), 16));
        }
        return bytes;
    }
```

Das byte[] kannst du dann z.b. so wieder decoden:


```
Cipher cipher = Cipher.getInstance("BlowFish");			
cipher.init(Cipher.DECRYPT_MODE, key);
			
byte[] original = cipher.doFinal(fromHex(hexstring));
			
String s = new String(original);
```


Denk aber daran, dass du auch den korrekten SecretKeySpec benötigst damit du es korrekt entschlüsseln kannst!


Edit:
Was ich damit meine ist, dass wenn du z.b. jetzt gerade in diesem moment einen SecretKeySpec erzeugst über den KeyGenerator und den Cipher und mit diesem schlüssel deinen Text verschlüsselst.
Das resultat dann abspeicherst. Das Programm beendest und zu einem späteren Zeitpunkt wieder öffnest um dann den Text zu decrypten, kann das nicht funktionieren, da der für diesen zweck neu erstellte SecretKeySpec NICHT mehr identisch zu dem Schlüssel ist, mit dem du den kram VERschlüsselt hast.
Wenn du das also persistent machen willst, musst du dir den SecretKey auch mit abspeichern (oder gibs da elegantere lösungen?).

Das wäre dann natürlich eine Schwachstelle in der Sicherheit, da der lokale Schlüssel von Leuten die sich damit auskennen vermutlich ausgelesen/wiederhergestellt werden könnte. Dazu reicht mein Wissen auf dem Gebiet nicht aus, lerne gerade selbst noch .
Habe das bei mir daher so gelöst, dass ich wenn ich etwas verschlüsselt abspeicher, mir den SecretKeySpec über Serializable mit abspeicher und dann vorm entschlüsseln wieder herstelle.

Bei mir war das Ziel aber auch nur, dass der username und Passwort nicht mehr klar lesbar in einer Datei steht...

Hoffe ich konnt dir weiterhelfen.


----------



## Andi_CH (9. Sep 2011)

Ulathar hat gesagt.:


> Denk aber daran, dass du auch den korrekten SecretKeySpec benötigst damit du es korrekt entschlüsseln kannst!



Im allgemeinen Fall verschlüssle ich ja etwas hier und entschlüssle es am anderen Ende der Welt - wie kommt der Schlüssel dahin?
Oder ich entschlüssle es zu einem anderen Zeitpunkt?

SecretKeySpec tönt danach, dass es nicht gut ist, die über das internet zu übermitteln oder abzuspeichern - wie macht man das?


----------



## Ulathar (9. Sep 2011)

wie gesagt, ich bin selber cryptoneuling und will an dieser stelle nix falsches sagen, bzw halbwahrheiten verbreiten.

ich bin mir jedoch sehr sicher, dass es aus sicherheitstechnischen gründen vermutlich KEINE gute idee ist, den key, mit dem man etwas verschlüsselt so zu speichern, dass "der feind" diesen problemlos finden/auslesen kann. allerdings wüsste ich auch nicht, wie man OHNE eine speicherung des schlüssels etwas zu einem späteren zeitpunkt (oder einer anderen stelle, z.b. anderer pc oder so) wieder entschlüsseln will.

dies ist allerdings immer und überall wo man etwas verschlüsselt die voraussetzung, um es wieder entschlüsseln zu können! nur verstecken "professionelle" lösungen diesen key in irgend einer weise (z.b. in einer anderen datei, oder in form eines passwortes aus dem der schlüssel generiert wird um das eigendliche passwort wieder her zu stellen, etc pp).


habe das daher bei mir so gelöst:

Mein Programm erzeugt bei bedarf (also wenn man etwas verschlüsseln will) eine instanz einer von mir geschriebenen klasse, die ein array aus X SecretKeySpecs erstellt.
Dieses Array füllt es dann mit X zufällig generierten Keys.

Anschließend bestimme ich "zufällig" 2 Keys aus dem array mit denen ich dann den benutzername und das passwort verschlüssel.

Diese erwähnte Klasse, die die Keys hält implementiert das serializable interface, so dass ich den zustant des objects persitent speichern kann. dies geschieht immer dann, wenn ich einen username und/oder passwort in eine datei schreibe.

Starte ich dann z.b. das Programm neu, prüft es, ob login informationen vorliegen UND ob eine keyfile vorhanden ist. wenn beides der fall ist, lade ich über einen Objectstream das keyfile und habe wieder exakt die selben X keys wie zum zeitpunkt der verschlüsselung, so dass ich username und passwort entschlüsseln kann.


Diese lösung hat natürlich diverse schwächen:
1. leute die sich damit auskennen dürften sehr einfach in der lage sein das keyfile zu rekonstruieren und somit die keys auszulesen. dann müssen sie "nur" noch durchprobieren welcher der X keys zu welchem verschlüsselten String passt
2. wenn ein anderer benutzer z.b. am selben pc sitzt und dort noch ein login + keyfile gespeichert ist, kann dieser sich natürlich problemlos als der vorherige user ausgeben (das ist dann aber die schuld des vorherigen benutzers)

aber wie gesagt, mein ziel war es eh nur dafür zu sorgen, dass im login file nicht in klar text steht
user = susi sorglos
password = asdf

sondern sowas wie 
user = 0as0fas024010696as5f6s4f7
password = 986s5674764asf47a5sf356


Beispiel wie ich die Keys erzeuge (hier allerdings AES):


```
KeyGenerator kgen = KeyGenerator.getInstance("AES");
kgen.init(128); 	// degree of encryption (the number of Bits)
			
// Generate the secret key specs.
SecretKey key = kgen.generateKey();						
byte[] raw = key.getEncoded();
			
SecretKeySpec keySpec = new SecretKeySpec(raw, "AES");
			
keyspecs[0] = keySpec;
```


So encode ich dann:


```
// Instantiate the cipher
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, key);
			
byte[] encrypted = cipher.doFinal(s.getBytes());
			
return asHex(encrypted);
```


Und so decode ich wieder:

```
// Instantiate the cipher
Cipher cipher = Cipher.getInstance("AES");			
cipher.init(Cipher.DECRYPT_MODE, key);
			
byte[] original = cipher.doFinal(fromHex(s));
			
String originalString = new String(original);
```

Dabei ist "key" der jeweilige SecretKeySpec aus dem keyspecs[X] array.


----------



## heinz86 (16. Sep 2011)

Guten Morgen!

Ich kämpfe immer noch ein wenig mit meinem Programm. Denn an irgendeiner Stelle gehen mir Informationen verloren und ich kann derzeit noch nicht nachvollziehen, an welcher Stelle dies passiert. Denn wenn ich eine Datei einlese, wird der vollständige String erzeugt, dies könnte ich nachvollziehen. Dann wird verschlüsselt und wenn ich das verschlüsselte wieder aus der geschriebenen Datei einlesen und entschlüsseln möchte, fehlt auf einmal die Hälfte des Textes.

Beispiel: Ich verschlüssele den Text "Dies ist nur ein Test." und entschlüssle anschließend dies wieder, dann erhalte ich nur als Antwort "Dies ist nur".

Vielleicht hat ja jemand einen siebten Sinn und sieht, woran dies liegen könnte.


Zum Lesen und schreiben verwende ich diese beiden Methoden:

```
public String readFromFile(String filename) {
		BufferedReader bR = null;
		StringBuilder builder = new StringBuilder();
		String inputtext = null;

		try {
			bR = new BufferedReader(new FileReader(filename));

			while ((inputtext = bR.readLine()) != null) {
				builder.append(inputtext);
			}
		} catch (FileNotFoundException ex) {
			ex.printStackTrace();
		} catch (IOException ex) {
			ex.printStackTrace();
		} finally {
			try {
				if (bR != null)
					bR.close();
			} catch (IOException ex) {
				ex.printStackTrace();
			}
		}
		return builder.toString();
	}

	public void writeToFile(String textToWrite, String filename) {

		BufferedWriter bW = null;
		try {
			bW = new BufferedWriter(new FileWriter(filename));
			bW.write(textToWrite);
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			try {
				if (bW != null)
					bW.close();
			} catch (IOException ex) {
				ex.printStackTrace();
			}
		}
	}
```

Das Verschlüsseln geschieht mit dem folgenden Textfragment:

```
try {
  ContentWorker cw = new ContentWorker();
  String input = cw.readFromFile(inputField.getText());

  String ciphertext = Blowfish.encryptInput(keyField.getText(), input);

  cw.writeToFile(ciphertext, inputField.getText() + ".blf");

} catch (Exception e1) {
    // TODO Auto-generated catch block
    e1.printStackTrace();
}
```

Das Entschlüsseln verläuft analog mit folgendem Code:

```
try {
  ContentWorker cw = new ContentWorker();
  String input = cw.readFromFile(inputField.getText());

  String plaintext = Blowfish.decryptInput(keyField.getText(), input);

  cw.writeToFile(plaintext, inputField.getText().replace(".blf", ".2"));

} catch (Exception e1) {
    // TODO Auto-generated catch block
    e1.printStackTrace();
}
```


Hier noch der Code für das Ver- bzw. Entschlüsseln:

```
public static String encryptInput(String key, String input)
			throws Exception {

		Cipher c = Cipher.getInstance("Blowfish");
		Key k = new SecretKeySpec(key.getBytes(), "Blowfish");
		c.init(Cipher.ENCRYPT_MODE, k);

		ByteArrayOutputStream baos = new ByteArrayOutputStream();
		CipherOutputStream cos = new CipherOutputStream(baos, c);
		byte[] inputBytes = input.getBytes();
		cos.write(inputBytes);
		cos.close();
		baos.close();

		return baos.toString();
	}

	public static String decryptInput(String key, String input)
			throws Exception {

		BufferedInputStream in = new BufferedInputStream(
				new ByteArrayInputStream(input.getBytes()));

		Cipher c = Cipher.getInstance("Blowfish");
		Key k = new SecretKeySpec(key.getBytes(), "Blowfish");
		c.init(Cipher.DECRYPT_MODE, k);
		CipherInputStream cis = new CipherInputStream(in, c);
		StringBuffer decrypted = new StringBuffer();
		int intInput = cis.read();
		while (intInput != -1) {
			decrypted.append((char) intInput);
			intInput = cis.read();
		}
		cis.close();
		in.close();
		return decrypted.toString();
	}
```


----------



## xerberuz (16. Sep 2011)

Erstmal zum auslesen aus der Datei. Wenn du readLine verwendest gehen Zeilenumbrüche verloren.

```
bR = new BufferedReader(new FileReader(filename));
 
            while ((inputtext = bR.readLine()) != null) {
                builder.append(inputtext);
            }
```


Und zum Verschlüsselungsproblem: Es wurde bereits weiter oben angesprochen, dass du das byte Array in einen String umwandeln musst. Auf dem byte array toString aufrufen funktioniert nicht wenn da binärer Schrott drin ist (wie es bei einer Verschlüsselung der Fall ist)

Schau dir mal Base64 an. Galileo Computing :: Java ist auch eine Insel – 4.8 Zeichenkodierungen und Base64


----------



## TheRealSpikee (16. Sep 2011)

Das dir "Informationen" verloren gehen liegt an dem Verfahren : NoPadding. Auch ist das Problem das du SOFORT doFinal() aufrufst. Das ist aber für Daten die Länger als die Blockgröße sind falsch.
Eine halbwegs vernünftige Crypto-Methode :

```
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());
}
```
Was den SecretKey angeht : erzeugen doch einfach mal ZWEI Objekte und checke ob das eine dem anderen entspricht. Es bringt dir ja nichts wenn du zwei verschiedene Keys hast ...


----------



## heinz86 (16. Sep 2011)

xerberuz hat gesagt.:


> Erstmal zum auslesen aus der Datei. Wenn du readLine verwendest gehen Zeilenumbrüche verloren.
> 
> ```
> bR = new BufferedReader(new FileReader(filename));
> ...



Hallo,

was soll ich denn als Alternative zu readLine() verwenden, damit mir nicht die Zeilenumbrüche verloren gehen?
Habe mir das Beispiel aus dem Buch einmal angeschaut, es lässt sich auch kompilieren, aber Eclipse meckert, dass er mit 
	
	
	
	





```
new BASE64Encoder().encode( bytes1 );
```
 bzw. 
	
	
	
	





```
new BASE64Decoder().decodeBuffer( s );
```
 nicht so viel anfangen kann. Es lassen sich z.B. auch nicht die Pakete 
	
	
	
	





```
sun.misc.BASE64Decoder;
```
 und 
	
	
	
	





```
sun.misc.BASE64Encoder;
```
einbinden.

Jemand eine Idee?
Ich finde BAES


----------



## Ulathar (17. Sep 2011)

Warum benutzt du denn nicht die von mir oben vorgeschlagene Lösung über ein Umwandeln von und nach HEX values?
Hab sogar den dazu notwendigen Code oben gepostet, das klappt wunderbar und man muss sich nicht die Mühen über BASE64 machen...

Und soweit ich korrekt informiert bin ist BASE64 teil von Apache und nicht (mehr?) "per default" im JDK enthalten. Brauchst also externe Libs (bei mir findet er es nämlich auch nicht, JDK 1.6.0_27).
Das war auch der Grund, wieso ich bei mir es "von Hand" über die HEX-Lösung realisiert habe.


BASE64 gibs z.B. hier (erster Treffer bei Google...):
Codec - Download Commons Codec

Die Sun Variante (die du oben probierst zu importieren) ist jedenfalls im JDK 1.4.2 enthalten laut diverser Quellen. Wieso es das in neueren Versionen nicht mehr gibt (oder anders heißt?) weiß ich nicht, ist mir auch egal.


Edit:
korrigiere: es gibt scheinbar ca. so viele base64 Implementationen wie Sand am Meer... Such dir die aus, die dir am besten gefällt.


----------



## heinz86 (17. Sep 2011)

Danke für die Antwort. Läuft astrein mit deinen beiden Funktionen.

Mein Problem ist jetzt noch, dass das Einlesen der Datei vielleicht noch optimiert werden könnte. Ich weiß nur noch nicht wie. Es kam ja der Hinweis, dass ich readLine() nicht verwenden sollte. Aber was wäre denn eine Alternative?


----------



## Ulathar (17. Sep 2011)

oh da gibs sehr viele möglichkeiten.
denkbar wäre z.b. ein StreamTokenizer oder ähnliches.

hab jetzt gerade verdammt wenig zeit, sonst würd ich dir beispiel code posten, aber evtl kannste ja noch mal zusammengefasst sagen was genau du machen willst (also welche art von daten du aus was für einer datei einlesen willst, struktur etc). evtl komm ich morgen oder übermorgen kurz dazu das näher auszuführen.

alternativ kannst du natürlich auch bei deinem readline bleiben, musst dir dann aber irgend eine art "cleverer schleife" ausdenken, so dass du sicherstellst immer alles einzulesen was auch wirklich zusammengehört, sollte dies in der datei über mehr als 1 zeile laufen.

so lang du nich zig MB in der Textdatei hast die du ständig lesen/schreiben willst ist auch das kriterium performance quasi egal ^^.


----------



## xerberuz (17. Sep 2011)

So ganz spontan. Solltest aber nochmal testen bevor dus verwendest.


```
private static String readFileToString(File file) throws IOException {
		Reader reader = null;
		try {
			reader = new BufferedReader(new FileReader(file));
			StringWriter writer = new StringWriter();
			for (int r; (r = reader.read()) != -1;) {
				writer.write(r);
			}
			return writer.toString();
		} finally  {
			if(reader != null) {
				try {
					reader.close();
				} catch (IOException e) {
				}
			}
		}
	}
```


----------

