数か月前、NIO と SSLEngine を使用して SSL Web サーバーを開発しました。GET リクエストは、小さな POST リクエスト (~10KB 未満) と同様にうまく機能します。ただし、それよりも大きなものを POST すると、散発的な SSLException 例外が発生します。
たとえば、小さな画像 (~16 KB) を POST すると、アプリケーション データを表す 3 つの TLS レコードが表示されます。1 つ目は HTTP ヘッダーで、残りの 2 つにはペイロードが含まれます。パケットの小ささは次のとおりです。
1: 613 bytes
2: 16341 bytes
3: 549 bytes
私のコードでは、SSLEngine を呼び出して TLS レコード (アプリケーション データ) をアンラップ/復号化します。多くの場合、SSLEngine は 2 番目のレコードをアンラップしようとするとチョークします。エラーは次のとおりです。
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);
このエラーは散発的に発生します。見える時もあるし、見えない時もある。多くの場合、エラーが表示され、単純にデータを再 POST すると (ブラウザを更新するなど)、すべてが正常に機能します (エラーは発生しません)。
私は決して SSL の専門家ではないので、問題をデバッグする最善の方法がわかりません。unwrap メソッドを正しく呼び出していると確信しています。2 番目のレコードは何らかの形で破損していますか? 暗号が変更されたので、握手を再開する必要がありますか? このエラーが発生した場合はどうすればよいですか?
他のポイントを結合します。
- Sun/Oracle JDK 6 と、それにバンドルされている Java Secure Socket Extension (JSSE) を使用しています。
- iOS 6 の Mobile Safari でこのエラーが頻繁に発生します。FireFox でこの問題が発生することはめったにありません。
前もって感謝します!