Java-SocketException:客户端发送请求时重置连接
在关于安全套接字的第10章中,有一个构建安全服务器的示例。代码可以找到 我正在尝试使代码更简单。这是我的密码:Java-SocketException:客户端发送请求时重置连接,java,ssl,jsse,Java,Ssl,Jsse,在关于安全套接字的第10章中,有一个构建安全服务器的示例。代码可以找到 我正在尝试使代码更简单。这是我的密码: try { SSLContext context = SSLContext.getInstance(algorithm); KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509"); KeyStore ks = KeyStore.getInstance("JKS")
try {
SSLContext context = SSLContext.getInstance(algorithm);
KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
KeyStore ks = KeyStore.getInstance("JKS");
//char[] password = System.console().readPassword("Password: "); // open the .class with command line
char[] password = {'t', 'h', 'i', 's', 'i', 's', 't', 'i', 'a', 'n'};
ks.load(new FileInputStream("src/jnp4e.keys"), password);
kmf.init(ks, password);
context.init(kmf.getKeyManagers(), null, null); // null = accept the default
// wipe the password
Arrays.fill(password, '0');
SSLServerSocketFactory factory
= context.getServerSocketFactory();
SSLServerSocket server
= (SSLServerSocket) factory.createServerSocket(PORT);
// add anonymous (non-authenticated) cipher suites
String[] supported = server.getSupportedCipherSuites();
String[] anonCipherSuitesSupported = new String[supported.length];
int numAnonCipherSuitesSupported = 0;
for (int i = 0; i < supported.length; i++) {
if (supported[i].indexOf("_anon_") > 0) {
anonCipherSuitesSupported[numAnonCipherSuitesSupported++]
= supported[i];
}
}
String[] oldEnabled = server.getEnabledCipherSuites();
String[] newEnabled = new String[oldEnabled.length
+ numAnonCipherSuitesSupported];
System.arraycopy(oldEnabled, 0, newEnabled, 0, oldEnabled.length);
System.arraycopy(anonCipherSuitesSupported, 0, newEnabled,
oldEnabled.length, numAnonCipherSuitesSupported);
server.setEnabledCipherSuites(newEnabled);
System.out.println("OK..");
// Now all the set up is complete and we can focus
// on the actual communication.
while (true) {
// This socket will be secure,
// but there's no indication of that in the code!
try (Socket theConnection = server.accept()) {
InputStream in = theConnection.getInputStream();
Reader r = new InputStreamReader(in, "UTF-8");
int c;
while ((c = r.read()) != -1) {
System.out.write(c);
}
} catch (IOException ex) {
ex.printStackTrace();
}
}
} catch (IOException | KeyManagementException | KeyStoreException | NoSuchAlgorithmException | CertificateException | UnrecoverableKeyException ex) {
ex.printStackTrace();
}
我先运行服务器,然后运行Cient。但当服务器接受来自客户端的输入时,会引发以下异常:
java.net.SocketException: Connection reset
at java.net.SocketInputStream.read(SocketInputStream.java:189)
at java.net.SocketInputStream.read(SocketInputStream.java:121)
...
更新客户端
根据dave的回答,我添加了两行代码flush()
和close()
但另一个例外是:
javax.net.ssl.SSLHandshakeException: Received fatal alert: certificate_unknown
套接字流上的OutputStreamWriter显然是缓冲区,而您的客户端没有
.flush()
或.close()
,因此您的数据实际上没有发送
如果您的Java程序(或者更确切地说是JVM)没有在套接字流上执行.close()
(包括关闭传递到流的写入程序)而退出,则处理取决于平台;在Windows上,它会发送一个RST,这会导致您在对等机上看到的“连接重置”异常。Unix通常在TCP级别关闭连接,这对于SSL/TLS来说实际上并不完全正常,但“足够接近”(事实上)Java将其视为EOF
编辑以下问题的:
服务器获取SSLHandshakeException“已接收警报错误\u证书证书证书\u未知”这在理论上可能意味着一些事情,但几乎总是意味着服务器正在使用的证书(来自您加载的密钥库,以及匹配的私钥)没有由CA(证书颁发机构)签名受客户信任
为客户机显示的代码不会设置或更改其信任库;如果其他地方没有这样做的代码,或者像java
commandline选项-Dx=y
这样的外部设置来设置系统属性,客户端将使用JSSE默认信任库,其中是文件JRE/lib/security/jssecacerts
,如果存在,则是文件JRE/lib/security/cacerts
(其中JRE是安装JRE的目录;如果使用JDK,则JRE是JDK目录的子目录)。如果您(以及您系统上的任何其他人)在安装JRE后没有修改这些文件,jssecacerts
不存在,cacerts
包含一组由Oracle确定的“已知根”CA,如Verisign和Equifax等
因此,您需要:
- 使用由著名CA颁发的证书;如果您尚未拥有此类证书,您必须通过证明(至少)您对已认证域名的控制权,并根据CA可能支付的费用,从CA处获得证书;如果您确实拥有或获得了这样的证书,请将其安装在您的密钥库中的privatekey条目中,并带有任何链证书(对于著名的CA,几乎总是至少有一个链证书)
- 使用由任何其他CA颁发的证书,包括您组成的临时CA,以及包括作为限制情况的自签名的证书,该证书隐式地是其自己的CA,例如自动生成的
;并将该CA(或自签名证书)的CA证书放入客户端使用的信任库中。为此,有两种方法:密钥工具-genkeypair
- 将服务器的CA证书(或自签名证书)放入客户端使用的JRE的默认信任库文件中。这确实会影响共享该默认信任库的任何其他程序,该信任库可能是使用该JRE的所有其他程序。如果您使用
它只会影响JSSE,如果您使用jssecacerts
它也会影响签名代码(如果有)的验证,另外,如果您升级了JRE(通常在Windows上是自动的),它会被清除cacerts
- 创建(或重用)另一个信任库,将服务器的CA证书放入其中,并让客户端使用该非默认信任库。有几个选项可供选择:从外部设置默认信任库的系统属性,在程序中显式设置它们(在首次使用JSSE之前!),显式加载“密钥库”文件(实际上包含证书),并在非默认SSLSocketFactory中使用其trustmanager,就像服务器代码对keymanager所做的那样,或者甚至用你喜欢的商店编写你自己的trustmanager,并以类似的方式使用
- 将服务器的CA证书(或自签名证书)放入客户端使用的JRE的默认信任库文件中。这确实会影响共享该默认信任库的任何其他程序,该信任库可能是使用该JRE的所有其他程序。如果您使用
keytool-genkeypair-keystore-ksfile-keyalg-RSA
对于提示“first and last name”(实际上是cert中的CommonName属性),输入服务器的名称,特别是客户端将用于连接到服务器的名称;在问题中,这是“localhost”。其他名称字段并不重要;根据您的喜好填写或省略它们,但如提示所示,该国家/地区(如果使用)必须为2个字母。您可以在命令行-dname“CN=common\u name\u value”
上添加,而不是回答提示。如果您有多个服务器名称,这里会省略一些选项。
对于某些其他应用程序,您可能需要使用-别名指定条目名称;对于这个问题,它是不需要的
获取证书的副本:
keytool-exportcert-rfc-keystore ksfile[-别名]-文件certfile
在本例中,客户机与服务器位于同一台机器上。在另一种情况下,需要将此文件的内容从服务器复制到客户端;这通常通过复制文件来实现
将证书放入客户端信任库。如上所述,有很多选择,但你可能会看到的一个建议最经常,因为它通常是快速
...
out.write("Hello");
out.flush();
socket.close();
...
javax.net.ssl.SSLHandshakeException: Received fatal alert: certificate_unknown