2016-04-24 3 views
1

I-Client-Zertifikat-Authentifizierung mit einem Server auf Java 1.7 eingebauten in HttpsServer.Wie mit com.sun.net.httpserver.HttpsServer Client-Zertifikat verlangen

ich irgendeine Art und Weise zu finden, kann nicht scheinen, basierend verlangen will Veranlasse den Server die Authentifizierung fehlzuschlagen. Es wird fröhlich Daten an jeden alten Client überhaupt liefern, egal ob sein Client-Zertifikat vertrauenswürdig, unbekannt oder völlig abwesend ist.

Meine Lektüre der Dokumente schlägt vor, dass setting HttpsParameters.setNeedClientAuth(true) einen Authentifizierungsfehler verursachen sollte, wenn der Client nicht vertrauenswürdig ist. Ich habe Notizen von Leuten mit ähnlichen Problemen gefunden, die die Verwendung desselben Flags in SSLEngine und SSLParameters unterschiedlich vorschlagen, aber beide haben das Verhalten für mich überhaupt nicht geändert.

Hier ist das einfachste Beispiel, das ich erstellen konnte. Betrachtet man das Fleisch der Transaktion (mit Wireshark oder -Djavax.net.debug = all), sehe ich nichts, was offensichtlich wie eine Zertifikatsanforderung vom Server aussieht ... das scheint natürlich offensichtlich, da es darauf reagiert sollte nicht.

Ich bin relativ neu sowohl Java und SSL beide. Missverstehe ich den Authentifizierungsprozess? Benutze ich in geeigneten Bibliotheken? Übersehen wir einen guten Weg, dies zu beheben? Vielen Dank!

Edit 1: Aktualisierter Beispielcode, um den Client Keystore und Truststore ordnungsgemäß zu trennen. Auch umformulierte Frage, um das Authentifizierungsproblem klarer zu machen.

package authserv; 

import java.io.BufferedReader; 
import java.io.FileInputStream; 
import java.io.IOException; 
import java.io.InputStreamReader; 
import java.io.OutputStream; 
import java.net.InetSocketAddress; 
import java.net.URL; 
import java.security.KeyStore; 

import javax.net.ssl.HttpsURLConnection; 
import javax.net.ssl.KeyManager; 
import javax.net.ssl.KeyManagerFactory; 
import javax.net.ssl.SSLContext; 
import javax.net.ssl.SSLParameters; 
import javax.net.ssl.SSLSession; 
import javax.net.ssl.SSLSocketFactory; 
import javax.net.ssl.TrustManagerFactory; 

import com.sun.net.httpserver.HttpExchange; 
import com.sun.net.httpserver.HttpHandler; 
import com.sun.net.httpserver.HttpsConfigurator; 
import com.sun.net.httpserver.HttpsExchange; 
import com.sun.net.httpserver.HttpsParameters; 
import com.sun.net.httpserver.HttpsServer; 

public class AuthServer { 
    final static String SERVER_PWD = "aaaaaa"; 
    final static String KST_SERVER = "keys/server.jks"; 
    final static String TST_SERVER = "keys/servertrust.jks"; 

    public static HttpsServer server; 

    public static void main(String[] args) throws Exception { 
     server = makeServer(); 
     server.start(); 
     //System.out.println("Server running, hit enter to stop.\n"); System.in.read(); 

     AuthClient cl = new AuthClient(); 
     cl.testIt(); 

     server.stop(0); 
    } 

