2012-02-09 57 views
-2

我需要在一個套接字中使用ssl連接進行密鑰對,而不會在客戶端中進行任何更改。在一個SSL套接字連接中使用兩個私鑰(keystore)和兩個公鑰(truststore)

爲什麼?

因爲一個客戶端在信任存儲中使用CN屬性進行連接握手,而其他客戶端使用同一屬性中的另一個值以相同的方式處理相同的任務。

所以我需要使用兩個關鍵的商店(私人)具有獨特的屬性,CN和也別名和份額兩種不同的信任存儲(公鑰)具有獨特的CN屬性也是別名也。

描述波紋管:

keyStore1

密鑰庫類型:JKS

密鑰庫提供:SUN

別名:對identity1

擁有者:CN = APP1 ...

發行人:CN = APP1 ...

trustStore1

別名:對identity1

業主:CN = APP1 ...

發行機構:CN = app1 ...

keyStore2

別名:identity2

業主:CN = APP 2 ...

發行人:CN = APP 2 ...

trustStore2

別名:identity2

業主:CN = APP 2 ...

發行人:CN = APP 2 ...

我試圖實現這種方式這個功能:

KeyStore KeyStore1; 

       try { 
        String keyStoreFile1 = "privatekey1"; 
        String keyStoreType1 = "jks"; 
        char[] keyStorePwd1 = "password".toCharArray(); 

        keyStore1 = KeyStore.getInstance(keyStoreType1); 
        keyStore1.load(new FileInputStream(keyStoreFile1), keyStorePwd1); 
       } catch (java.security.GeneralSecurityException thr) { 
        throw new IOException("Cannot load keystore (" + thr + ")"); 
       } 

       KeyStore trustStore1; 

       try { 
        String trustStoreFile1 = "publickey1"; 
        String trustStoreType1 = "jks"; 
        char[] trustStorePwd1 = "password".toCharArray(); 

        trustStore1 = KeyStore.getInstance(trustStoreType1); 
        trustStore.load(new FileInputStream(trustStoreFile1), trustStorePwd1); 
       } catch (java.security.GeneralSecurityException thr) { 
        throw new IOException("Cannot load truststore (" + thr + ")"); 
       } 


       KeyStore keyStore2; 

       try { 
        String keyStoreFile2 = "privatekey2"; 
        String keyStoreType2 = "jks"; 
        char[] keyStorePwd2 = "anotherpass".toCharArray(); 

        keyStore2 = KeyStore.getInstance(key2StoreType); 
        keyStore2.load(new FileInputStream(keyStoreFile2), keyStorePwd2); 
       } catch (java.security.GeneralSecurityException thr) { 
        throw new IOException("Cannot load keystore (" + thr + ")"); 
       } 

       KeyStore trustStore2; 

       try { 
        String trustStoreFile2 = "publickey2"; 
        String trustStoreType2 = "jks"; 
        char[] trustStorePwd2 = "anotherpass".toCharArray(); 

        trustStore2 = KeyStore.getInstance(trustStoreType2); 
        trustStore2.load(new FileInputStream(trustStoreFile2), trustStorePwd2); 
       } catch (java.security.GeneralSecurityException thr) { 
        throw new IOException("Cannot load truststore (" + thr + ")"); 
       } 



       KeyManagerFactory kmfkey1 = KeyManagerFactory 
.getInstance(KeyManagerFactory.getkey1Algorithm()); 

       kmfkey1.init(keyStore1, "password".toCharArray()); 

       TrustManagerFactory tmfkey1 = 
         TrustManagerFactory.getInstance(TrustManagerFactory.getkey1Algorithm()); 
       tmfkey1.init(trustStore1); 


       SSLContext ctx = SSLContext.getInstance("SSL"); 
       ctx.init(kmfkey1.getKeyManagers(), tmfkey1.getTrustManagers(), null); 


       KeyManagerFactory kmfkey2 = KeyManagerFactory. 
getInstance(KeyManagerFactory.getkey1Algorithm()); 

       kmfkey2.init(keyStore2, "password".toCharArray()); 

       TrustManagerFactory tmfkey2 = TrustManagerFactory 
