แฮนด์เชค SSL ไม่สำเร็จ - ใบรับรองไคลเอ็นต์ไม่ถูกต้อง

คุณกำลังดูเอกสารประกอบของ Apigee Edge
ไปที่ เอกสารประกอบของ Apigee X
info

ลักษณะปัญหา

แอปพลิเคชันไคลเอ็นต์ได้รับรหัสสถานะ HTTP 503 พร้อม ข้อความ "Service Unavailable" เป็นการตอบกลับคำขอ API ในข้อมูลการย้ายข้อมูล UI คุณจะเห็นว่า error.cause คือ Received fatal alert: bad_certificate ในโฟลว์คำขอเป้าหมาย สำหรับคำขอ API ที่ล้มเหลว

หากคุณมีสิทธิ์เข้าถึงบันทึกของ Message Processor คุณจะเห็นข้อความแสดงข้อผิดพลาดเป็น Received fatal alert: bad_certificate สำหรับคำขอ API ที่ล้มเหลว ข้อผิดพลาดนี้เกิดขึ้นระหว่างกระบวนการแฮนด์เชค SSL ระหว่าง Message Processor กับเซิร์ฟเวอร์แบ็กเอนด์ในการตั้งค่า TLS แบบ 2 ทาง

ข้อความแสดงข้อผิดพลาด

แอปพลิเคชันไคลเอ็นต์ได้รับโค้ดตอบกลับต่อไปนี้

HTTP/1.1 503 Service Unavailable

นอกจากนี้ คุณอาจเห็นข้อความแสดงข้อผิดพลาดต่อไปนี้

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

ผู้ใช้ Private Cloud จะเห็นข้อผิดพลาดต่อไปนี้สำหรับคำขอ API ที่เฉพาะเจาะจง ในบันทึกของ Message Processor /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

สาเหตุที่เป็นไปได้

สาเหตุที่เป็นไปได้ของปัญหานี้มีดังนี้

สาเหตุ คำอธิบาย วิธีการแก้ปัญหาที่ใช้ได้กับ
ไม่มีใบรับรองไคลเอ็นต์ Keystore ที่ใช้ในปลายทางเป้าหมายของเซิร์ฟเวอร์เป้าหมายไม่มีใบรับรองไคลเอ็นต์ ผู้ใช้ Edge Private และ Public Cloud
ผู้ออกใบรับรองไม่ตรงกัน ผู้ออกใบรับรองของใบรับรองย่อย (ใบรับรองแรกในกลุ่มใบรับรอง) ใน Keystore ของ Message Processor ไม่ตรงกับผู้ออกใบรับรองที่เซิร์ฟเวอร์แบ็กเอนด์ยอมรับ ผู้ใช้ Edge Private และ Public Cloud

