gpt4 book ai didi

java - Java-SocketException:客户端发送请求时重置连接

转载 作者:太空宇宙 更新时间:2023-11-03 13:16:58 26 4
gpt4 key购买 nike

在关于安全套接字的第10章Java Network Programming 4th Edition中,有一个构建安全服务器的示例。可以在here中找到该代码。

我正在尝试使代码更简单。这是我的代码:

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


唯一的区别是在我的代码中,我创建了一个 Reader,以便服务器可以读取字符。
我用发送文本的简单客户端尝试了此服务器。这是客户:

int port = 7000; // default https port
String host = "localhost";
SSLSocketFactory factory
= (SSLSocketFactory) SSLSocketFactory.getDefault();


SSLSocket socket = null;
try {
socket = (SSLSocket) factory.createSocket(host, port);

// enable all the suites
String[] supported = socket.getSupportedCipherSuites();
socket.setEnabledCipherSuites(supported);

Writer out = new OutputStreamWriter(socket.getOutputStream(), "UTF-8");
out.write("Hello");

}catch(Exception e){

}


我先运行服务器,然后运行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()

...
out.write("Hello");
out.flush();
socket.close();
...


但是另一个例外出现了:

javax.net.ssl.SSLHandshakeException: Received fatal alert: certificate_unknown

最佳答案

套接字流上的OutputStreamWriter显然正在缓冲,并且您的客户端没有.flush().close(),因此实际上并未发送您的数据。

如果您的Java程序(或更确切地说是JVM)在没有对套接字流执行.close()的情况下退出(包括关闭传递给Stream的Writer),则处理取决于平台;在Windows上,它会发送RST,从而导致您在对等端看到“连接重置”异常。 Unix通常在TCP级别上关闭连接,这实际上对于SSL / TLS而言并不完全正常,但是“足够接近”(实际上)使得Java将其视为EOF。

编辑后续问题:

服务器收到SSLHandshakeException“接收到警报bad_certificatecertificate_unknown”,从理论上讲可能有几件事,但几乎总是意味着该服务器正在使用的证书(来自您加载的密钥库以及匹配的私钥)未由受信任的CA(证书颁发机构)签名客户端。

您为客户端显示的代码不执行任何操作来设置或更改其信任库;如果没有其他代码可以执行此操作,或者没有外部设置(例如java命令行选项-Dx=y来设置系统属性),则客户端将使用JSSE默认信任库,如果文件JRE/lib/security/jssecacerts存在,则为文件JRE/lib/security/cacerts,否则,文件jssecacerts(其中JRE是JRE的安装目录;如果使用JDK,则JRE是JDK目录的子目录)。如果自安装JRE以来您(和系统上的其他任何人)都没有修改这些文件,则cacerts不存在,并且keytool -genkeypair包含由Oracle确定的一组“知名根” CA,例如Verisign和Equifax等

因此,您需要:


使用由知名CA颁发的证书;如果您还没有这样的证书,则必须通过证明(至少)控制您所认证的域名并取决于CA来支付费用,才能从CA获得该证书;如果您确实拥有或获得了这样的证书,则将其与任何链证书一起安装在您的密钥库中的私钥条目中(对于著名的CA,几乎总是有至少一个链证书)。
使用由任何其他CA颁发的证书,包括您组成的临时CA,并在限制条件下使用自签名证书,该证书本身就是它自己的CA,例如自动生成的jssecacerts;并将该CA的CA证书(或该自签名证书)放入客户端使用的信任库中。为此,有两种方法:


将服务器的CA证书(或自签名证书)放入客户端使用的JRE的默认信任库文件中。这确实会影响共享该默认信任库的任何其他程序,这可能是所有使用该JRE的其他程序。如果使用cacerts,则仅影响JSSE;如果使用keytool -genkeypair -keystore ksfile -keyalg RSA,则还影响对签名代码(如果有)的验证,并且,如果在适当的位置升级JRE,它也将消失,这在Windows上通常是自动的。
创建(或重用)另一个信任库,将服务器的CA证书放入其中,然后让客户端使用该非默认信任库。有几个选项:在外部设置默认信任库的系统属性,在程序中显式设置它们(在首次使用JSSE之前!),显式加载“密钥库”文件(实际上包含证书)并使用它在非默认SSLSocketFactory中的trustmanager,就像您的服务器代码对keymanager所做的一样,甚至使用您喜欢的任何商店编写自己的trustmanager并类似地使用。



编辑#2简单示例

详细介绍所有这些选项可能太长了,但是一个简单的选项如下。


生成密钥对和(默认)自签名证书:


-dname "CN=common_name_value"

对于提示“名字和姓氏”(实际上是证书中的CommonName属性),请输入服务器的名称,特别是客户端将用于连接服务器的名称;在问题中,这是“本地主机”。其他名称字段无关紧要;根据您的喜好填写或忽略它们,但提示时显示的国家(如果使用)必须为2个字母。无需回答提示,您可以在命令行-alias name上添加。如果服务器的名称不只一个,此处将省略一些选项。
对于其他一些应用程序,您可能需要使用keytool -exportcert -rfc -keystore ksfile [-alias name] -file certfile指定条目名称。对于此问题,则不需要。


获取证书的副本:


keytool -importcert -keystore JRE/lib/security/cacerts -file certfile

在此示例中,客户端与服务器位于同一台计算机上。在另一种情况下,有必要将该文件的内容从服务器复制到客户端。通常,通过复制文件可以最方便地完成此操作。


将证书放入客户端信任库。如上所述,有很多选项,但是您可能会经常看到一个建议,因为通常启动最快的方法是将其放在JRE默认文件cacerts中:


java

其中,JRE是您的JRE(Java运行时环境)的安装目录。这取决于您的操作系统以及如何与软件包管理器一起安装JRE(或JDK,包括JRE)。如果您安装了多个JRE和/或JDK,则取决于您使用的是哪个。

在Unix上,如果您在未指定路径的情况下调用which java,则type java(或者在bash和其他shell中,可能是/somewhere/java[version]/bin/java)将告诉您所运行的完整路径名。但是请注意,这通常是指向实际位置的符号链接,其格式应为bin/java;将lib/security/cacerts部分更改为%windir%\system32\java.exe

在Windows上,如果安装普通的“系统范围” Java,则运行的程序为windir,其中c:\windows通常为c:\program files\java\jreN,但是JRE的实际代码和文件位于c:\program files (x86)\java\jreNjre7中,具体取决于在您的体系结构(32位或64位)上,并且jreN当前为jre8 (如果适用),但将来可能会扩展。或运行Java控制面板;在Java选项卡中,“查看”按钮显示已安装的JRE的位置。

关于java - Java-SocketException:客户端发送请求时重置连接,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31064883/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com