.getInstance(TrustManagerFactory.getkey1Algorithm()); 

       tmfkey2.init(trustStore2); 


       SSLContext ctxkey2 = SSLContext.getInstance("SSL"); 
       ctxkey2.init(kmfkey2.getKeyManagers(), tmfkey2.getTrustManagers(), null); 

       SSLServerSocketFactory sslSrvSockFact = (SSLServerSocketFactory) SSLServerSocketFactory.getDefault(); 

       serverSocket = sslSrvSockFact.createServerSocket(port); 

... 但我收到此錯誤信息

...

java.security.KeyStoreException:在com.sun.net未初始化密鑰庫中 在java.security.KeyStore.aliases(KeyStore.java:941) .ssl.internal.ssl.SunX509KeyManagerImpl。(SunX509KeyManagerImpl.java:106) at com.sun.net.ssl.internal.ssl.KeyManagerFactoryImpl $ SunX509.engineInit(KeyManagerFactoryImpl.java:41) at javax.net.ssl。 KeyManagerFactory.init(KeyManagerFactory.java:192)

...

所以我真的很想知道是否有可能在一個套接字連接中使用密鑰對,或以不能看到或處理的不同方式解決此問題。

編輯1

布魯諾,你能不能給我一個完整的或完整的例子嗎?

因爲目前尚不清楚我....

我想兩件事情:

One解決方案就是把兩個鍵下面先前建議的私人密鑰庫裏面...但不工作我接收波紋管的消息:在螺紋

異常 「主」 java.lang.NoSuchMethodError:javax.net.ssl.SSLContext.setDefault(Ljavax /淨/ SSL /的SSLContext) 在... initialiseManager( 499)

你是對的...我還需要選擇其中一個方面或

javax.net.ssl.SSLException: 無證書或密鑰對應於啓用的SSL密碼套件。 而試圖將消息發送到服務器...

其次,我跟着你的建議......但是SSL Socket連接不啓動

我實現了這個與許多其他人是邊打邊 幫助代碼在這裏本網站...感謝所有