ขั้นตอนการวินิจฉัยที่พบบ่อย

  1. เปิดใช้การย้ายข้อมูลใน UI ของ Edge, เรียก API และจำลองปัญหา
  2. ในผลลัพธ์การย้ายข้อมูล UI ให้ไปที่แต่ละเฟสและระบุตำแหน่งที่เกิด ข้อผิดพลาด ข้อผิดพลาดน่าจะเกิดขึ้นในโฟลว์คำขอเป้าหมาย
  3. ตรวจสอบโฟลว์ที่แสดงข้อผิดพลาด คุณควรเห็นข้อผิดพลาดตามที่แสดง ในข้อมูลการย้ายข้อมูลตัวอย่างด้านล่าง:

    alt_text

  4. ตามที่เห็นในภาพหน้าจอด้านบน error.cause คือ "Received fatal alert: bad_certificate".
  5. หากคุณเป็นผู้ใช้ Private Cloud ให้ทำตามวิธีการด้านล่าง:
    1. คุณสามารถรับรหัสข้อความสำหรับคำขอ API ที่ล้มเหลวได้โดยการกำหนดค่าของส่วนหัวข้อผิดพลาด "X-Apigee.Message-ID" ในเฟสที่ระบุโดย AX ในข้อมูลการย้ายข้อมูล
    2. ค้นหารหัสข้อความนี้ในบันทึกของ Message Processor /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]

      บันทึกของ Message Processor มีสแต็กเทรซสำหรับข้อผิดพลาด Received fatal alert: bad_certificate แต่ไม่มี ข้อมูลเพิ่มเติมที่ระบุสาเหตุของปัญหานี้

  6. หากต้องการตรวจสอบปัญหานี้เพิ่มเติม คุณจะต้องบันทึกแพ็กเก็ต TCP/IP โดยใช้ tcpdump เครื่องมือ
    1. หากคุณเป็นผู้ใช้ Private Cloud คุณจะบันทึก แพ็กเก็ต TCP/IP ในเซิร์ฟเวอร์แบ็กเอนด์หรือ Message Processor ได้ เราขอแนะนำให้บันทึกในเซิร์ฟเวอร์แบ็กเอนด์เนื่องจากระบบจะถอดรหัสแพ็กเก็ตใน เซิร์ฟเวอร์แบ็กเอนด์
    2. หากคุณเป็นผู้ใช้ Public Cloud ให้บันทึกแพ็กเก็ต TCP/IP ในเซิร์ฟเวอร์แบ็กเอนด์
    3. เมื่อตัดสินใจได้แล้วว่าจะบันทึกแพ็กเก็ต TCP/IP ที่ใด ให้ใช้คำสั่ง tcpdump ด้านล่างเพื่อบันทึกแพ็กเก็ต TCP/IP
    4. tcpdump -i any -s 0 host <IP address> -w <File name>

      หากคุณบันทึกแพ็กเก็ต TCP/IP ใน Message Processor ให้ใช้ที่อยู่ IP สาธารณะของเซิร์ฟเวอร์แบ็กเอนด์ในคำสั่ง tcpdump

      หากเซิร์ฟเวอร์แบ็กเอนด์/Message Processor มีที่อยู่ IP หลายรายการ แล้ว คุณจะต้องใช้คำสั่ง tcpdump อื่น โปรดดูข้อมูลเพิ่มเติมเกี่ยวกับเครื่องมือนี้และคำสั่งอื่นๆ ที่คล้ายกันได้ที่ tcpdump

  7. วิเคราะห์แพ็กเก็ต TCP/IP โดยใช้เครื่องมือ Wireshark หรือเครื่องมือที่คล้ายกันที่คุณคุ้นเคย

ต่อไปนี้คือการวิเคราะห์ข้อมูลแพ็กเก็ต TCP/IP ตัวอย่างโดยใช้เครื่องมือ Wireshark

