2013-10-03 100 views
1

我使用Java中的SSLServerSocket類建立安全套接字連接,然後使用目標c中的Stream類(CF和NS)。 我有它設置,使用下面的代碼:SSL Socket連接:CFNetwork SSLHandshake失敗(-9824)連接到Java SSLServerSocket

的Java SSLServerSocket代碼(SecureClientWorker涉及每個連接):

public void listenSocket(int port){ 
     try { 
      System.setProperty("javax.net.ssl.keyStore", "path/to/certificate(signed_by_own_root_certificate).jks"); 
//I try to make this root certificate trusted by iOS (see obj-c code below) 
      System.setProperty("javax.net.ssl.keyStorePassword", "PASSWORD..."); 
     } catch (IOException e1) { 
      e1.printStackTrace(); 
     } 
     try{ 
      SSLServerSocketFactory ssf = (SSLServerSocketFactory) SSLServerSocketFactory.getDefault(); 
      server = (SSLServerSocket) ssf.createServerSocket(port); 

      logger.info("Started to listen on port: "+port); 
     }catch(Exception e){ 
      logger.warning("Could not listen on port: "+port); 
      e.printStackTrace(); 
     } 

     while(running){ 
      SecureClientWorker w; 
      try{ 
       w = new SecureClientWorker(server.accept(), this); 
       Thread t = new Thread(w); 
       t.start(); 
      }catch(IOException e){ 
       if(running) 
        e.printStackTrace(); 
      } 
     } 

    } 

目標C客戶端代碼:

[self addRootCert]; //This is where I attempt to make the root certificate trusted, If needed I can post it 
CFReadStreamRef readStream; 
CFWriteStreamRef writeStream; 
CFStreamCreatePairWithSocketToHost(NULL, (__bridge CFStringRef)self.ip.text, self.portNumber.text.intValue, &readStream, &writeStream); 

// Set options then bridge to ARC: 
NSDictionary *settings = [[NSDictionary alloc] initWithObjectsAndKeys: 
          (id)kCFStreamSocketSecurityLevelNegotiatedSSL, kCFStreamPropertySocketSecurityLevel, 
          [NSNumber numberWithBool:YES], kCFStreamSSLAllowsExpiredCertificates, 
          [NSNumber numberWithBool:YES], kCFStreamSSLAllowsAnyRoot, 
          [NSNumber numberWithBool:YES], kCFStreamSSLAllowsExpiredRoots, 
          [NSNumber numberWithBool:NO], kCFStreamSSLValidatesCertificateChain, 
          nil]; 

if (readStream) { 
    CFReadStreamSetProperty(readStream, kCFStreamPropertySSLSettings, (__bridge CFDictionaryRef)settings); 
    inStream = (__bridge_transfer NSInputStream*) readStream; 
    [inStream setDelegate:self]; 
    [inStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; 
    [inStream open]; 
} 

if (writeStream) { 
    CFWriteStreamSetProperty(writeStream, kCFStreamPropertySSLSettings, (__bridge CFDictionaryRef)settings); 
    outStream = (__bridge_transfer NSOutputStream*) writeStream; 
    [outStream setDelegate:self]; 
    [outStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; 
    [outStream open]; 
} 

然而,這導致錯誤代碼CFNetwork SSLHandshake failed (-9824)和inStream對象在NSStreamDelegate方法中返回錯誤NSStreamEventErrorOccurred

在Java方面,一個IOException似乎出現以下拋出:

in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream())); //ClientSocket is just the connection to the client 
[...] 
in.readLine(); 

的的printStackTrace說:javax.net.ssl.SSLHandshakeException: no cipher suites in common

我一定要創建一個KeyManager(因爲我讀別的地方)?我該如何做到這一點(從無到有開放SSLServerSocket請)?

任何幫助將不勝感激!

編輯

我已經改變了服務器端的代碼,因爲它可能無法設置系統屬性。

我的新代碼是:

KeyStore ks = KeyStore.getInstance("JKS"); 
      FileInputStream ksIs = new FileInputStream(new File("path/to/certificate(signed_by_own_root_certificate).jks")); 
      try { 
       ks.load(ksIs, "PASSWORD...".toCharArray()); 
      } catch (NoSuchAlgorithmException e) { 
       // TODO Auto-generated catch block 
       e.printStackTrace(); 
      } catch (CertificateException e) { 
       // TODO Auto-generated catch block 
       e.printStackTrace(); 
      } finally { 
       if (ksIs != null) { 
        ksIs.close(); 
       } 
      } 

      KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); 
      kmf.init(ks, "PASWD".toCharArray()); 
      KeyManager keyManagers[] = kmf.getKeyManagers(); 
      SSLContext sc = SSLContext.getDefault(); 
      sc.init(keyManagers, null, new SecureRandom()); 
      SSLServerSocketFactory socketFactory = sc.getServerSocketFactory(); 

      server = (SSLServerSocket) socketFactory.createServerSocket(port); 

不過我現在收到此錯誤信息: java.security.KeyManagementException: Default SSLContext is initialized automatically

一些研究,我試圖讓我自己TrustManager後,但不能得到它仍然可以工作

我錯過了什麼?

回答

4

假設您的應用程序可以讀取密鑰庫文件,並且密碼是正確的,我猜測您在獲取此套接字工廠之前已在您的Java應用程序中使用了SSL/TLS連接(可能是由另一個圖書館默認完成)。

javax.net.ssl.keyStore*系統屬性僅用於初始化缺省SSLContext一次。在上下文初始化後更改它們將不會執行任何操作。

javax.net.ssl.SSLHandshakeException: no cipher suites in common錯誤消息是一個非常典型的密鑰庫未被發現或正確加載。這是因爲沒有任何證書/密鑰材料,RSA/DSA密碼套件都未啓用:這些是遠程客戶端需要查找的密碼套件。

一種快速的方法來檢查,這將是在命令行上設置這些屬性,或者在您的應用程序的一開始就設定他們(而不是在其他地方改變它們在你的服務器代碼)。

如果您需要這些設置不是全局的,那麼您需要初始化一個KeyManager


按照你的編輯,java.security.KeyManagementException: Default SSLContext is initialized automatically是因爲你試圖重新配置默認SSLContext。改用一個新的實例:

SSLContext sc = SSLContext.getInstance("TLS"); 
+0

但我沒有得到任何錯誤消息說,它無法打開密鑰庫..我也只是增加了一個編輯與更新的代碼 –

+0

「*但我不得到任何錯誤消息,說它無法打開密鑰庫*「:你不會。如果在設置屬性之前默認的'SSLContext'沒有任何密鑰庫(默認值)被初始化,那就太晚了。 (您的新編輯是一個不同的問題。) – Bruno

+0

非常感謝,編輯修正了它! (從你所說的必須做很長的路,我不能從代碼設置系統屬性!)並感謝.getInstance()方法! 'TLS'是最好用的嗎? –