private KeyManager[] getKeyManagers() throws IOException, GeneralSecurityException { 

     // First, get the default KeyManagerFactory. 
     String alg = KeyManagerFactory.getDefaultAlgorithm(); 
     KeyManagerFactory kmFact = KeyManagerFactory.getInstance(alg); 

     // Next, set up the KeyStore to use. We need to load the file into 
     // a KeyStore instance. 

     File keyFile = new File("privatekey1"); 

     FileInputStream fis = new FileInputStream(keyFile); 
     LogManager.log("Loaded keystore privatekey1 " + keyFile.getAbsolutePath(), 
LogManager.LOG_LOWEST_LEVEL); 
     KeyStore ks = KeyStore.getInstance("jks"); 
     String keyStorePassword = "password"; 
     ks.load(fis, keyStorePassword.toCharArray()); 
     fis.close(); 
     // Now we initialise the KeyManagerFactory with this KeyStore 
     kmFact.init(ks, keyStorePassword.toCharArray()); 


     KeyManagerFactory dkmFact = KeyManagerFactory.getInstance(alg); 

     File keyFileTwo = new File("privatekey2"); 

     FileInputStream fisTwo = new FileInputStream(keyFileTwo); 
     LogManager.log("Loaded keystore privatekey2 " + keyFileTwo.getAbsolutePath(), LogManager.LOG_LOWEST_LEVEL); 
     KeyStore ksTwo = KeyStore.getInstance("jks"); 
     String keyStorePasswordTwo = "password"; 
     ksTwo.load(fisTwo, keyStorePasswordTwo.toCharArray()); 
     fisTwo.close(); 
     // Now we initialise the KeyManagerFactory with this KeyStore 
     dkmFact.init(ksTwo, keyStorePasswordTwo.toCharArray()); 


     // default 
     //KeyManagerFactory dkmFact = KeyManagerFactory.getInstance(alg); 
     //dkmFact.init(null, null); 


     // Get the first X509KeyManager in the list 
     X509KeyManager customX509KeyManager = getX509KeyManager(alg, kmFact); 
     X509KeyManager jvmX509KeyManager = getX509KeyManager(alg, dkmFact); 

     KeyManager[] km = {new MultiKeyStoreManager(jvmX509KeyManager, customX509KeyManager)}; 
     LogManager.log("Number of key managers registered:" + km.length, LogManager.LOG_LOWEST_LEVEL); 
     return km; 
    } 

    /** 
    * Find a X509 Key Manager compatible with a particular algorithm 
    * @param algorithm 
    * @param kmFact 
    * @return 
    * @throws NoSuchAlgorithmException 
    */ 
    private X509KeyManager getX509KeyManager(String algorithm, KeyManagerFactory kmFact) 
      throws NoSuchAlgorithmException { 
     KeyManager[] keyManagers = kmFact.getKeyManagers(); 

     if (keyManagers == null || keyManagers.length == 0) { 
      throw new NoSuchAlgorithmException("The default algorithm :" + algorithm + " produced no key managers"); 
     } 

     X509KeyManager x509KeyManager = null; 

     for (int i = 0; i < keyManagers.length; i++) { 
      if (keyManagers[i] instanceof X509KeyManager) { 
       x509KeyManager = (X509KeyManager) keyManagers[i]; 
       break; 
      } 
     } 

     if (x509KeyManager == null) { 
      throw new NoSuchAlgorithmException("The default algorithm :" + algorithm + " did not produce a X509 Key manager"); 
     } 
     return x509KeyManager; 
    } 

    private void initialiseManager(int iPort) throws IOException, GeneralSecurityException { 
     // Next construct and initialise a SSLContext with the KeyStore and 
     // the TrustStore. We use the default SecureRandom. 

     // load your key store as a stream and initialize a KeyStore 
     File trustFile = new File("publicKey"); 
     LogManager.log("Trust File Loaded " + trustFile.getAbsolutePath(), LogManager.LOG_LOWEST_LEVEL); 

     InputStream trustStream = new FileInputStream(trustFile); 
     KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType()); 

     // if your store is password protected then declare it (it can be null however) 
     char[] trustPassword = "password".toCharArray(); 

     // load the stream to your store 
     trustStore.load(trustStream, trustPassword); 

     File trustFileTwo = new File("publicKeyTwo"); 
     LogManager.log("Trust File Loaded " + trustFileTwo.getAbsolutePath(), LogManager.LOG_LOWEST_LEVEL); 

     InputStream trustStreamTwo = new FileInputStream(trustFileTwo); 
     KeyStore trustStoreTwo = KeyStore.getInstance(KeyStore.getDefaultType()); 

     // if your store is password protected then declare it (it can be null however) 
     char[] trustPasswordTwo = "password".toCharArray(); 

     // load the stream to your store 
     trustStoreTwo.load(trustStreamTwo, trustPasswordTwo); 


     // initialize a trust manager factory with the trusted store 
     TrustManagerFactory trustFactory = 
       TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); 
     trustFactory.init(trustStore); 
     trustFactory.init(trustStoreTwo); 

     // get the trust managers from the factory 
     TrustManager[] managers = trustFactory.getTrustManagers(); 


     SSLContext context = SSLContext.getInstance("SSL"); 
     context.init(getKeyManagers(), managers, new SecureRandom()); 
     SSLContext.setDefault(context); 

     SSLServerSocketFactory sslSrvFact = (SSLServerSocketFactory) SSLServerSocketFactory.getDefault(); 

     serverSocket = sslSrvFact.createServerSocket(iPort); 
     // this method didn't create a Socket Connection correctly 

    } 

    class MultiKeyStoreManager implements X509KeyManager { 

     private final X509KeyManager jvmKeyManager; 
     private final X509KeyManager customKeyManager; 

     public MultiKeyStoreManager(X509KeyManager jvmKeyManager, X509KeyManager customKeyManager) { 
      this.jvmKeyManager = jvmKeyManager; 
      this.customKeyManager = customKeyManager; 
     } 

     @Override 
     public String chooseClientAlias(String[] keyType, Principal[] issuers, Socket socket) { 
      // try the first key manager 
      String alias = customKeyManager.chooseClientAlias(keyType, issuers, socket); 
      if (alias == null) { 
       alias = jvmKeyManager.chooseClientAlias(keyType, issuers, socket); 
       LogManager.log("Reverting to JVM CLIENT alias : " + alias, LogManager.LOG_LOWEST_LEVEL); 
      } 

      return alias; 

     } 

     @Override 
     public String chooseServerAlias(String keyType, Principal[] issuers, Socket socket) { 
      // try the first key manager 
      String alias = customKeyManager.chooseServerAlias(keyType, issuers, socket); 
      if (alias == null) { 
       alias = jvmKeyManager.chooseServerAlias(keyType, issuers, socket); 
       LogManager.log("Reverting to JVM Server alias : " + alias ,LogManager.LOG_LOWEST_LEVEL); 
      } 
      return alias; 
     } 

     @Override 
     public String[] getClientAliases(String keyType, Principal[] issuers) { 
      String[] cAliases = customKeyManager.getClientAliases(keyType, issuers); 
      String[] jAliases = jvmKeyManager.getClientAliases(keyType, issuers); 
      LogManager.log("Supported Client Aliases Custom: " + cAliases.length + " JVM : " + jAliases.length, 
        LogManager.LOG_LOWEST_LEVEL); 
      return (String[]) ArrayUtils.addAll(cAliases, jAliases); 
     } 

     @Override 
     public PrivateKey getPrivateKey(String alias) { 
      PrivateKey key = customKeyManager.getPrivateKey(alias); 
      if (key == null) { 
       System.out.println("Reverting to JVM Key : " + alias); 
       return jvmKeyManager.getPrivateKey(alias); 
      } else { 
       return key; 
      } 
     } 

     @Override 
     public String[] getServerAliases(String keyType, Principal[] issuers) { 
      String[] cAliases = customKeyManager.getServerAliases(keyType, issuers); 
      String[] jAliases = jvmKeyManager.getServerAliases(keyType, issuers); 
      LogManager.log("Supported Server Aliases Custom: " + cAliases.length + " JVM : " + jAliases.length, 
        LogManager.LOG_LOWEST_LEVEL); 
      return (String[]) ArrayUtils.addAll(cAliases, jAliases); 
     } 

     @Override 
     public java.security.cert.X509Certificate[] getCertificateChain(String string) { 
      java.security.cert.X509Certificate[] chain = customKeyManager.getCertificateChain("alias_key1"); 
      if (chain == null || chain.length == 0) { 
       LogManager.log("Reverting to JVM Chain : " + string, LogManager.LOG_LOWEST_LEVEL); 
       return jvmKeyManager.getCertificateChain("alias_key2"); 
      } else { 
       return chain; 
      } 
     } 
    } 

