SSL Socket连接:CFNetwork SSLHandshake失败(-9824)连接到Java SSLServerSocket

问题描述:

我使用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后,但不能得到它仍然可以工作

我错过了什么?

假设您的应用程序可以读取密钥库文件,并且密码是正确的,我猜测您在获取此套接字工厂之前已在您的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'是最好用的吗? –