    public static HttpsServer makeServer() throws Exception { 
     server = HttpsServer.create(new InetSocketAddress(8888), 0); 

     //server.setHttpsConfigurator(new HttpsConfigurator(SSLContext.getInstance("TLS"))); // Default config with no auth requirement. 
     SSLContext sslCon = createSSLContext(); 
     MyConfigger authconf = new MyConfigger(sslCon); 
     server.setHttpsConfigurator(authconf); 

     server.createContext("/auth", new HelloHandler()); 
     return server; 
    } 
    private static SSLContext createSSLContext() { 
     SSLContext sslContext = null; 
     KeyStore ks; 
     KeyStore ts; 

     try{ 
      sslContext = SSLContext.getInstance("TLS"); 

      ks = KeyStore.getInstance("JKS"); 
      ks.load(new FileInputStream(KST_SERVER), SERVER_PWD.toCharArray()); 
      KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509"); 
      kmf.init(ks, SERVER_PWD.toCharArray()); 

      ts = KeyStore.getInstance("JKS"); 
      ts.load(new FileInputStream(TST_SERVER), SERVER_PWD.toCharArray()); 
      TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509"); 
      tmf.init(ts); 

      sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null); 


     } catch (Exception e) { 
      e.printStackTrace(); 
     }  
     return sslContext; 
    } 
} 

class MyConfigger extends HttpsConfigurator { 
    public MyConfigger(SSLContext sslContext) { 
     super(sslContext); } 

    @Override 
    public void configure(HttpsParameters params) { 
     SSLContext sslContext = getSSLContext(); 
     SSLParameters sslParams = sslContext.getDefaultSSLParameters(); 
     sslParams.setNeedClientAuth(true); 
     params.setNeedClientAuth(true); 
     params.setSSLParameters(sslParams); 
     super.configure(params); 
    /* Other configure options that don't seem to help: 
     SSLEngine engine = sslContext.createSSLEngine(); 
     engine.setNeedClientAuth(true); 
     params.setCipherSuites (engine.getEnabledCipherSuites()); 
     params.setProtocols (engine.getEnabledProtocols()); 
    */ 
    } 
} 

class HelloHandler implements HttpHandler { 
    public void handle(HttpExchange t) throws IOException { 
     HttpsExchange ts = (HttpsExchange) t; 
     SSLSession sess = ts.getSSLSession(); 
     //if(sess.getPeerPrincipal() != null) System.out.println(sess.getPeerPrincipal().toString()); // Principal never populated. 

     t.getResponseHeaders().set("Content-Type", "text/plain"); 
     t.sendResponseHeaders(200,0); 
     String response = "Hello! You seem trustworthy!\n"; 
     OutputStream os = t.getResponseBody(); 
     os.write(response.getBytes()); 
     os.close(); 
    } 
} 


class AuthClient{ 
    static String KEYSTORE = ""; 
    static String TRUSTSTORE = "keys/clienttrust.jks"; 
    static String CLIENT_PWD = "aaaaaa"; 

    public static void main(String[] args) throws Exception { 
     KEYSTORE = "keys/unauthclient.jks"; // Doesn't exist in server trust store, should fail authentication. 
     //KEYSTORE = "keys/authclient.jks"; // Exists in server trust store, should pass authentication. 

     AuthClient cl = new AuthClient(); 
     cl.testIt(); 
    } 

    public void testIt(){ 
     try { 
      String https_url = "https://localhost:8888/auth/"; 
      URL url; 
      url = new URL(https_url); 
      HttpsURLConnection conn = (HttpsURLConnection)url.openConnection(); 
      conn.setSSLSocketFactory(getSSLFactory()); 

      conn.setRequestMethod("POST"); 
      conn.setDoOutput(true); 
      conn.setUseCaches(false); 

      // Print response 
      BufferedReader bir = new BufferedReader(new InputStreamReader(conn.getInputStream())); 
      String line = null; 
      while((line = bir.readLine()) != null) { 
        System.out.println(line); 
       } 
      bir.close(); 
      conn.disconnect(); 

     } catch (Exception e) { 
      e.printStackTrace(); 
     } 
    } 

    private static SSLSocketFactory getSSLFactory() throws Exception { 
     // Create key store 
     KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); 
     KeyManager[] kmfs = null; 
     if(KEYSTORE.length() > 0) { 
      keyStore.load(new FileInputStream(KEYSTORE), CLIENT_PWD.toCharArray()); 
      KeyManagerFactory kmf = KeyManagerFactory.getInstance(
         KeyManagerFactory.getDefaultAlgorithm()); 
      kmf.init(keyStore, CLIENT_PWD.toCharArray()); 
      kmfs = kmf.getKeyManagers(); 
     } 

