TLS-Package 后的神秘字节
Mysterious byte after TLS-Package
我正在尝试创建从 Java 到 Vala 服务器的 SSL TCP 连接。
一切正常,直到我向服务器发送第二个包。 (也是第一个包裹发送罚款)。
服务器只接收第二个包的第一个字节(在本例中为“1”),没有别的,
但如果我在没有 SSL 的情况下连接到服务器,一切正常。
我认为服务器不是问题,因为来自另一个 Vala 客户端的所有其他连接都运行良好。
我使用的是不受信任的证书,因此我创建了一个自定义 TrustManager 并且我使用的是 OpenJDK 7(初级 OS - Linux)。
这是我的代码:
//Main:
SSLHandler handler = new SSLHandler();
handler.createSecureSocket("localhost", 7431);
byte[] data = {1,4,1,1,1,1};
handler.getOutputStream().write(data);
handler.getOutputStream().write(data);
// SSLHandler
public class SSLHandler {
// SSL Socket erstellen
SSLSocket sslSocket;
public void createSecureSocket(String ip, int port) throws UnknownHostException, IOException, KeyManagementException, NoSuchAlgorithmException {
SSLSocketFactory factory = (SSLSocketFactory) new DefaultTrustManager().createSSLFactory("TLS");
sslSocket = (SSLSocket) factory.createSocket(ip, port);
}
public OutputStream getOutputStream() throws IOException {
return sslSocket.getOutputStream();
}
public InputStream getInputStream() throws IOException {
return sslSocket.getInputStream();
}
}
//Custom Trust Manager
public class DefaultTrustManager implements X509TrustManager {
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {}
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {}
@Override
public X509Certificate[] getAcceptedIssuers() {
return null;
}
public SSLSocketFactory createSSLFactory(String protocol) throws NoSuchAlgorithmException, KeyManagementException {
SSLContext sslContext = SSLContext.getInstance(protocol);
TrustManager[] byPassTrustManager = new TrustManager[] {this};
sslContext.init(null, byPassTrustManager, new SecureRandom());
return sslContext.getSocketFactory();
}
}
有人知道这个问题的解决方案吗?
TLDR:进行多次接收。
TLS/SSL 就像 TCP 被定义为 流服务 并且不保证保留记录边界,只是为了按顺序传送字节——或者如果那是不可能的, 指出错误。例如,在 TCP 或 TLS/SSL 中,如果一方执行 3 次发送操作,每次发送操作 1000 字节,并且接收方执行一系列接收操作,则这些操作可能会收到 500、700、1200 和 600 字节。一般来说,接收方必须如有必要进行多次接收 才能接收超过一个字节的数据结构。这可以使用定界符来完成,例如 SMTP:将 header 作为行读取,直到得到一个空行,将 body(数据)读取,直到得到仅由 'dot' (. ).或者简单地用一个计数:继续阅读直到你得到 N 个字节。例如,HTTP 和 HTTPS 使用这两种方法(在某些情况下使用更多)。
在实践中,TCP 实现经常拆分大于 1000 字节的数据,这是由于它们为传输分割数据并重新组合的方式。因此 TCP 程序员在大约 1982 年之后还没有被教导关于这个问题已经从经验中学到 "keep reading until done"。
但历史上 SSL/TLS 实现大多 did 保留最多大约 16k 字节的记录边界,这也是由于协议内部工作的方式。结果,许多没有阅读规范的程序员错误地认为 SSL/TLS 是一种记录服务。
这因 the BEAST attack in 2011, which in some (fairly limited) circumstances can break encrypted data that is sent using SSLv3 or TLSv1.0 with a CBC-mode cipher, because those protocols implement CBC mode by chaining the IV from one data record to the next. One defense against BEAST, implemented by many stacks including Java JSSE, is to transmit each data record in two (or more) parts such that the IV for the sensitive part is not visible in advance; the informal consensus is to use 1 byte and then (at most) the remaining n-1 bytes. See https://security.stackexchange.com/questions/63215/why-does-firefox-split-https-request .
而改变
请注意,此防御仅在 SSL 连接中的第二次和后续写入时完成,因为第一个使用基于 KDF 的 IV,未链接,并且仅适用于 CBC-mode 密码套件,因为只有它们以这种方式链接 IV。 TLSv1.1 及更高版本不需要它,但目前我无法验证 JSSE 是否真的在那种情况下省略了它。
我正在尝试创建从 Java 到 Vala 服务器的 SSL TCP 连接。 一切正常,直到我向服务器发送第二个包。 (也是第一个包裹发送罚款)。 服务器只接收第二个包的第一个字节(在本例中为“1”),没有别的, 但如果我在没有 SSL 的情况下连接到服务器,一切正常。 我认为服务器不是问题,因为来自另一个 Vala 客户端的所有其他连接都运行良好。
我使用的是不受信任的证书,因此我创建了一个自定义 TrustManager 并且我使用的是 OpenJDK 7(初级 OS - Linux)。 这是我的代码:
//Main:
SSLHandler handler = new SSLHandler();
handler.createSecureSocket("localhost", 7431);
byte[] data = {1,4,1,1,1,1};
handler.getOutputStream().write(data);
handler.getOutputStream().write(data);
// SSLHandler
public class SSLHandler {
// SSL Socket erstellen
SSLSocket sslSocket;
public void createSecureSocket(String ip, int port) throws UnknownHostException, IOException, KeyManagementException, NoSuchAlgorithmException {
SSLSocketFactory factory = (SSLSocketFactory) new DefaultTrustManager().createSSLFactory("TLS");
sslSocket = (SSLSocket) factory.createSocket(ip, port);
}
public OutputStream getOutputStream() throws IOException {
return sslSocket.getOutputStream();
}
public InputStream getInputStream() throws IOException {
return sslSocket.getInputStream();
}
}
//Custom Trust Manager
public class DefaultTrustManager implements X509TrustManager {
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {}
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {}
@Override
public X509Certificate[] getAcceptedIssuers() {
return null;
}
public SSLSocketFactory createSSLFactory(String protocol) throws NoSuchAlgorithmException, KeyManagementException {
SSLContext sslContext = SSLContext.getInstance(protocol);
TrustManager[] byPassTrustManager = new TrustManager[] {this};
sslContext.init(null, byPassTrustManager, new SecureRandom());
return sslContext.getSocketFactory();
}
}
有人知道这个问题的解决方案吗?
TLDR:进行多次接收。
TLS/SSL 就像 TCP 被定义为 流服务 并且不保证保留记录边界,只是为了按顺序传送字节——或者如果那是不可能的, 指出错误。例如,在 TCP 或 TLS/SSL 中,如果一方执行 3 次发送操作,每次发送操作 1000 字节,并且接收方执行一系列接收操作,则这些操作可能会收到 500、700、1200 和 600 字节。一般来说,接收方必须如有必要进行多次接收 才能接收超过一个字节的数据结构。这可以使用定界符来完成,例如 SMTP:将 header 作为行读取,直到得到一个空行,将 body(数据)读取,直到得到仅由 'dot' (. ).或者简单地用一个计数:继续阅读直到你得到 N 个字节。例如,HTTP 和 HTTPS 使用这两种方法(在某些情况下使用更多)。
在实践中,TCP 实现经常拆分大于 1000 字节的数据,这是由于它们为传输分割数据并重新组合的方式。因此 TCP 程序员在大约 1982 年之后还没有被教导关于这个问题已经从经验中学到 "keep reading until done"。
但历史上 SSL/TLS 实现大多 did 保留最多大约 16k 字节的记录边界,这也是由于协议内部工作的方式。结果,许多没有阅读规范的程序员错误地认为 SSL/TLS 是一种记录服务。 这因 the BEAST attack in 2011, which in some (fairly limited) circumstances can break encrypted data that is sent using SSLv3 or TLSv1.0 with a CBC-mode cipher, because those protocols implement CBC mode by chaining the IV from one data record to the next. One defense against BEAST, implemented by many stacks including Java JSSE, is to transmit each data record in two (or more) parts such that the IV for the sensitive part is not visible in advance; the informal consensus is to use 1 byte and then (at most) the remaining n-1 bytes. See https://security.stackexchange.com/questions/63215/why-does-firefox-split-https-request .
而改变请注意,此防御仅在 SSL 连接中的第二次和后续写入时完成,因为第一个使用基于 KDF 的 IV,未链接,并且仅适用于 CBC-mode 密码套件,因为只有它们以这种方式链接 IV。 TLSv1.1 及更高版本不需要它,但目前我无法验证 JSSE 是否真的在那种情况下省略了它。