【问题标题】:SSL Socket Connection: CFNetwork SSLHandshake failed (-9824) connecting to Java SSLServerSocketSSL 套接字连接:CFNetwork SSLHandshake 连接到 Java SSLServerSocket 失败 (-9824)
【发布时间】:2013-10-10 05:44:56
【问题描述】:

我正在使用 Java 中的 SSLServerSocket 类和目标 c 中的 Stream 类(CF 和 NS)设置安全套接字连接。 我使用以下代码进行了设置:

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();
            }
        }

    }

Objective 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 objective-c sockets ssl certificate


    【解决方案1】:

    假设您的应用程序可以读取密钥库文件并且密码正确,我的猜测是您在获取此套接字工厂之前已经在 J​​ava 应用程序中的某个地方使用了 SSL/TLS 连接(这可能会完成由另一个库隐含)。

    javax.net.ssl.keyStore* 系统属性仅用于初始化默认 SSLContext 一次。在初始化该上下文后更改它们将无济于事。

    javax.net.ssl.SSLHandshakeException: no cipher suites in common 错误消息是非常典型的未找到或未正确加载密钥库的典型。这是因为在没有任何证书/密钥材料的情况下,没有启用任何 RSA/DSA 密码套件:这些是远程客户端将要寻找的。​​p>

    检查这一点的一种快速方法是在命令行上设置这些属性,或者在应用程序的最开始设置它们(而不是在服务器代码的其他地方更改它们)。

    如果您不希望这些设置在您的应用程序中是全局的,您确实需要初始化一个KeyManager


    在您编辑后,java.security.KeyManagementException: Default SSLContext is initialized automatically 发生是因为您尝试重新配置默认的 SSLContext。改用新实例:

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

    【讨论】:

    • 但是我没有收到任何错误消息说它无法打开密钥库。我还刚刚添加了一个带有更新代码的编辑
    • "但我没有收到任何错误消息说它无法打开密钥库":你不会。如果在您设置属性之前初始化默认的SSLContext 而没有任何密钥库(默认值),那就太晚了。 (您的新编辑是一个不同的问题。)
    • 非常感谢,编辑已修复! (根据你所说的,它必须完成很长的路要走,我无法从代码中设置系统属性!)感谢 .getInstance() 方法! TLS 是最好用的吗?
    • 使用 TLS 或 TLS1.1/1.2 不会对服务器端的情况产生影响,前提是您使用的是从 OpenJDK 派生的东西。在这种情况下,它们的区别仅在于客户端默认启用的协议。无论如何,服务器端默认启用所有支持的协议。
    猜你喜欢
    • 1970-01-01
    • 2013-05-14
    • 2016-10-12
    • 1970-01-01
    • 2016-08-03
    • 2016-01-31
    • 1970-01-01
    • 1970-01-01
    • 2013-09-18
    相关资源
    最近更新 更多