几个月前,我使用 NIO 和 SSLEngine 开发了一个 SSL Web 服务器。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 方法。第二条记录是否以某种方式损坏?密码是否已更改,我需要重新启动握手?遇到此错误时我该怎么办?
其他几点:
- 我正在使用 Sun/Oracle JDK 6 和与之捆绑的 Java 安全套接字扩展 (JSSE)。
- 在使用 Mobile Safari、iOS 6 时,我经常看到这个错误。我很少遇到 FireFox 的这个问题。
提前致谢!