0

数か月前、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 でこの問題が発生することはめったにありません。

前もって感謝します!

4

2 に答える 2

3

あなたはこれをすべて間違っています。

着信データが必要な場合は、 unwrap() を呼び出すだけです。unwrap() は、必要に応じて (BUFFER_UNDERFLOW) 戻りステータスを介してネットワークから読み取るように指示します。また、タスクの実行、ラップの実行などを指示する場合もあります。プロンプトなしでネットワークを読み取ったり、TLS レコードを覗いたり、長さを読み取ったり、データを読み取ったり、再構成したり、SSLEngine にフィードしたりしないでください。それはすでにそれをすべて行っています。あなたのコードは逆さまです。

同様に、書き込み時には、wrap() を呼び出すだけです。ネットワークへの書き込み (BUFFER_OVERFLOW)、タスクの実行などのタイミングを教えてくれます。

于 2013-01-12T23:38:46.177 に答える
-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() メソッドは問題なく動作しました。

于 2013-07-19T16:22:38.010 に答える