and this gave me this status 

* 2012.02.09 18時47分00秒激活SSL連接

2012.02.09 18時47分00秒[... ::運行]

2012.02.09十八時47分00秒信託文件中加載公鑰

2012.02.09十八時47分00秒信託文件加載publicKeyTwo

2012.02.09十八時47分00秒加載密鑰庫專用密鑰專用密鑰

2012.02.09 18:47:00加載密鑰庫privateKey2 privateKeyTwo

2012.02。09十八點47分00秒的關鍵管理人員的註冊數:1 *

卻沒有任何反應,當我試圖發送郵件服務器...

正在很難找到真正工作在這樣的一個例子案件。

EDIT 2

嗨布魯諾

關於這個問題,Java和SSH,我真的你先進的知識非常感謝您 幫助。所有這些信息都可以幫助我更好地理解這個問題,同時爲我顯示的方式... Thx很多

而且你是對的...我在Java 5上使用Java 6的代碼JRE

但再次下你的食譜,我得到這個代碼:

  // Load the key store: change store type if needed 
     KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType()); 
     FileInputStream fis = new FileInputStream("keyStore1"); 
     char[] keyPass = "passw".toCharArray(); 
     try { 
      ks.load(fis, keyPass); 
     } finally { 
      if (fis != null) { 
       fis.close(); 
      } 
     } 

     // Get the default Key Manager 
     KeyManagerFactory kmf = KeyManagerFactory.getInstance(
       KeyManagerFactory.getDefaultAlgorithm()); 
     kmf.init(ks, keyPass); 

     final X509KeyManager origKm = (X509KeyManager) kmf.getKeyManagers()[0]; 
     X509KeyManager km = new X509KeyManager() { 

      public String chooseServerAlias(String[] keyType, Principal[] issuers, Socket socket) { 
       String alias; 

       InetAddress remoteAddress = socket.getInetAddress(); 

       if (remoteAddress.getHostAddress().equalsIgnoreCase("11.111.111.11")) { 
        alias = "alias1"; 
        LogManager.log("Reverting to JVM CLIENT alias : " + alias, LogManager.LOG_LOWEST_LEVEL); 
       } else { 
        alias = "alias2"; 
        LogManager.log("Reverting to JVM CLIENT alias : " + alias, LogManager.LOG_LOWEST_LEVEL); 
       } 
       return alias; 
      } 

      public String chooseClientAlias(String[] keyType, Principal[] issuers, Socket socket) { 
       // try this key manager 
       String alias = origKm.chooseClientAlias(keyType, issuers, socket); 
       LogManager.log("Reverting to JVM CLIENT alias : " + alias, LogManager.LOG_LOWEST_LEVEL); 
       return alias; 
      } 

      public String[] getClientAliases(String keyType, Principal[] issues) { 

       String[] cAliases = origKm.getClientAliases(keyType, issues); 
       LogManager.log("Supported Client Aliases : " + cAliases.length, LogManager.LOG_LOWEST_LEVEL); 
       return cAliases; 

      } 

      public String[] getServerAliases(String keyType, Principal[] issues) { 

       String[] sAliases = origKm.getServerAliases(keyType, issues); 
       LogManager.log("Supported Server Aliases: " + sAliases.length, LogManager.LOG_LOWEST_LEVEL); 
       return sAliases; 

      } 

      public String chooseServerAlias(String keyType, Principal[] issues, Socket socket) { 

       // try this key manager 
       String alias = origKm.chooseServerAlias(keyType, issues, socket); 
       LogManager.log("Reverting to JVM Server alias : " + alias, LogManager.LOG_LOWEST_LEVEL); 
       return alias; 
      } 

      public X509Certificate[] getCertificateChain(String keyType) { 

       // here I could specify my other keystore, keystore2 how I could do this? 

       // I'm thinking in the righ way when I implemented this code to get the correct private key? 

       X509Certificate[] chain = origKm.getCertificateChain("alias1"); 
       if (chain == null || chain.length == 0) { 
        LogManager.log("Reverting to JVM Chain : " + keyType, LogManager.LOG_LOWEST_LEVEL); 

        return origKm.getCertificateChain("alias2"); 
       } else { 
        return chain; 
       } 
      } 

      public PrivateKey getPrivateKey(String alias) { 

       PrivateKey key = origKm.getPrivateKey(alias); 

       // here I could get my other keystore according the alias, for example 
       // keystore2 how I could do this? 

       LogManager.log("Reverting to JVM Key : " + alias, LogManager.LOG_LOWEST_LEVEL); 
       return key; 
      } 
     }; 

     SSLContext sslContext = SSLContext.getInstance("SSL"); 
     sslContext.init(new KeyManager[]{km}, null, null); 

     SSLServerSocketFactory sslSrvFact = sslContext.getServerSocketFactory(); 
     objServerSocket = sslSrvFact.createServerSocket(iPort); 

