SSL 핸드셰이크 실패 - 잘못된 클라이언트 인증서

<ph type="x-smartling-placeholder"></ph> 현재 Apigee Edge 문서를 보고 있습니다.
Apigee X 문서.
정보

증상

클라이언트 애플리케이션이 HTTP 상태 코드 503을 수신합니다. API 요청에 대한 응답으로 'Service Unavailable' 메시지를 반환합니다. UI 트레이스에서 error.cause 대상 요청 흐름의 Received fatal alert: bad_certificate 를 반환합니다.

메시지 프로세서 로그에 액세스할 수 있는 경우 그러면 다음과 같은 오류 메시지가 표시됩니다. Received fatal alert: bad_certificate 를 반환합니다. 이 오류는 SSL 핸드셰이크 중에 양방향 TLS 설정으로 메시지 프로세서와 백엔드 서버 간의 프로세스를 처리합니다.

오류 메시지

클라이언트 애플리케이션이 다음과 같은 응답 코드를 받습니다.

HTTP/1.1 503 Service Unavailable

또한 다음과 같은 오류 메시지가 표시될 수 있습니다.

{
 "fault": {
    "faultstring":"The Service is temporarily unavailable",
    "detail":{
        "errorcode":"messaging.adaptors.http.flow.ServiceUnavailable"
    }
 }
}

프라이빗 클라우드 사용자에게 특정 API 요청에 대해 다음 오류가 표시됩니다. 다음과 같이 메시지 프로세서 로그 /opt/apigee/var/log/edge-message-processor/system.log에 표시됩니다.