alt_text

  1. ข้อความ #4 ใน tcpdump ด้านบนแสดงให้เห็นว่า Message Processor (ต้นทาง) ส่งข้อความ "Client Hello" ไปยังเซิร์ฟเวอร์แบ็กเอนด์ (ปลายทาง)
  2. ข้อความ #5 แสดงให้เห็นว่าเซิร์ฟเวอร์แบ็กเอนด์รับทราบข้อความ Client Hello จาก Message Processor
  3. เซิร์ฟเวอร์แบ็กเอนด์ส่งข้อความ "Server Hello" พร้อมกับใบรับรอง จากนั้นขอให้ไคลเอ็นต์ส่งใบรับรองในข้อความ #7
  4. Message Processor ตรวจสอบใบรับรองเสร็จสมบูรณ์และรับทราบ ข้อความ ServerHello ของเซิร์ฟเวอร์แบ็กเอนด์ในข้อความ #8
  5. Message Processor ส่งใบรับรองไปยังเซิร์ฟเวอร์แบ็กเอนด์ในข้อความ #9
  6. เซิร์ฟเวอร์แบ็กเอนด์รับทราบการรับใบรับรองของ Message Processor Certificate ในข้อความ #11
  7. อย่างไรก็ตาม เซิร์ฟเวอร์จะส่งการแจ้งเตือนร้ายแรง: ใบรับรองไม่ถูกต้อง ไปยัง Message Processor ทันที (ข้อความ #12) ซึ่งแสดงให้เห็นว่าใบรับรองที่ Message Processor ส่งมาไม่ถูกต้อง ดังนั้นการตรวจสอบใบรับรองจึงล้มเหลวใน เซิร์ฟเวอร์แบ็กเอนด์ ด้วยเหตุนี้ การแฮนด์เชค SSL จึงล้มเหลวและระบบจะปิดการเชื่อมต่อ


    alt_text

  8. ตอนนี้มาดูข้อความ #9 เพื่อตรวจสอบเนื้อหาของใบรับรองที่ Message Processor ส่งมา


    alt_text

  9. คุณจะเห็นว่าเซิร์ฟเวอร์แบ็กเอนด์ไม่ได้รับใบรับรองจากไคลเอ็นต์ (ความยาวใบรับรอง: 0) ดังนั้น เซิร์ฟเวอร์แบ็กเอนด์จึงส่งการแจ้งเตือนร้ายแรง: ใบรับรองไม่ถูกต้อง
  10. โดยปกติแล้วเหตุการณ์นี้จะเกิดขึ้นเมื่อไคลเอ็นต์ ซึ่งก็คือ Message Processor (กระบวนการที่ใช้ Java) มีลักษณะดังนี้
    1. ไม่มีใบรับรองไคลเอ็นต์ใน Keystore หรือ
    2. ส่งใบรับรองไคลเอ็นต์ไม่ได้ เหตุการณ์นี้อาจเกิดขึ้นหากไคลเอ็นต์ไม่พบ ใบรับรองที่ออกโดยผู้ออกใบรับรองที่เซิร์ฟเวอร์แบ็กเอนด์ยอมรับ หมายความว่า หากผู้ออกใบรับรองของใบรับรองย่อยของไคลเอ็นต์ (เช่น ใบรับรองแรกในกลุ่มใบรับรอง) ไม่ตรงกับผู้ออกใบรับรองที่เซิร์ฟเวอร์แบ็กเอนด์ยอมรับ Message Processor จะไม่ส่งใบรับรอง

มาดูสาเหตุแต่ละข้อแยกกันดังนี้

สาเหตุ: ไม่มีใบรับรองไคลเอ็นต์

การวินิจฉัย

หากไม่มีใบรับรองใน Keystore ที่ระบุไว้ในส่วนข้อมูล SSL ของปลายทางเป้าหมาย หรือเซิร์ฟเวอร์เป้าหมายที่ใช้ในปลายทางเป้าหมาย นั่นคือสาเหตุของข้อผิดพลาดนี้

ทำตามขั้นตอนด้านล่างเพื่อดูว่านี่คือสาเหตุหรือไม่

  1. ระบุ Keystore ที่ใช้ในปลายทางเป้าหมายหรือเซิร์ฟเวอร์เป้าหมาย สำหรับพร็อกซี API ที่เฉพาะเจาะจงโดยทำตามขั้นตอนด้านล่าง:
    1. รับชื่ออ้างอิง Keystore จากองค์ประกอบ Keystore ในส่วน SSLInfo ในปลายทางเป้าหมายหรือเซิร์ฟเวอร์เป้าหมาย

      มาดูส่วน SSLInfo ตัวอย่างในการกำหนดค่าปลายทางเป้าหมายกัน

      <SSLInfo>
        <Enabled>true</Enabled>
        <ClientAuthEnabled>true</ClientAuthEnabled>
        <KeyStore>ref://myKeystoreRef</KeyStore>
        <KeyAlias>myKey</KeyAlias>
        <TrustStore>ref://myTrustStoreRef</TrustStore>
      </SSLInfo>
    2. ในตัวอย่างด้านบน ชื่ออ้างอิง Keystore คือ "myKeystoreRef"
    3. ไปที่ UI ของ Edge แล้วเลือกพร็อกซี API -> การกำหนดค่าสภาพแวดล้อม

      เลือกแท็บข้อมูลอ้างอิง แล้วค้นหาชื่ออ้างอิง Keystore จดชื่อในคอลัมน์ข้อมูลอ้างอิง สำหรับข้อมูลอ้างอิง Keystore ที่เฉพาะเจาะจง ชื่อนี้จะเป็นชื่อ Keystore


      alt_text

    4. ในตัวอย่างด้านบน คุณจะเห็นว่า myKeystoreRef มีข้อมูลอ้างอิง ถึง "myKeystore" ดังนั้นชื่อ Keystore คือ myKeystore
  2. ตรวจสอบว่า Keystore นี้มีใบรับรองหรือไม่โดยใช้ UI ของ Edge หรือ List certs for keystore API
  3. หาก Keystore มีใบรับรอง ให้ไปที่ สาเหตุ: ผู้ออกใบรับรองไม่ตรงกัน.
  4. หาก Keystore ไม่มีใบรับรอง นั่นคือเหตุผลที่ Message Processor ไม่ได้ส่งใบรับรองไคลเอ็นต์

ความละเอียด

  1. ตรวจสอบว่าได้อัปโหลดกลุ่มใบรับรองไคลเอ็นต์ที่ถูกต้องและสมบูรณ์ไปยัง Keystore ที่เฉพาะเจาะจงใน Message Processor แล้ว

สาเหตุ: ผู้ออกใบรับรองไม่ตรงกัน

โดยทั่วไป เมื่อเซิร์ฟเวอร์ขอให้ไคลเอ็นต์ส่งใบรับรอง เซิร์ฟเวอร์จะ ระบุชุดผู้ออกใบรับรองหรือผู้ออกใบรับรองที่ยอมรับ หากผู้ออกใบรับรองของใบรับรองย่อย (เช่น ใบรับรองแรกในกลุ่มใบรับรอง) ใน Keystore ของ Message Processor ไม่ตรงกับผู้ออกใบรับรองที่เซิร์ฟเวอร์แบ็กเอนด์ยอมรับ Message Processor (ซึ่งเป็น กระบวนการที่ใช้ Java) จะไม่ส่งใบรับรองไปยังเซิร์ฟเวอร์แบ็กเอนด์

ทำตามขั้นตอนด้านล่างเพื่อยืนยันว่ากรณีนี้เกิดขึ้นหรือไม่

  1. List certs for keystore API.
  2. รับรายละเอียดของใบรับรองแต่ละรายการที่ได้รับในขั้นตอนที่ 1 ด้านบนโดยใช้ Get cert for keystore API
  3. จดผู้ออกใบรับรองย่อย (เช่น ใบรับรองแรกในกลุ่มใบรับรอง) ที่จัดเก็บไว้ใน Keystore

    ใบรับรองย่อยตัวอย่าง

    {
      "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 หรือไม่ หากไม่ตรงกัน Message Processor จะไม่ส่งใบรับรองไคลเอ็นต์ ไปยังเซิร์ฟเวอร์แบ็กเอนด์

    ในตัวอย่างด้านบน คุณจะเห็นว่าผู้ออกใบรับรองย่อยของไคลเอ็นต์ ใน Keystore ของ Message Processor ไม่ตรงกับ ผู้ออกใบรับรองที่เซิร์ฟเวอร์แบ็กเอนด์ยอมรับ ดังนั้น Message Processor จึงไม่ ส่งใบรับรองไคลเอ็นต์ไปยังเซิร์ฟเวอร์แบ็กเอนด์ ซึ่งทำให้การแฮนด์เชค 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. หากคุณเป็นผู้ใช้ Public Cloud โปรดระบุข้อมูลต่อไปนี้
    1. ชื่อองค์กร
    2. ชื่อสภาพแวดล้อม
    3. ชื่อพร็อกซี API
    4. คำสั่ง curl ที่สมบูรณ์เพื่อจำลองข้อผิดพลาด
    5. ไฟล์การย้ายข้อมูลที่แสดงข้อผิดพลาด
    6. แพ็กเก็ต TCP/IP ที่บันทึกไว้ในเซิร์ฟเวอร์แบ็กเอนด์
  2. หากคุณเป็นผู้ใช้ Private Cloud โปรดระบุข้อมูลต่อไปนี้
    1. ข้อความแสดงข้อผิดพลาดทั้งหมดที่พบ
    2. App Bundle ของพร็อกซี API
    3. ไฟล์การย้ายข้อมูลที่แสดงข้อผิดพลาด
    4. บันทึกของ Message Processor /opt/apigee/var/log/edge-message-processor/logs/system.log
    5. แพ็กเก็ต TCP/IP ที่บันทึกไว้ในเซิร์ฟเวอร์แบ็กเอนด์หรือ Message Processor
    6. เอาต์พุตของ Get cert for keystore API.
  3. รายละเอียดเกี่ยวกับส่วนต่างๆ ในเพลย์บุ๊กนี้ที่คุณได้ลองทำตามแล้ว รวมถึงข้อมูลเชิงลึกอื่นๆ ที่จะช่วยให้เราแก้ปัญหานี้ได้อย่างรวดเร็ว