# SSL Socket zu IRC Server aufbauen?



## sh (17. Jan 2011)

Hi!

Ich wollte - in erster Linie zur Übung - einen kleinen IRC-Channel-Bot schreiben. Funktioniert alles auch prima, kann für
bestimmte User bestimmte Commands erlauben, wie zB. "!quote", woraufhin der Bot dann ein zufälliges Zitat ausspuckt.

Nur jetzt wollt ich das ganze auch mit optionalem SSL versehen, da manche Server die Clients nur über SSL connecten lassen.

Soweit hab ich das ganze mal angefangen:


```
public void connectSSL(String serv, int port) {
		try {
            
		    SocketFactory socketFactory = SSLSocketFactory.getDefault();
		    Socket socket = socketFactory.createSocket(serv, port);

		    BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
		    
		    String data;
		    while((data = br.readLine()) != null) {
		    	System.out.println(data);
		    	//parse data here
		    }
		    
		    socket.close();
		    br.close();

		} catch(IOException e) {
			System.err.print(e);
		}

	}
```


Und sogleich diesen Fehler hier bekommen:

```
javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
```

Also erstmal Google gefragt und herausgefunden:
- Die Methode ansich baut schon eine sichere Verbindung auf
- Manche IRC Server (scheinbar auch der, auf den ich meinen Bot connecten lassen will) senden zuerst eine art "Test"-Zertifikat welches man dann irgendwie speichern und "laden" muss


Heady86 hat hier auch dieses Problem:
http://www.java-forum.org/soa/107453-ssl-verbindung-jupload.html
Allerdings noch keine Antworten zu seinem Thread.

Brauche ich, um auf den SSL Port eines IRCd connecten zu können eine Art HandShake am Anfang? Wenn ja, wie genau stellt man das an? Wie speicher ich dieses "Test"-Zertifikat?
Wäre dankbar für den link zu einem guten Tutorial oder einen kurzen Beispielcode anhand dessen ich nachvollziehen kann was da gemacht wird und wie das funktioniert.


Gruß


----------



## Murray (17. Jan 2011)

Bei SSL sendet der Server ein Zertifikat, mit dem er bestatigt, das er auch wirklich der ist, der er zu sein vorgibt. Es obliegt dann dem Client, diesem Zertifikat zu trauen oder nicht.
In Java es ist so, dass das "Vertrauen" darin besteht, dass  sich das jeweilige Zertifikat im Keystore befindet. 
Man kann daher
 - mit dem Browser der Wahl die HTTPs-URL angehen
 - danach aus dem Brower das Zertifkat n eine Datei exportieren
 - diese Datei mit keytool.exe (beim JDK und bei der JRE dabei) in den Keystore übernehmen


----------



## sh (17. Jan 2011)

Ist das auch direkt in meinem Programm automatisiert moeglich? 

Da der Bot auf mehrere Netzwerke gleichzeitig connecten soll und mindestens 2 davon SSL benutzen und eventuell in Zukunft noch mehr Netzwerke dazukommen hätt ichs lieber alles automatisiert, also das ich nichts mehr von Hand dazu beisteuern muss, wie zB. das Zertifikat exportieren etc.

Vielen Dank für die Informationen das hilft schon weiter, da muss ich wohl Google nochmal nach SSL Referenz befragen.


----------



## FArt (17. Jan 2011)

java automatically import self signed certificate - Google-Suche


----------



## sh (18. Jan 2011)

FArt hat gesagt.:


> java automatically import self signed certificate - Google-Suche



Auf die Idee bin ich auch schon gekommen, allerdings hab ich nur Beispiele für "Java KeyTool" gefunden - was natürlich nicht voll automatisch funktioniert.. Da muss ich selber das Zertifikat in die trustStore eintragen, soweit ich das verstanden hab, oder lieg ich da falsch?

Auch steht bischen was zu HttpURLConnection-Zertifikate "einfangen", allerdings connecte ich ja auf ein IRC Server und nicht auf eine Website.

Danke trotzdem!


----------



## FArt (18. Jan 2011)

Keytool nutzt die öffentliche Schnittstelle, die man auch programmatisch ansprechen kann. Ein Beispiel mit IrC wirst du wohl kaum finden, aber wenn der Server zuerst das selbstsignierte Zertifikat sendet, dann hast du es ja zur Verfügung.
KeyStore (Java 2 Platform SE v1.4.2)


----------



## sh (18. Jan 2011)

Ah jetz versteh ich das ganze langsam! Ja, danke, ich werds gleich mal ausprobieren wenn ich mal ne Stunde freizeit hab.

Grüße


----------



## sh (30. Jan 2011)

So, jetzt hab ich wieder mehr Zeit. Also, hab mal nochmal bei google geschaut und folgendes gefunden:
No more PKIX PATH Exceptions
InstallCert Example

Wenn ich das InstallCert.java nun ausfuehre sagt es mir es hat das Unsigned Certificate (nach dem hand-shake) in "cacerts" eingetragen.

Gut so weit, nur wie lade ich jetzt das Zertifikat wenn ich eine sichere Verbindung zu dem Server aufbauen will, dessen Zertifikat ich eben mit InstallCert gespeichert habe, aus dem KeyStore so das mein Programm sich mit diesem Zertifikat am Server anmeldet?