是正是這一點我需要實現我的目標是什麼?

編輯3

使用這種方法得到了一個客戶端和服務器之間的握手使用第二 密鑰庫與別名2利用公衆的信任,商店2客戶端

...但我還是當我的錯誤試圖用信託商店1在客戶端

...收到一則由[地址=/11.111.111.11] 恢復到JVM服務器別名:別名2 恢復到JVM密鑰:別名2 錯誤retriving: javax.net.ssl.SSLHandshakeException:收到致命警報:certificate_unknown

我的代碼不會改變使用與alias1私人key1的服務器......當客戶端使用帶有證書alias1公衆truststore1 ...

更何況是必要的解決這個做......我認爲這是最終的解決方案...附近再次

THX!

EDIT 4

布魯諾,我按照你的建議改變了getCertificateChain法顯示波紋管

public X509Certificate[] getCertificateChain(String alias) { 
     X509Certificate[] chain = origKm.getCertificateChain(alias); 
     return chain; 
} 

public String chooseServerAlias(String keyType, Principal[] issuers, Socket socket) { 
        String alias; 

還我刪除重複的方法,該方法.. 。

而在網癮使用舊的信任存儲的客戶端不驗證主機名

private static final HostnameVerifier DO_NOT_VERIFY = new HostnameVerifier() { 

    public boolean verify(String hostname, SSLSession session) { 
     return true; 
    } 
}; 

但對於那些需要使用第二信任存儲做到這一點的驗證客戶端,這是一個原因,因爲我需要處理與密鑰對證書...

EDIT5

我想知道我怎麼能實現服務器套接字讓它能夠與證書根據識別和使用 正確的證書正在使用的客戶端,以繼續進行 握手通信服務器。

解釋更好,在服務器側是:

AppServerSideSocket.jar

  • 專用密鑰存儲器:privateKeyApp(JKS類型,與密鑰工具生成)
  • 公共密鑰庫:publicKeyApp(JKS型,與所有客戶共享)

而且在客戶端...

AppClientSideSocket.jar - 公共密鑰庫:publicKeyApp

AppServerSideSocket.jar監聽客戶端的請求,並通過客戶端發送一次接收到的過程 信息

AppClientSideSocket.jar使用SSL使用SSL連接服務器publicKeyAppwithout 驗證服務器主機名握手後發送AppServerSideSocket應用程序的信息。

現在我的另一個客戶端應用程序,AppClientSideSocketNEW.jar,這驗證服務器的主機名,以與服務器通信 。在這種情況下,客戶端 的公共證書中使用的CN必須與AppServerSideSocket.jar所在的主機名匹配。

最初的連接被以這種方式配置服務器端:

if (usingSSLConn) { 
    System.setProperty("javax.net.ssl.keyStore", "privateKeyApp"); 
    System.setProperty("javax.net.ssl.keyStorePassword", "privateKeyAppPassword"); 

    SSLServerSocketFactory sslServerSocketFactory = (SSLServerSocketFactory) SSLServerSocketFactory.getDefault(); 
    ServerSocket serverSocketApp = sslServerSocketFactory.createServerSocket(Port); 

} else 
    serverSocketApp = new ServerSocket(Port); 
} 

