问题
I am trying to create a SSL TCP Connection from Java to a Vala Server. Everything works fine until I send a second package to the server. (also the first package sends fine). The server only receives the first byte (in this case the "1") of the second package, nothing else, but if I connect to the server without SSL everything works fine. I think that the server isn't the problem, because every other connection from another Vala client works pretty well.
I'm using an unstrusted Certificate, so I created a custom TrustManager and I'm using OpenJDK 7 (Elementary OS - Linux). Here's my code:
//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();
}
}
Does anybody know a solution for this problem?
回答1:
TLDR: do multiple receives.
TLS/SSL like TCP is defined as a stream service and does NOT guarantee to preserve record boundaries, only to deliver the bytes in order -- or if that's impossible, indicate the error. For example, in either TCP or TLS/SSL if one party does 3 send operations of 1000 bytes each, and the receiver does a series of receive operations, those might receive 500, 700, 1200, and 600 bytes. In general a receiver must do multiple receives if necessary to receive a data structure of more than one byte. This can be done with delimiters, like SMTP: read header as lines until you get an empty line, and body (data) until you get a line consisting solely of a 'dot' (.). Or simply with a count: keep reading until you get N bytes total. HTTP and HTTPS for example uses both of these (and more in some cases).
In practice TCP implementations have very often split data larger than about 1000 bytes, due to the way they segment data for transmission and reassemble it. Thus TCP programmers after about 1982 not already taught about this issue have learned from experience to "keep reading until done".
But historically SSL/TLS implementations mostly did preserve record boundaries up to roughly 16k bytes, again due to the way the protocol works internally. As a result many programmers who didn't read the specifications came to the false belief that SSL/TLS is a record service. This changed as a result of 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 .
Note this defense is only done on the second and subsequent writes in an SSL connection, because the first one uses an IV based on the KDF, not chained, and only for CBC-mode ciphersuites, because only they chain IVs in this fashion. And it isn't needed for TLSv1.1 and higher, but at the moment I can't verify if JSSE actually omits it in that case.
来源:https://stackoverflow.com/questions/33357924/mysterious-byte-after-tls-package