几个月前,我开发了一个SSL web server using NIO and the SSLEngine。 GET请求与小POST请求一样工作得很好(约10KB以下)。但是,当我发布大于此的任何内容时,我会遇到零星的SSLException异常。
例如,如果我发布一个小图像(~16KB),我可以看到3个代表应用程序数据的TLS记录。第一个是HTTP标头,另外两个包含有效负载。这是分组的小小的一种感觉:
1: 613 bytes
2: 16341 bytes
3: 549 bytes
在我的代码中,我调用SSLEngine来解包/解密TLS记录(应用程序数据)。 SSLEngine经常在尝试打开第二条记录时窒息。这是错误:
javax.net.ssl.SSLException: Invalid padding
at com.sun.net.ssl.internal.ssl.Alerts.getSSLException(Unknown Source)
at com.sun.net.ssl.internal.ssl.SSLEngineImpl.fatal(Unknown Source)
at com.sun.net.ssl.internal.ssl.SSLEngineImpl.readRecord(Unknown Source)
at com.sun.net.ssl.internal.ssl.SSLEngineImpl.readNetRecord(Unknown Source)
at com.sun.net.ssl.internal.ssl.SSLEngineImpl.unwrap(Unknown Source)
at javax.net.ssl.SSLEngine.unwrap(Unknown Source)
at javaxt.http.servlet.HttpServletRequest.getApplicationData(HttpServletRequest.java:1793)
...
Caused by: javax.crypto.BadPaddingException: Invalid TLS padding: 199
...
这是抛出错误的代码。
private byte[] getApplicationData() throws IOException {
//Read the next 5 bytes from the socket channel. This should contain TLS
//record information.
if (recordHeader==null){
recordHeader = ByteBuffer.allocateDirect(5);
read(recordHeader);
}
//Verify that the message contains application data
recordHeader.rewind();
if(recordHeader.get()!=23) throw new IOException();
//Get the length of the TLS record
recordHeader.position(3);
int recordLength = Integer.parseInt(getHex(recordHeader) + getHex(recordHeader), 16);
recordHeader.rewind();
//Read the TLS record
ByteBuffer recordData = ByteBuffer.allocateDirect(recordLength);
read(recordData);
recordData.rewind();
//Merge the TLS header and record data into a single buffer
ByteBuffer tlsRecord = ByteBuffer.allocateDirect(recordLength+recordHeader.capacity());
tlsRecord.put(recordHeader);
tlsRecord.put(recordData);
tlsRecord.rewind();
//Decrypt the application data
ByteBuffer output = ByteBuffer.allocateDirect(tlsRecord.capacity());
SSLEngineResult serverResult = sslEngine.unwrap(tlsRecord, output);
runDelegatedTasks(serverResult, sslEngine);
byte[] arr = new byte[output.position()];
output.rewind();
output.get(arr);
//Clean up
recordHeader.clear();
recordData.clear();
tlsRecord.clear();
output.clear();
recordHeader = recordData = tlsRecord = output = null;
//Return array
return arr;
}
SSLException抛出此行:
SSLEngineResult serverResult = sslEngine.unwrap(tlsRecord, output);
偶尔会发生此错误。有时我看到它,有时我不看。通常情况下,如果我确实看到错误并只是重新发布数据(例如刷新浏览器),那么一切都很好(没有错误)。
我不是SSL专家,所以我不知道如何最好地调试问题。我很确定我正在正确地调用unwrap方法。第二条记录是否有点腐败?密码是否已更改,我需要重新启动握手?遇到此错误后该怎么办?
结合其他观点:
提前致谢!
答案 0 :(得分:3)
你这样做是错的。
您应该在需要传入数据时调用unwrap()。 unwrap()会告诉您在必要时从网络读取(BUFFER_UNDERFLOW)通过其返回状态;它也可能告诉你运行一个任务,做一个包装等等。你应该不自己自己读取网络,查看TLS记录,读取长度,读取数据,重新组装它们,以及喂他们到SSLEngine。它已经做到了这一切。你的代码是颠倒的。
同样在写作时,只需调用wrap()。它会告诉您何时写入网络(BUFFER_OVERFLOW),运行任务等。
答案 1 :(得分:-1)
经过长时间的中断后,我终于解决了这个问题。问题是,当我读取TLS记录时,我假设我的read()方法返回了所有请求的字节(即recordData ByteBuffer已满)。那不是真的。结果,sslEngine.unwrap()调用被轰炸了。
要解决此问题,我只需将其替换为:
//Read the TLS record
ByteBuffer recordData = ByteBuffer.allocateDirect(recordLength);
read(recordData)
有了这个:
//Read the TLS record
ByteBuffer recordData = ByteBuffer.allocateDirect(recordLength);
int ttl = read(recordData);
if (ttl<recordLength){
while (ttl<recordLength){
recordData.position(ttl);
ByteBuffer tmp = ByteBuffer.allocateDirect(recordLength-ttl);
ttl += read(tmp);
recordData.put(tmp);
}
recordData.rewind();
}
一旦recordData ByteBuffer被填满,SSLEngine unwrap()方法就能完美运行。