     // create trust store (validates the self-signed server!) 
     KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType()); 
     trustStore.load(new FileInputStream(TRUSTSTORE), CLIENT_PWD.toCharArray()); 
     TrustManagerFactory trustFactory = TrustManagerFactory.getInstance(
         TrustManagerFactory.getDefaultAlgorithm()); 
     trustFactory.init(trustStore); 

     SSLContext sslContext = SSLContext.getInstance("TLS"); 
     sslContext.init(kmfs, trustFactory.getTrustManagers(), null); 
     return sslContext.getSocketFactory(); 
    } 
} 

Hier ist ein Bash-Skript zum Erstellen der erforderlichen Zertifikate und Keystores.

#!/bin/bash 
LOCALNAME=localhost 
PASS=aaaaaa 

function main 
{ 
    gen 
    list 
} 

function gen 
{ 
mkdir -p keys 
rm -f authclient.cert authclient.jks server.cert server.jks servertrust.jks clienttrust.jks unauthclient.jks 

# create the keypairs for authclient, unauthclient and for server. 
keytool -genkey -alias server -keyalg RSA -keystore server.jks -validity 365 -dname "cn=$LOCALNAME, ou=Auth, o=Auth, c=CA" -storepass $PASS -keypass $PASS 
keytool -genkey -alias authclient -keyalg RSA -keystore authclient.jks -validity 365 -dname "cn=$LOCALNAME, ou=Auth, o=Auth, c=CA" -storepass $PASS -keypass $PASS 
keytool -genkey -alias unauthclient -keyalg RSA -keystore unauthclient.jks -validity 365 -dname "cn=$LOCALNAME, ou=Auth, o=Auth, c=CA" -storepass $PASS -keypass $PASS 

keytool -export -file server.cert -keystore server.jks -storepass $PASS -alias server 
keytool -export -file authclient.cert -keystore authclient.jks -storepass $PASS -alias authclient 

# Create a bare client truststore with no keypair 
echo yes | keytool -import -file server.cert -alias server -keystore clienttrust.jks -storepass $PASS 

# Create a truststore for the server containing ONLY authclient 
echo yes | keytool -import -file authclient.cert -alias authclient -keystore servertrust.jks -storepass $PASS 

# Add the server's cert to the client's keystores 
#echo yes | keytool -import -file server.cert -alias server -keystore authclient.jks -storepass $PASS 
#echo yes | keytool -import -file server.cert -alias server -keystore unauthclient.jks -storepass $PASS 
} 

function list { 
for x in *.jks; do 
    SER=$(keytool -list -v -keystore $x -storepass aaaaaa | grep Serial) 
    echo $x $SER 
done 
} 
main 
+1

Ihr Kunde wird nicht mit einem konfigurierten Schlüsselspeicher ausgeführt wird, so kann es ein Zertifikat nicht senden, ob sie gefragt wird eins oder nicht. – EJP

+0

Das Ausführen des Clients mit "unauthclient.jks" (ein ordnungsgemäßer Schlüsselspeicher mit einem nicht erkannten Client-Zertifikat) hat genau das gleiche Verhalten. Aber in jedem Fall sollte der Server sicher eine Verbindung von einem Client ablehnen, der sich nicht authentifizieren kann? –

+1

Sie führen Ihren Client mit einem Truststore und keinen Keystore aus. Es kann nirgends einen privaten Schlüssel oder ein entsprechendes Zertifikat erhalten. – EJP

Antwort

0

Am Ende gab es mehrere Probleme. - Das Problem mit der API-Berechtigung wurde durch die Gradle-Einstellung eines ungewöhnlich qualifizierten JDKs verursacht. - Das gleiche JDK-Problem verursachte Probleme mit SSLparams - In meinem anfänglichen Beispiel wurde der Truststore nicht festgelegt.