所有的客戶收到了同樣的publicKeyApp,並沒有驗證主機服務器連接,所以不要緊 如果服務器在哪裏服務器套接字應用程序(AppServerSideSocket.jar)安裝在服務器上,主機名爲 badServer1.com,並且privateKeyApp和publicKeyApp中的密鑰的CN設置爲goodServer1.com,因爲所有的 客戶端都不驗證主機名或密鑰的CN屬性。

波紋管表現出了一塊這種連接

private static final HostnameVerifier DO_NOT_VERIFY = new HostnameVerifier() { 

     public boolean verify(String hostname, SSLSession session) { 
      return true; 
     } 
}; 

System.setProperty("javax.net.ssl.trustStore", publicKey1); 
     System.getProperties().setProperty("java.protocol.handler.pkgs", "javax.net.ssl.internal.www.protocol"); 
    HttpsURLConnection.setDefaultHostnameVerifier(DO_NOT_VERIFY); 
    ... 
    SOAPConnectionFactory soapConn = SOAPConnectionFactory.newInstance(); 
    SOAPConnection connection = soapConn.createConnection(); 
    ...  
    URL endpoint = new URL(hostname + ":" + port); 

,但新的客戶端(AppClientSideSocketNEW的。jar)強制執行此驗證,現在有必要爲此客戶端提供一個新的證書 ,其CN屬性的新值反映服務器套接字所在的正確主機名CN。