2017-10-23 05:28:57,813 org:org-name env:env-name api:apiproxy-name rev:revision-number messageid:message_id NIOThread@0 ERROR HTTP.CLIENT - HTTPClient$Context.handshakeFailed() : SSLClientChannel[C:IP address:port # Remote host:IP address:port #]@65461 useCount=1 bytesRead=0 bytesWritten=0 age=529ms lastIO=529ms handshake failed, message: Received fatal alert: bad_certificate

가능한 원인

이 문제의 가능한 원인은 다음과 같습니다.

원인 설명 해당하는 문제 해결 안내
클라이언트 인증서 없음 대상 서버의 대상 엔드포인트에 사용되는 키 저장소에 클라이언트 인증서가 없습니다. Edge 프라이빗 및 퍼블릭 클라우드 사용자
인증 기관 불일치 리프 인증서의 인증 기관 (인증서 체인의 첫 번째 인증서) 이 백엔드 서버에서 수락한 인증 기관과 일치하지 않습니다. Edge 프라이빗 및 퍼블릭 클라우드 사용자

일반적인 진단 단계

  1. Edge UI에서 트레이스를 사용 설정하고 API를 호출한 후 문제를 재현합니다.
  2. UI 트레이스 결과에서 각 단계를 탐색하고 오류가 발생했습니다. 대상 요청 흐름에서 오류가 발생했을 수 있습니다.
  3. 오류가 표시된 Flow를 검토합니다. 다음과 같이 오류를 관찰해야 합니다. 아래 trace 예시에서:

    alt_text

  4. 위의 스크린샷에서 볼 수 있듯이 error.cause 'Received fatal alert: bad_certificate'
  5. Private Cloud 사용자인 경우 다음 안내를 따르세요. <ph type="x-smartling-placeholder">
      </ph>
    1. 실패한 API 요청에 대한 메시지 ID는 오류 헤더 'X-Apigee.Message-ID'의 값 트레이스에서 AX가 표시한 단계에서
    2. 메시지 프로세서 로그에서 이 메시지 ID를 검색합니다. /opt/apigee/var/log/edge-message-processor/system.log 에서 오류에 대한 추가 정보를 확인할 수 있습니다.
      2017-10-23 05:28:57,813 org:org-name env:env-name api:apiproxy-name
      rev:revision-number messageid:message_id NIOThread@0 ERROR HTTP.CLIENT - HTTPClient$Context.handshakeFailed() :
      SSLClientChannel[C:IP address:port # Remote host:IP address:port #]@65461 useCount=1
      bytesRead=0 bytesWritten=0 age=529ms lastIO=529ms handshake failed, message: Received fatal alert: bad_certificate
      2017-10-23 05:28:57,813 org:org-name env:env-name api:apiproxy-name
      rev:revision-number messageid:message_id NIOThread@0 ERROR HTTP.CLIENT - HTTPClient$Context.handshakeFailed() : SSLInfo:
      KeyStore:java.security.KeyStore@52de60d9 KeyAlias:KeyAlias TrustStore:java.security.KeyStore@6ec45759
      2017-10-23 05:28:57,814 org:org-name env:env-name api:apiproxy-name
      rev:revision-number messageid:message_id NIOThread@0 ERROR ADAPTORS.HTTP.FLOW - RequestWriteListener.onException() :
      RequestWriteListener.onException(HTTPRequest@6071a73d)
      javax.net.ssl.SSLException: Received fatal alert: bad_certificate
      at sun.security.ssl.Alerts.getSSLException(Alerts.java:208) ~[na:1.8.0_101]
      at sun.security.ssl.SSLEngineImpl.fatal(SSLEngineImpl.java:1666) ~[na:1.8.0_101]
      at sun.security.ssl.SSLEngineImpl.fatal(SSLEngineImpl.java:1634) ~[na:1.8.0_101]
      at sun.security.ssl.SSLEngineImpl.recvAlert(SSLEngineImpl.java:1800) ~[na:1.8.0_101]
      at com.apigee.nio.NIOSelector$SelectedIterator.findNext(NIOSelector.java:496) [nio-1.0.0.jar:na]
      at com.apigee.nio.util.NonNullIterator.computeNext(NonNullIterator.java:21) [nio-1.0.0.jar:na]
      at com.apigee.nio.util.AbstractIterator.hasNext(AbstractIterator.java:47) [nio-1.0.0.jar:na]
      at com.apigee.nio.NIOSelector$2.findNext(NIOSelector.java:312) [nio-1.0.0.jar:na]
      at com.apigee.nio.NIOSelector$2.findNext(NIOSelector.java:302) [nio-1.0.0.jar:na]
      at com.apigee.nio.util.NonNullIterator.computeNext(NonNullIterator.java:21) [nio-1.0.0.jar:na]
      at com.apigee.nio.util.AbstractIterator.hasNext(AbstractIterator.java:47) [nio-1.0.0.jar:na]
      at com.apigee.nio.handlers.NIOThread.run(NIOThread.java:59) [nio-1.0.0.jar:na]
      

      메시지 프로세서 로그에 오류에 대한 스택 추적이 있습니다. Received fatal alert: bad_certificate, 제외: 이 문제의 원인을 나타내는 추가 정보가 있습니다.

  6. 이 문제를 더 자세히 조사하려면 다음을 사용하여 TCP/IP 패킷을 캡처해야 합니다. tcpdump 도구
    1. Private Cloud 사용자인 경우 백엔드 서버 또는 메시지 프로세서의 TCP/IP 패킷 패킷이 복호화될 때 백엔드 서버에서 캡처하는 것이 좋습니다. 백엔드 서버입니다
    2. 퍼블릭 클라우드 사용자인 경우 TCP/IP를 캡처합니다. 백엔드 서버의 패킷도 확인할 수 있습니다.
    3. TCP/IP 패킷을 캡처할 위치를 결정했으면 tcpdump 미만 명령을 사용하여 TCP/IP 패킷을 캡처합니다.
    4. tcpdump -i any -s 0 host <IP address> -w <File name>
      드림

      메시지 프로세서에서 TCP/IP 패킷을 가져올 경우에는 tcpdump 명령어에 백엔드 서버의 공개 IP 주소입니다.

      백엔드 서버/메시지 프로세서의 IP 주소가 여러 개인 경우 다른 tcpdump 명령어를 사용해야 합니다 다음을 참고하세요. tcpdump 를 참조하세요.

  7. Wireshark 도구 또는 익숙한 유사 도구를 사용하여 TCP/IP 패킷을 분석합니다.

다음은 Wireshark 도구를 사용하여 샘플 TCP/IP 패킷 데이터를 분석한 것입니다.

alt_text

  1. 위의 tcpdump에서 메시지 #4는 메시지 프로세서 (소스)가 백엔드 서버 (대상)에 'Client Hello' 메시지를 보냈습니다.
  2. 메시지 #5는 백엔드 서버가 클라이언트 Hello 메시지를 승인했음을 보여줍니다. 전송됩니다.
  3. 백엔드 서버는 인증서와 함께 그런 다음 메시지 #7에서 인증서를 보내도록 클라이언트에 요청합니다.
  4. 메시지 프로세서는 인증서 확인을 완료하고 메시지 #8에 백엔드 서버의 ServerHello 메시지를 반환합니다.
  5. 메시지 프로세서는 메시지 #9의 백엔드 서버로 인증서를 보냅니다.
  6. 백엔드 서버는 메시지 프로세서의 메시지 #11의 인증서입니다.
  7. 하지만 치명적 경고: 잘못된 인증서를 메시지 프로세서 (메시지 #12). 이는 메시지 프로세서가 불량해서 인증서 확인에 실패했기 때문에 백엔드 서버입니다 그 결과 SSL 핸드셰이크가 실패했고, 영업이 종료됩니다.


    alt_text

  8. 이제 메시지 #9를 보고 메시지 프로세서가 보낸 인증서의 내용을 확인해 보겠습니다.


    alt_text

  9. 백엔드 서버가 클라이언트에서 인증서를 받지 못했습니다. (인증서 길이: 0). 따라서 백엔드 서버에서 Fatal Alert: Bad Certificate를 보냅니다.
  10. 일반적으로 클라이언트, 즉 메시지 프로세서 (Java 기반 프로세스)에서 이런 상황이 발생합니다. <ph type="x-smartling-placeholder">
      </ph>
    1. 키 저장소에 클라이언트 인증서가 없는 경우
    2. 클라이언트 인증서를 보낼 수 없습니다. 이는 포드의 현재 위치에서 백엔드 서버의 허용 가능한 인증기관 중 하나에서 발급한 인증서 즉, 클라이언트의 리프 인증서의 인증 기관에서 체인의 첫 번째 인증서가 백엔드 서버의 메시지 프로세서가 인증서를 보내지 않습니다.

이러한 각 원인을 다음과 같이 자세히 살펴보겠습니다.

원인: 클라이언트 인증서가 없음

진단

SSL 정보 섹션에 지정된 키 저장소에 인증서가 없는 경우 대상 엔드포인트에서 사용되는 대상 서버의 이것이 오류의 원인입니다

원인을 확인하려면 다음 단계를 따르세요.

  1. 대상 엔드포인트 또는 대상 서버에서 사용 중인 키 저장소 확인 특정 API 프록시에 대해 다음 단계를 따르세요. <ph type="x-smartling-placeholder">
      </ph>
    1. 키 저장소 요소에서 키 저장소 참조 이름 가져오기 대상 엔드포인트 또는 대상 서버의 SSLInfo 섹션에 있습니다.

      대상 엔드포인트 구성의 샘플 SSLInfo 섹션을 살펴보겠습니다.

      <SSLInfo>
        <Enabled>true</Enabled>
        <ClientAuthEnabled>true</ClientAuthEnabled>
        <KeyStore>ref://myKeystoreRef</KeyStore>
        <KeyAlias>myKey</KeyAlias>
        <TrustStore>ref://myTrustStoreRef</TrustStore>
      </SSLInfo>
    2. 위의 예에서 키 저장소 참조 이름은 'myKeystoreRef'입니다.
    3. Edge UI로 이동하여 API 프록시 -> 환경 구성.

      References 탭을 선택하고 키 저장소 참조 이름을 검색합니다. 특정 키 저장소 참조의 참조 열에 이름을 기록해 둡니다. 이 이름이 키 저장소 이름이 됩니다.


      alt_text

    4. 위의 예에서는 myKeystoreRef에 참조가 있음을 알 수 있습니다. 'myKeystore'로 설정합니다. 따라서 키 저장소 이름은 myKeystore입니다.
  2. Edge UI 또는 keystore API의 인증서 나열
  3. 키 저장소에 인증서가 포함된 경우 원인: 인증 기관 불일치
  4. 키 저장소에 인증서가 없으면 클라이언트 인증서가 메시지 프로세서에 의해 전송되지 않습니다.

해상도

  1. 적절하고 완전한 클라이언트 인증서 체인이 메시지 프로세서의 특정 키 저장소에 업로드되었는지 확인합니다.

원인: 인증 기관 불일치

일반적으로 서버는 클라이언트에게 인증서를 보내도록 요청할 때 허용되는 발급자 또는 인증기관 집합을 나타냅니다. 리프 인증서의 발급자/인증 기관 (예: 첫 번째 인증서 체인의 인증서)는 메시지 프로세서의 키 저장소에 있는 백엔드 서버에서 수락한 인증 기관과 일치하지 않으면 메시지 프로세서 (Java 기반 프로세스)는 인증서를 백엔드 서버로 전송하지 않습니다

다음 단계에 따라 이 경우에 해당하는지 확인하세요.

  1. keystore API의 인증서 나열
  2. 위의 1단계에서 획득한 각 인증서의 세부정보 가져오기: keystore API에 대한 인증서 가져오기.
  3. 키 저장소에 저장된 리프 인증서 (즉, 인증서 체인의 첫 번째 인증서) 발급자를 기록합니다.

    샘플 리프 인증서

    {
      "certInfo" : [ {
        "basicConstraints" : "CA:FALSE",
        "expiryDate" : 1578889324000,
        "isValid" : "Yes",
        "issuer" : "CN=MyCompany Test SHA2 CA G2, DC=testcore, DC=test, DC=dir, DC=mycompany, DC=com",
        "publicKey" : "RSA Public Key, 2048 bits",
        "serialNumber" : "65:00:00:00:d2:3e:12:d8:56:fa:e2:a9:69:00:06:00:00:00:d2",
        "sigAlgName" : "SHA256withRSA",
        "subject" : "CN=nonprod-api.mycompany.com, OU=ITS, O=MyCompany, L=MELBOURNE, ST=VIC, C=AU",
        "subjectAlternativeNames" : [ ],
        "validFrom" : 1484281324000,
        "version" : 3
      } ],
      "certName" : "nonprod-api.mycompany.com.key.pem-cert"
    }
    

    위의 예시에서 발급자/인증 기관은 "CN=MyCompany Test SHA2 CA G2, DC=testcore, DC=test, DC=dir, DC=mycompany, DC=com" 드림

  4. 다음 기법 중 하나를 사용하여 백엔드 서버에서 허용하는 발행자 또는 인증기관 목록을 확인합니다.

    기술 1: 아래의 openssl 명령어 사용

    openssl s_client -host <backend server host name> -port <Backend port#> -cert <Client Certificate> -key <Client Private Key>
    

    '허용되는 클라이언트 인증서 CA 이름'이라는 제목의 섹션을 참조하세요. 가 이 명령어의 출력에 표시됩니다.

    Acceptable client certificate CA names
    /C=AU/ST=VIC/L=MELBOURNE/O=MyCompany/OU=ITS/CN=nonprod-api.mycompany.com
    /C=AU/ST=VIC/L=MELBOURNE/O=MyCompany/OU=ITS/CN=nonprod-api.mycompany.com
    

    기술 #2: 다음 위치에서 Certificate Request 패킷 확인 TCP/IP 패킷. 백엔드 서버가 클라이언트에 인증서를 전송하도록 요청합니다.

    위에 표시된 샘플 TCP/IP 패킷에서 Certificate Request는 패킷은 메시지 #7입니다. '고유 이름' 섹션을 참조하세요. 여기에는 백엔드 서버의 허용되는 인증 기관이 포함됩니다.

    alt_text

  5. 3단계에서 획득한 인증 기관이 목록과 일치하는지 확인 4단계에서 입수한 백엔드 서버의 수락된 발급자 또는 인증기관의 ID를 포함합니다. 불일치가 있는 경우 메시지 프로세서가 클라이언트 인증서를 전송하지 않습니다. 백엔드 서버로 전송합니다

    위의 예에서 클라이언트의 리프 인증서 발급자가 오류가 백엔드 서버의 어떤 것과도 일치하지 않는지 허용되는 인증 기관. 따라서 메시지 프로세서는 백엔드 서버로 클라이언트 인증서를 전송합니다. 이로 인해 SSL 핸드셰이크가 실패하고 백엔드 서버에서 'Fatal alert: bad_certificate'을(를) 보냅니다. 메시지가 표시됩니다.

해상도

  1. 일치하는 발급자/인증 기관이 있는지 확인하세요. 클라이언트의 리프 인증서 (첫 번째 인증서)의 발급자/인증 기관 백엔드 서버의 Truststore에 저장됩니다.
  2. 이 플레이북에 설명된 예에서 발급기관의 인증서는 "issuer" : "CN=MyCompany Test SHA2 CA G2, DC=testcore, DC=test, DC=dir, DC=mycompany, DC=com" 드림 백엔드 서버의 Truststore에 추가되어 문제를 해결했습니다.

문제가 계속되면 진단 정보를 수집해야 함을 참고하세요.

진단 정보 수집 필요

위의 안내를 따른 후에도 문제가 지속되면 다음 진단 정보를 수집해 주세요 연락 및 공유 대상 Apigee Edge 지원:

  1. 퍼블릭 클라우드 사용자인 경우 다음 정보를 제공하세요. <ph type="x-smartling-placeholder">
      </ph>
    1. 조직 이름
    2. 환경 이름
    3. API 프록시 이름
    4. curl 명령어를 완료하여 오류를 재현하세요.
    5. 오류를 보여주는 추적 파일
    6. 백엔드 서버에서 캡처된 TCP/IP 패킷
  2. 프라이빗 클라우드 사용자인 경우 다음 정보를 제공하세요. <ph type="x-smartling-placeholder">
      </ph>
    1. 오류 메시지 완료 확인됨
    2. API 프록시 번들
    3. 오류를 보여주는 추적 파일
    4. 메시지 프로세서 로그 /opt/apigee/var/log/edge-message-processor/logs/system.log
    5. 백엔드 서버 또는 메시지 프로세서에서 캡처된 TCP/IP 패킷
    6. Get cert for keystore API의 출력
  3. 이 플레이북의 어떤 섹션을 시도했는지 및 기타 다른 섹션인지에 관한 세부정보 유용한 정보를 제공해 주세요.