2013-01-11 2 views
0

몇 달 전 SSL web server using NIO and the SSLEngine을 개발했습니다. GET 요청은 작은 POST 요청 (10KB 미만)처럼 훌륭하게 작동합니다. 그러나 POST보다 더 큰 POST를 실행하면 간혹 SSLException 예외가 발생합니다.SSLException : 잘못된 패딩 TLS 응용 프로그램 데이터의 래핑 해제

예를 들어, 작은 이미지 (~ 16KB)를 POST하면 응용 프로그램 데이터를 나타내는 3 개의 TLS 레코드를 볼 수 있습니다. 첫 번째는 HTTP 헤더이고 다른 두 개는 페이로드를 포함합니다.

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 메소드를 올바르게 호출하고 있다고 확신합니다. 두 번째 레코드가 어떻게 든 손상 되었습니까? 사이퍼가 바뀌었고 핸드 셰이크를 다시 시작해야합니까? 이 오류가 발생하면 어떻게해야합니까?

커플 다른 점 : 나는 썬/오라클 JDK 6와 함께 번들로 제공 자바 보안 소켓 확장 (JSSE)을 사용하고

  • .
  • 모바일 사파리, iOS 6에서이 오류가 많이 보입니다. FireFox에서이 문제가 거의 발생하지 않습니다.

미리 감사드립니다.

답변

0

오랜 틈새를 마침내 마침내이 문제를 해결할 수있었습니다. 문제는 TLS 레코드를 읽을 때 read() 메서드가 요청한 바이트를 모두 반환한다고 가정합니다. 즉, recordData ByteBuffer가 가득 찼습니다. 그건 사실이 아니야. 그 결과 sslEngine.unwrap() 호출이 폭파되었습니다.이와

//Read the TLS record 
    ByteBuffer recordData = ByteBuffer.allocateDirect(recordLength); 
    read(recordData) 

: 다음 recordData의 ByteBuffer가 작성되자

//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(); 
    } 

는, SSLEngine에의 언랩() 메소드가 완벽하게 작동

이 문제를 해결하려면 단순히이를 교체했다.

2

이 모든 것을 잘못하고 있습니다.

들어오는 데이터를 원하면 unwrap()을 호출해야합니다. unwrap()은 반환 상태를 통해 필요한 경우 (BUFFER_UNDERFLOW) 네트워크에서 읽을 것을 지시합니다. 그것은 또한 당신에게 작업을 수행하라는 지시를 할 수도 있습니다. 은 네트워크를 직접 읽지 말고, TLS 레코드를 들여다보고, 길이를 읽고, 데이터를 읽고, 재조합하여 SSLEngine에 공급합니다. . 이미 모든 것을 수행합니다. 코드가 거꾸로되어 있습니다.

마찬가지로 작성할 때 wrap()을 호출하십시오. 네트워크 (BUFFER_OVERFLOW)에 쓸 시간, 작업 실행시기 등을 알려줍니다.

+0

오리지널 포스트에 답변 해 주셔서 감사합니다. 문제가 무엇인지 발견했습니다 (허용 된 솔루션 참조). SSLEngine에 유효한 버퍼를 공급하는 한 본질적으로 네트워크를 읽거나, TLS 레코드를 파싱하는 등의 문제가 있다고 생각하지 않습니다. 최소한 내 유스 케이스에서는 잘 작동하는 것 같다. 도와 주셔서 다시 한 번 감사드립니다! – Peter

+0

그게 뭐가 잘못되었는지는 어려운 방법입니다. 답안에서 알 수 있듯이 공식적으로 필요하지 않은 많은 추가 코드로 끝날 것입니다. 이 SSLSocketChanrl 구현은이 엿보기 및 파킹을 포함하지 않습니다. 지상의 구멍에서 TLS 프로토콜을 알지 못합니다. – EJP