Von Interesse, ich änderte schließlich auf willClientAuth (true), und authentifiziert in der Handler.

package authserv; 

import java.io.FileInputStream; 
import java.io.IOException; 
import java.io.OutputStream; 
import java.net.InetSocketAddress; 
import java.security.KeyStore; 

import javax.net.ssl.KeyManagerFactory; 
import javax.net.ssl.SSLContext; 
import javax.net.ssl.SSLParameters; 
import javax.net.ssl.SSLSession; 
import javax.net.ssl.TrustManagerFactory; 

import com.sun.net.httpserver.HttpExchange; 
import com.sun.net.httpserver.HttpHandler; 
import com.sun.net.httpserver.HttpsConfigurator; 
import com.sun.net.httpserver.HttpsExchange; 
import com.sun.net.httpserver.HttpsParameters; 
import com.sun.net.httpserver.HttpsServer; 

public class AuthServer { 
    final static String SERVER_PWD = "aaaaaa"; 
    final static String KST_SERVER = "keys/server.jks"; 
    final static String TST_SERVER = "keys/servertrust.jks"; 

    public static HttpsServer server; 

    public static void main(String[] args) throws Exception { 
     server = makeServer(); 
     server.start(); 

     System.out.println("Server running, hit enter to stop.\n"); System.in.read(); 
     //AuthClient cl = new AuthClient(); cl.testIt(); 

     server.stop(0); 
    } 

    public static HttpsServer makeServer() throws Exception { 
     server = HttpsServer.create(new InetSocketAddress(8888), 0); 

     //server.setHttpsConfigurator(new HttpsConfigurator(SSLContext.getInstance("TLS"))); // Default config with no auth requirement. 
     SSLContext sslCon = createSSLContext(); 
     MyConfigger authconf = new MyConfigger(sslCon); 
     server.setHttpsConfigurator(authconf); 

     server.createContext("/auth", new HelloHandler()); 
     return server; 
    } 
    private static SSLContext createSSLContext() { 
     SSLContext sslContext = null; 
     KeyStore ks; 
     KeyStore ts; 

     try{ 
      sslContext = SSLContext.getInstance("TLS"); 

      ks = KeyStore.getInstance("JKS"); 
      ks.load(new FileInputStream(KST_SERVER), SERVER_PWD.toCharArray()); 
      KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509"); 
      kmf.init(ks, SERVER_PWD.toCharArray()); 

      ts = KeyStore.getInstance("JKS"); 
      ts.load(new FileInputStream(TST_SERVER), SERVER_PWD.toCharArray()); 
      TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509"); 
      tmf.init(ts); 

      sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null); 

     } catch (Exception e) { 
      e.printStackTrace(); 
     }  
     return sslContext; 
    } 
} 

class MyConfigger extends HttpsConfigurator { 
    public MyConfigger(SSLContext sslContext) { 
     super(sslContext); } 

    @Override 
    public void configure(HttpsParameters params) { 
     SSLContext sslContext = getSSLContext(); 
     SSLParameters sslParams = sslContext.getDefaultSSLParameters(); 
     sslParams.setNeedClientAuth(true); 
     params.setNeedClientAuth(true); 
     params.setSSLParameters(sslParams); 
    } 
} 

class HelloHandler implements HttpHandler { 
    public void handle(HttpExchange t) throws IOException { 
     HttpsExchange ts = (HttpsExchange) t; 
     SSLSession sess = ts.getSSLSession(); 
     //if(sess.getPeerPrincipal() != null) System.out.println(sess.getPeerPrincipal().toString()); // Principal never populated. 
     System.out.printf("Responding to host: %s\n",sess.getPeerHost()); 

     t.getResponseHeaders().set("Content-Type", "text/plain"); 
     t.sendResponseHeaders(200,0); 
     String response = "Hello! You seem trustworthy!\n"; 
     OutputStream os = t.getResponseBody(); 
     os.write(response.getBytes()); 
     os.close(); 
    } 
} 