我沒有訪問第二個客戶端,我確信它進行主機名驗證。

所以我創建了兩個新的密鑰對證書(privateKeyAppNew和publicKeyAppNew),顯然通信發生 使用使用這種新的公共publicKeyAppNew鍵這個新的密鑰對和新的客戶端服務器之間成功進行配置後,服務器使用這個新的密鑰對當然。

但我需要繼續爲舊客戶端使用舊的密鑰對。我想知道我該如何處理這個問題。

使用keymanager讓我能夠在客戶端嘗試連接時驗證服務器應用程序上的客戶端證書,並選擇合適的並使用正確的證書執行握手?

或者我需要在不同的端口有不同的ssl套接字連接爲哪種客戶端?

+0

你是說你想要根據客戶端連接提供不同的證書嗎?你也嘗試使用客戶端證書嗎? – Bruno 2012-02-09 19:38:53

+1

[在SSL Socket Factory連接中使用多個密鑰對]的可能重複(http://stackoverflow.com/questions/9179717/using-more-than-one-key-pair-in-ssl-socket-factory - 連接) – EJP 2012-02-10 00:42:00

+0

我認爲你真的需要改善你的問題。從一開始就很混亂,但它並沒有變得更好。請指定您的錯誤消息,並在哪一方獲得他們(客戶端或服務器)。不要嘗試額外的東西,這些東西必然會導致額外的無關問題(例如,「我在Java 5 JRE上使用Java 6的代碼」)。也許使用更有意義的日誌消息(你的「回覆到...」消息並不意味着它們是什麼,因爲你沒有「回覆」任何東西),請記錄'remoteAddress.getHostAddress()'以確保這是你所期望的,以及它需要的分支。 – Bruno 2012-02-13 21:43:30

回答

1

簡單的方法是將所有內容放在一個密鑰存儲區中 - 所有密鑰和所有信任證書。這會消除你的問題。

+0

這並不能真正幫助選擇使用哪個密鑰/證書的問題。 – Bruno 2012-02-09 20:14:26

5

的「顯而易見」的問題是,你實際上並沒有做任何使用SSLContext S的創建:

SSLServerSocketFactory sslSrvSockFact = 
    (SSLServerSocketFactory) SSLServerSocketFactory.getDefault(); 

這至少應該是:

SSLServerSocketFactory sslSrvSockFact = 
    (SSLServerSocketFactory) ctx.getServerSocketFactory(); 

的問題是,你將不得不選擇一個上下文或其他...

你的問題的解決方案是在the answer I gave to your other similar question a couple of days ago:你需要實現自己的X509KeyManager能夠選擇你要使用的密鑰。

無論您是想使用單個密鑰庫還是從兩個密鑰庫加載密鑰/證書都沒有多大關係:如果您確實想要,您當然可以實施getPrivateKeygetCertificateChain,以便它們從加載密鑰/證書兩個不同的密鑰庫取決於別名。儘管如此,這將是不必要的複雜。您仍然必須根據別名選擇進行操作,因此您可以使用不同的別名從單個密鑰存儲區加載密鑰/證書。

從服務器的角度來看,選擇一個別名(以及密鑰/證書對)的唯一方法是使用套接字(或引擎,如果使用的是X509ExtendedKeyManager)中提供的內容。由於Java 7不支持服務器名稱指示(這會讓客戶端在選擇過程之前知道它請求的是哪個主機名),您可能必須根據客戶端IP地址或您的服務器IP地址正在使用(如果你有多個)。

Using two private keys (keystore) and two public keys (truststore)

您似乎對密鑰庫和信任庫的內容感到困惑。除非您計劃使用客戶端證書身份驗證,否則您可以忽略服務器上的信任存儲設置。您可以使用默認(null)作爲您的SSLContext.init(...)的第二個參數。您的「keystore(keystore)」是本地方(本例中爲您的服務器)使用的信息,「truststore(keystore)」用於確定要信任哪個遠程方。

您將要呈現給客戶端的公鑰(或將被精簡的證書)也位於與您的私鑰相關聯的密鑰庫中,而不在信任庫中。

編輯:

Exception in thread "main" java.lang.NoSuchMethodError: javax.net.ssl.SSLContext.setDefault(Ljavax/net/ssl/SSLContext;) at ...initialiseManager(499)

NoSuchMethodError

Thrown if an application tries to call a specified method of a class (either static or instance), and that class no longer has a definition of that method.

Normally, this error is caught by the compiler; this error can only occur at run time if the definition of a class has incompatibly changed.

這有什麼,與你的SSL設置。不確定你在這裏做了什麼,但看起來你可能在Java 5 JRE上使用Java 6的代碼(Java 6在SSLContext上沒有setDefault)。更重要的是,你在這裏使用Java的一般方式有些問題。

javax.net.ssl.SSLException:

No available certificate or key corresponds to the SSL cipher suites which are enabled.

這很可能由事實來解釋:你似乎沒有使用你已經在所有初始化的SSLContext小號...

  • 如果你有兩對私鑰/證書在您的密鑰庫中。

我的回答here仍然存在。我會盡量使它更加明確。我在這裏假設您的一個證書/私鑰使用alias1,另一個alias2。如果您不確定,請使用keyool -list查找。您可以選擇並設置它們。

// Load the key store: change store type if needed 
KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType()); 
FileInputStream fis = new FileInputStream("/path/to/keystore"); 
try { 
    ks.load(fis, keystorePassword); 
} finally { 
    if (fis != null) { fis.close(); } 
} 