Kurz: Wie baue ich mit hilfe dieses Zertifikates jetzt eine SSLSocket-Verbindung zum Server auf?


----------



## HoaX (30. Jan 2011)

Wenn du, wie ich vermute, keinen bedarf hast das Zertifikat zu prüfen und allen trauen willst, dann könntest du auch einen eigene SSLSocketFactory nehmen mit angepassten TrustManager. z.B. Bits and Codes: Create a Trust Manager that trust all the SSL certificates for Java


----------



## sh (31. Jan 2011)

Deine Idee mit dem "eigenen TrustManager" hat mich einiges weiter gebracht, es scheint das der Socket jetzt eine Verbindung herstellen kann, ich bekomme auch die erste Zeile des IRC Protokolls zurück, nur
Komischerweise kann ich nichts senden, bzw. wenn ich etwas sende passiert einfach nichts.

Hier mal mein Test-Code:


```
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintStream;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;


public class Main {
	public static void main(String... args) {
	
		// Create a trust manager that does not validate certificate chains
		TrustManager[] trustAllCerts = new TrustManager[]{
		    new X509TrustManager() {
		        public java.security.cert.X509Certificate[] getAcceptedIssuers() {
		            return null;
		        }
		        public void checkClientTrusted(
		            java.security.cert.X509Certificate[] certs, String authType) {
		        }
		        public void checkServerTrusted(
		            java.security.cert.X509Certificate[] certs, String authType) {
		        }
		    }
		};

		// Install the all-trusting trust manager
		try {
		    SSLContext sc = SSLContext.getInstance("SSL");
		    sc.init(null, trustAllCerts, new java.security.SecureRandom());
			BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
			PrintStream out = System.out;
		    SSLSocketFactory f = (SSLSocketFactory) sc.getSocketFactory();
		      try {
		         SSLSocket c = (SSLSocket) f.createSocket("irc.link-net.org", 7000);
		         c.startHandshake();
		         BufferedWriter w = new BufferedWriter(new OutputStreamWriter(c.getOutputStream()));
		         BufferedReader r = new BufferedReader(new InputStreamReader(c.getInputStream()));
		         String m = null;
		         
		         while ((m=r.readLine())!= null) {
		        	out.println(m);
		        	if(m.contains("NOTICE AUTH :***")) {
				        sendRaw(c, "USER SMtest SMtest SMtest :SMTest");
				        sendRaw(c, "NICK SMtest");
		        	}
		            m = in.readLine();
		            w.write(m,0,m.length());
		            w.newLine();
		            w.flush();
		         }
		         w.close();
		         r.close();
		         c.close();
		      } catch (IOException e) {
		         System.err.println(e.toString());
		      }
		} catch (Exception e) {
			e.printStackTrace();
		}
		
	}
	
	public static void sendRaw(SSLSocket socket, String command, Object... args) {
		try {
			BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
			bw.write(String.format(command, args));
			System.out.println("RAW -> "+String.format(command, args));
			bw.newLine();
			bw.flush();
		} catch(IOException e) {
			e.printStackTrace();
		}
	}
	
}
```

Normalerweise sollte der Nutzer nach USER und NICK event am Chat-Server angemeldet sein und für andere Nutzer sichtbar werden.

Allerdings gehts bei mir einfach nicht weiter. Geh ich die Sache vll Komplett falsch an? Die funktion sendRaw() funktioniert bei meinem Original-Projekt und "normalem" Socket, also keine SSL Verbindung, prima.


----------



## sh (1. Feb 2011)

Ihr glaubt nicht worans lag.. am Routing zum Zielserver. Ich habe immer die ersten 2, 3 Protokoll-Zeilen empfangen und danach wars tot. Also hab ich mal die gleiche DNS benutzt mit mIRC und rausgefunden das die auf verschiedene zufällige IP-Adressen zeigt, wovon manche tot waren..

Also es funkt jetzt, hier nochmal für diejenigen die mal das gleiche Problem haben:


```
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.security.cert.X509Certificate;

import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

public class Example {
	public static void main(String[] args) throws Exception {
		TrustManager[] trustAllCerts = new TrustManager[] {new X509TrustManager() {
				public java.security.cert.X509Certificate[] getAcceptedIssuers() {
					return null;
				}
				public void checkClientTrusted(X509Certificate[] certs, String authType) {
				}
				public void checkServerTrusted(X509Certificate[] certs, String authType) {
				}
			}
		};


		SSLContext sc = SSLContext.getInstance("SSL");
		sc.init(null, trustAllCerts, new java.security.SecureRandom());
		

		
		SSLSocketFactory sslF = sc.getSocketFactory();
		SSLSocket sock = (SSLSocket) sslF.createSocket("irc.link-net.org", 7000);

		BufferedReader br = new BufferedReader(new InputStreamReader(sock.getInputStream()));
		BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(sock.getOutputStream()));
		
		bw.write("USER SMtest SMtest SMtest :SMtest");
		bw.newLine();
		bw.flush();
		bw.write("NICK SMtest\r\n");
		bw.newLine();
		bw.flush();
		
		String str = null;
		while((str = br.readLine()) != null) {
			
			System.out.println(str);
		}

	}
}
```

Danke an alle für die Hilfe


----------