Und hier ist ein Client sowohl Scheitern und Erfolg zu demonstrieren:

package authserv; 

import java.io.BufferedReader; 
import java.io.FileInputStream; 
import java.io.InputStreamReader; 
import java.net.SocketException; 
import java.net.URL; 
import java.security.KeyStore; 

import javax.net.ssl.HttpsURLConnection; 
import javax.net.ssl.KeyManager; 
import javax.net.ssl.KeyManagerFactory; 
import javax.net.ssl.SSLContext; 
import javax.net.ssl.SSLHandshakeException; 
import javax.net.ssl.SSLSocketFactory; 
import javax.net.ssl.TrustManagerFactory; 

public class AuthClient{ 
    static String NO_KEYSTORE = ""; 
    static String UNAUTH_KEYSTORE = "keys/unauthclient.jks"; // Doesn't exist in server trust store, should fail authentication. 
    static String AUTH_KEYSTORE = "keys/authclient.jks"; // Exists in server trust store, should pass authentication. 
    static String TRUSTSTORE = "keys/clienttrust.jks"; 
    static String CLIENT_PWD = "aaaaaa"; 

    public static void main(String[] args) throws Exception { 

     AuthClient cl = new AuthClient(); 
     System.out.println("No keystore:"); 
     cl.testIt(NO_KEYSTORE); 
     System.out.println("Unauth keystore:"); 
     cl.testIt(UNAUTH_KEYSTORE); 
     System.out.println("Auth keystore:"); 
     cl.testIt(AUTH_KEYSTORE); 
    } 

    public void testIt(String jksFile){ 
     try { 
      String https_url = "https://localhost:8888/auth/"; 
      URL url; 
      url = new URL(https_url); 
      HttpsURLConnection conn = (HttpsURLConnection)url.openConnection(); 
      conn.setSSLSocketFactory(getSSLFactory(jksFile)); 

      conn.setRequestMethod("POST"); 
      conn.setDoOutput(true); 
      conn.setUseCaches(false); 

      // Print response 
      BufferedReader bir = new BufferedReader(new InputStreamReader(conn.getInputStream())); 
      String line = null; 
      while((line = bir.readLine()) != null) { 
        System.out.println(line); 
       } 
      bir.close(); 
      conn.disconnect(); 
     } catch (SSLHandshakeException|SocketException e) { 
      System.out.println(e.getMessage()); 
      System.out.println(""); 
     } catch (Exception e) { 
      e.printStackTrace(); 
     } 
    } 

    private static SSLSocketFactory getSSLFactory(String jksFile) throws Exception { 
     // Create key store 
     KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); 
     KeyManager[] kmfs = null; 
     if(jksFile.length() > 0) { 
      keyStore.load(new FileInputStream(jksFile), CLIENT_PWD.toCharArray()); 
      KeyManagerFactory kmf = KeyManagerFactory.getInstance(
         KeyManagerFactory.getDefaultAlgorithm()); 
      kmf.init(keyStore, CLIENT_PWD.toCharArray()); 
      kmfs = kmf.getKeyManagers(); 
     } 

     // create trust store (validates the self-signed server!) 
     KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType()); 
     trustStore.load(new FileInputStream(TRUSTSTORE), CLIENT_PWD.toCharArray()); 
     TrustManagerFactory trustFactory = TrustManagerFactory.getInstance(
         TrustManagerFactory.getDefaultAlgorithm()); 
     trustFactory.init(trustStore); 

     SSLContext sslContext = SSLContext.getInstance("TLS"); 
     sslContext.init(kmfs, trustFactory.getTrustManagers(), null); 
     return sslContext.getSocketFactory(); 
    } 
} 
+0

Wie haben Sie WantClientAuth implementiert? Ich bekomme immer den Fehler SSL-Schlüssel konnte nicht verifiziert werden ... –

Verwandte Themen