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");
但我没有得到任何错误消息说,它无法打开密钥库..我也只是增加了一个编辑与更新的代码 –
“*但我不得到任何错误消息,说它无法打开密钥库*“:你不会。如果在设置属性之前默认的'SSLContext'没有任何密钥库(默认值)被初始化,那就太晚了。 (您的新编辑是一个不同的问题。) – Bruno
非常感谢,编辑修正了它! (从你所说的必须做很长的路,我不能从代码设置系统属性!)并感谢.getInstance()方法! 'TLS'是最好用的吗? –