// Get the default Key Manager 
KeyManagerFactory kmf = KeyManagerFactory.getInstance(
    KeyManagerFactory.getDefaultAlgorithm()); 
kmf.init(ks, keyPassword); 

final X509KeyManager origKm = (X509KeyManager)kmf.getKeyManagers()[0]; 
X509KeyManager km = new X509KeyManager() { 
    public String chooseServerAlias(String keyType, 
            Principal[] issuers, Socket socket) { 
     InetAddress remoteAddress = socket.getInetAddress(); 
     if (/* remoteAddress has some conditions you need to define yourself */ { 
      return "alias1"; 
     } else { 
      return "alias2"; 
     } 
    } 

    public String chooseClientAlias(String[] keyType, 
            Principal[] issuers, Socket socket) { 
     // Delegate this other methods to origKm. 
     origKm.chooseClientAlias(keyType, issuers, socket); 
    } 

    // Delegate this other methods to origKm, in the same way as 
    // it was done for chooseClientAlias. 
} 

SSLContext sslContext = SSLContext.getInstance("TLS"); 
sslContext.init(new KeyManager[] { km }, null, null); 

SSLSocketFactory sslSocketFactory = sslContext.getSSLSocketFactory(); 
  • 如果你真的想兩個不同的密鑰庫。

做同樣的,在此之上,在getCertificateChain(String alias),選擇取決於別名使用的兩個密鑰庫,並用它來獲取證書鏈。 getPrivateKey(...)同樣的事情。

+0

我可以使用客戶端IP地址來選擇正確的證書布魯諾,例如...不是更容易使兩個SSL /套接字連接,而不是? – Bera 2012-02-10 14:15:47

+0

@Bera,在我的例子中有一個拼寫錯誤,'chooseServerAlias'顯然(或者可能不是)重寫了定義的方法(第一個參數是'String'),沒有定義一個新的重載方法,並且使用String []作爲第一個參數。另外,避免'getCertificateChain'中的複雜邏輯:只包裝原來的密鑰管理器並使用'alias'(最終,這並不重要,但是你使用了錯誤的參數名稱,這可能導致更多的混淆) 。 – Bruno 2012-02-13 19:57:33

+0

除此之外,您應該知道客戶提供的證書中的主機名(主題替換名稱,或者如果CN中沒有SAN),必須與客戶端請求的內容匹配。 – Bruno 2012-02-13 19:58:23