วิธีแก้ข้อผิดพลาด javax.net.ssl.SSLHandshakeException


101

ฉันเชื่อมต่อกับ VPN เพื่อตั้งค่า API สินค้าคงคลังเพื่อรับรายการผลิตภัณฑ์และใช้งานได้ดี เมื่อฉันได้รับผลลัพธ์จากบริการบนเว็บและฉันเชื่อมโยงกับ UI นอกจากนี้ฉันยังรวม PayPal เข้ากับแอปพลิเคชันของฉันสำหรับการชำระเงินด่วนเมื่อฉันทำการเรียกชำระเงินฉันพบข้อผิดพลาดนี้ ฉันใช้ servlet สำหรับกระบวนการ back-end มีใครบอกวิธีแก้ไขปัญหานี้ได้ไหม

javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: 
PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException:
unable to find valid certification path to requested target

1
สิ่งที่ฉันอยากรู้คือเป้าหมายที่แน่นอนในกรณีนั้น ... คุณได้รับข้อยกเว้น แต่คุณไม่ได้รับข้อมูลเกี่ยวกับเป้าหมายซึ่งอาจแตกต่างจากที่คุณคาดหวัง .. ฉันมีกรณีเช่นนี้ฉันแน่ใจว่าลิงค์ของฉัน มีใบรับรองและฉันยังคงได้รับข้อยกเว้นนี้
ante.sabo

คำตอบ:


151

ขั้นแรกคุณต้องขอรับใบรับรองสาธารณะจากเซิร์ฟเวอร์ที่คุณพยายามเชื่อมต่อ ซึ่งสามารถทำได้หลายวิธีเช่นติดต่อผู้ดูแลระบบเซิร์ฟเวอร์และขอใช้ OpenSSL เพื่อดาวน์โหลดหรือเนื่องจากดูเหมือนว่าเป็นเซิร์ฟเวอร์ HTTP การเชื่อมต่อกับเบราว์เซอร์ใด ๆ การดูข้อมูลความปลอดภัยของเพจ และบันทึกสำเนาใบรับรอง (Google น่าจะบอกคุณได้อย่างชัดเจนว่าต้องทำอะไรกับเบราว์เซอร์เฉพาะของคุณ)

ตอนนี้คุณได้บันทึกใบรับรองไว้ในไฟล์แล้วคุณต้องเพิ่มใบรับรองดังกล่าวไปยังที่เก็บความน่าเชื่อถือของ JVM ที่$JAVA_HOME/jre/lib/security/สำหรับ JREs หรือ$JAVA_HOME/lib/securityสำหรับ JDKs มีไฟล์ชื่อcacertsซึ่งมาพร้อมกับชวาและมีใบรับรองของประชาชนที่รู้จักกันดีหน่วยรับรอง ในการนำเข้าใบรับรองใหม่ให้เรียกใช้ keytool ในฐานะผู้ใช้ที่มีสิทธิ์เขียนลงใน cacerts:

keytool -import -file <the cert file> -alias <some meaningful name> -keystore <path to cacerts file>

มักจะถามรหัสผ่านจากคุณ รหัสผ่านเริ่มต้นเป็นที่มาพร้อมกับ Java changeitเป็น แทบไม่มีใครเปลี่ยนแปลงเลย หลังจากที่คุณทำตามขั้นตอนง่ายๆเหล่านี้เสร็จแล้วคุณจะสื่อสารได้อย่างปลอดภัยและด้วยความมั่นใจว่าคุณกำลังพูดคุยกับเซิร์ฟเวอร์ที่ถูกต้องและมีเพียงเซิร์ฟเวอร์ที่ถูกต้องเท่านั้น (ตราบเท่าที่พวกเขาไม่ทำคีย์ส่วนตัวหาย)


12
ฉันต้องการรายละเอียดเล็กน้อยมากกว่า "ไม่ทำงาน" ลองอัปเดตคำถามของคุณด้วยสิ่งที่คุณได้ลองและผลลัพธ์ที่ผิดพลาด น่าเสียดายที่มันเลยเวลาเข้านอนไปแล้วบางทีอาจมีคนอื่นตอบคำถามของคุณได้ นี่เป็นสถานการณ์ที่พบบ่อยมาก คุณจะพบจำนวนของข้อมูลบนออนไลน์รวมทั้งเอกสาร keytool
Ryan Stewart

โดยทั่วไปฉันต้องมีใบรับรอง PayPal ฉันทำตามขั้นตอนของคุณแล้วและยังเพิ่มใบรับรองในตำแหน่งที่เหมาะสม จากนั้นฉันเรียกใช้แอพมันบอกว่ามีข้อผิดพลาดเดียวกัน?
selladurai

จริงๆแล้วมันใช้งานได้ดีไม่มีปัญหาเบราว์เซอร์และฉันเชื่อมต่อ VPN เพื่อรับรายการสินค้าคงคลังในตอนนั้นที่ฉันชำระเงินด้วย PayPal มันบอกว่าเป็นข้อผิดพลาด SSL แต่ฉันใช้ข้อมูลออฟไลน์หรือข้อมูลโค้ดร้อนหมายความว่ามันใช้งานได้
selladurai

4
@selladurai: คุณไม่จำเป็นต้องนำเข้าใบรับรองสำหรับ paypal เว้นแต่ไฟล์ cacerts ของคุณจะเสียหายหรือถูกแก้ไขไฟล์ควรมีใบรับรองรูทที่เชื่อถือได้ทั้งหมดและใบรับรองของ paypal ควรสามารถตรวจสอบย้อนกลับไปยังไฟล์เหล่านั้นได้ คุณอาจลองรับสำเนาของ cacerts ที่คุณรู้ว่าดีและลองใช้แทน หากปัญหายังคงมีอยู่แสดงว่าคุณอาจไม่ได้เชื่อมต่อกับ paypal
Ryan Stewart

@RyanStewart ฉันต้องนำเข้าปลาแก้วหรือไม่? เนื่องจากฉันได้เพิ่มไฟล์. cer ลงใน cacert ในโฮมไดเร็กทอรี java ของฉัน แต่ glassfish ดูเหมือนจะไม่ได้ใช้งานดังนั้นฉันจึงยังคงได้รับข้อผิดพลาด
Ced

15

ตอนนี้ฉันแก้ไขปัญหานี้ด้วยวิธีนี้

import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.io.OutputStream; 

// Create a trust manager that does not validate certificate chains like the default 

TrustManager[] trustAllCerts = new TrustManager[]{
        new X509TrustManager() {

            public java.security.cert.X509Certificate[] getAcceptedIssuers()
            {
                return null;
            }
            public void checkClientTrusted(java.security.cert.X509Certificate[] certs, String authType)
            {
                //No need to implement.
            }
            public void checkServerTrusted(java.security.cert.X509Certificate[] certs, String authType)
            {
                //No need to implement.
            }
        }
};

// Install the all-trusting trust manager
try 
{
    SSLContext sc = SSLContext.getInstance("SSL");
    sc.init(null, trustAllCerts, new java.security.SecureRandom());
    HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
} 
catch (Exception e) 
{
    System.out.println(e);
}

แน่นอนว่าโซลูชันนี้ควรใช้เฉพาะในสถานการณ์ที่ไม่สามารถติดตั้งใบรับรองที่จำเป็นได้โดยใช้keytoolเช่นการทดสอบในพื้นที่กับใบรับรองชั่วคราว


53
ว้าวฉันลังเลที่จะเรียกสิ่งนั้นว่า "แก้ไข" โดยพื้นฐานแล้วคุณเพิ่งปิดการรักษาความปลอดภัย ใช่ข้อมูลจะยังคงถูกเข้ารหัส แต่คุณไม่สามารถรับประกันได้ว่าคุณกำลังคุยกับคนที่คุณคาดหวังว่าจะคุยด้วย วิธีแก้ไขที่ถูกต้องคือการรับคีย์สาธารณะของเซิร์ฟเวอร์เป้าหมายของคุณและอิมพอร์ตไปยัง Trust Store ของ JVM ที่ทำการเชื่อมต่อ
Ryan Stewart

1
ดูคำตอบของฉันสำหรับวิธีแก้ปัญหาที่เหมาะสม
Ryan Stewart

ตัวอย่างของอะไร? คำตอบของฉันควรมีทุกขั้นตอนที่คุณต้องการ
Ryan Stewart


3
รหัสที่แสดงที่นี่ดูมีประโยชน์หากคุณกำลังเขียนการทดสอบอย่างง่าย ๆ กับเซิร์ฟเวอร์ dev ฉันจะไม่พึ่งพามันสำหรับการผลิต
Jason D

12

เมื่อใดก็ตามที่เราพยายามเชื่อมต่อกับ URL

หากเซิร์ฟเวอร์ที่ไซต์อื่นกำลังทำงานบนโปรโตคอล https และได้รับคำสั่งว่าเราควรสื่อสารผ่านข้อมูลที่ให้ไว้ในใบรับรองเราจะมีตัวเลือกต่อไปนี้:

1) ขอใบรับรอง (ดาวน์โหลดใบรับรอง) นำเข้าใบรับรองนี้ใน Trustore การใช้ java trustore เริ่มต้นสามารถพบได้ใน \ Java \ jdk1.6.0_29 \ jre \ lib \ security \ cacerts ดังนั้นหากเราพยายามเชื่อมต่อกับการเชื่อมต่อ URL อีกครั้งจะได้รับการยอมรับ

2) ในกรณีธุรกิจปกติเราอาจเชื่อมต่อกับ URLS ภายในในองค์กรและเรารู้ว่า URL เหล่านี้ถูกต้อง ในกรณีเช่นนี้คุณเชื่อว่าเป็น URL ที่ถูกต้องในกรณีดังกล่าวข้างต้นสามารถใช้รหัสซึ่งจะไม่บังคับให้จัดเก็บใบรับรองเพื่อเชื่อมต่อกับ URL ใด URL หนึ่ง

สำหรับประเด็นที่ 2 เราต้องทำตามขั้นตอนด้านล่าง:

1) เขียนวิธีการด้านล่างซึ่งตั้งค่า HostnameVerifier สำหรับ HttpsURLConnection ซึ่งจะคืนค่าเป็นจริงสำหรับทุกกรณีซึ่งหมายความว่าเราไว้วางใจ trustStore

  // trusting all certificate 
 public void doTrustToCertificates() throws Exception {
        Security.addProvider(new com.sun.net.ssl.internal.ssl.Provider());
        TrustManager[] trustAllCerts = new TrustManager[]{
                new X509TrustManager() {
                    public X509Certificate[] getAcceptedIssuers() {
                        return null;
                    }

                    public void checkServerTrusted(X509Certificate[] certs, String authType) throws CertificateException {
                        return;
                    }

                    public void checkClientTrusted(X509Certificate[] certs, String authType) throws CertificateException {
                        return;
                    }
                }
        };

        SSLContext sc = SSLContext.getInstance("SSL");
        sc.init(null, trustAllCerts, new SecureRandom());
        HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
        HostnameVerifier hv = new HostnameVerifier() {
            public boolean verify(String urlHostName, SSLSession session) {
                if (!urlHostName.equalsIgnoreCase(session.getPeerHost())) {
                    System.out.println("Warning: URL host '" + urlHostName + "' is different to SSLSession host '" + session.getPeerHost() + "'.");
                }
                return true;
            }
        };
        HttpsURLConnection.setDefaultHostnameVerifier(hv);
    }

2) เขียนวิธีการด้านล่างซึ่งเรียกใช้ doTrustToCertificates ก่อนที่จะพยายามเชื่อมต่อกับ URL

    // connecting to URL
    public void connectToUrl(){
     doTrustToCertificates();//  
     URL url = new URL("https://www.example.com");
     HttpURLConnection conn = (HttpURLConnection)url.openConnection(); 
     System.out.println("ResponseCode ="+conn.getResponseCode());
   }

การโทรนี้จะส่งคืนรหัสตอบกลับ = 200 หมายความว่าการเชื่อมต่อสำเร็จ

สำหรับรายละเอียดเพิ่มเติมและตัวอย่างตัวอย่างคุณสามารถดูURL


1
Google Play Store จะปฏิเสธสิ่งนี้ พวกเขาจะเห็นว่าคุณไม่สนใจ X509Certificate ระหว่างการสแกน APK
Oliver Dixon

0

SSLHandshakeException สามารถแก้ไขได้ 2 วิธี

  1. รวม SSL

    • รับ SSL (โดยถามผู้ดูแลระบบต้นทางสามารถดาวน์โหลดได้โดยใช้คำสั่ง openssl หรือเบราว์เซอร์ใด ๆ จะดาวน์โหลดใบรับรอง)

    • เพิ่มใบรับรองลงใน truststore (cacerts) ซึ่งอยู่ที่ JRE / lib / security

    • ระบุตำแหน่ง truststore ในอาร์กิวเมนต์ vm เป็น "-Djavax.net.ssl.trustStore ="

  2. ละเว้น SSL

    สำหรับ # 2 นี้โปรดไปที่คำตอบอื่นของฉันในเว็บไซต์ stackoverflow อื่น: วิธีการยืนยันSSL ละเว้นข้อผิดพลาดใบรับรอง SSL กับ Java


-1

ฉันเชื่อว่าคุณกำลังพยายามเชื่อมต่อกับบางสิ่งโดยใช้ SSL แต่มีบางอย่างกำลังให้ใบรับรองซึ่งไม่ได้รับการตรวจสอบโดยหน่วยงานรับรองมาตรฐานเช่น verisign .. โดยพื้นฐานแล้วการเชื่อมต่อที่ปลอดภัยโดยค่าเริ่มต้นจะสามารถสร้างได้ก็ต่อเมื่อผู้ที่พยายามเชื่อมต่อรู้ คีย์ของคู่สัญญาหรือคำสั่งอื่น ๆ เช่น verisign สามารถเข้ามาได้และบอกว่าคีย์สาธารณะที่ให้มานั้นถูกต้องจริงๆ ..

ALL OS ไว้วางใจหน่วยงานออกใบรับรองจำนวนหนึ่งและผู้ออกใบรับรองขนาดเล็กจะต้องได้รับการรับรองจากผู้รับรองรายใหญ่รายหนึ่งที่สร้างเครือข่ายผู้รับรองหากคุณได้รับสิ่งที่ฉันหมายถึง ...

กลับมาที่ประเด็น .. ฉันมีปัญหาเหมือนกันเมื่อเขียนโปรแกรม java applet และเซิร์ฟเวอร์ java (หวังว่าสักวันฉันจะเขียนบล็อกโพสต์ที่สมบูรณ์เกี่ยวกับวิธีที่ฉันได้รับความปลอดภัยทั้งหมดในการทำงาน :))

โดยพื้นฐานแล้วสิ่งที่ฉันต้องทำคือการแยกคีย์สาธารณะจากเซิร์ฟเวอร์และเก็บไว้ในที่เก็บคีย์ภายในแอพเพล็ตของฉันและเมื่อฉันเชื่อมต่อกับเซิร์ฟเวอร์ฉันใช้ที่เก็บคีย์นี้เพื่อสร้างโรงงานที่เชื่อถือและโรงงานที่เชื่อถือเพื่อสร้าง ssl การเชื่อมต่อ มีขั้นตอนการเปลี่ยนแปลงเช่นการเพิ่มคีย์ให้กับโฮสต์ที่เชื่อถือได้ของ JVM และการแก้ไขที่เก็บความไว้วางใจเริ่มต้นเมื่อเริ่มต้นระบบ ..

ฉันทำสิ่งนี้ประมาณสองเดือนย้อนหลังและไม่มีซอร์สโค้ดกับฉันในตอนนี้ .. ใช้ google และคุณควรจะสามารถแก้ปัญหานี้ได้ หากคุณไม่สามารถส่งข้อความกลับมาได้และฉันสามารถให้ซอร์สโค้ดที่เกี่ยวข้องสำหรับโปรเจ็กต์แก่คุณได้ .. อย่าทราบว่าวิธีนี้ช่วยแก้ปัญหาของคุณได้หรือไม่เนื่องจากคุณไม่ได้ให้รหัสซึ่งทำให้เกิดข้อยกเว้นเหล่านี้ นอกจากนี้ฉันกำลังทำงานกับแอพเพล็ตที่คิดว่าฉันมองไม่เห็นว่าทำไมมันถึงไม่ทำงานบน Serverlets ...

ป.ล. ฉันไม่สามารถรับซอร์สโค้ดก่อนวันหยุดสุดสัปดาห์เนื่องจาก SSH ภายนอกถูกปิดใช้งานในสำนักงานของฉัน :(


1
ป.ล. ฉันพบรหัส java นี้ทางออนไลน์เพื่อแยกและจัดเก็บคีย์สาธารณะของเซิร์ฟเวอร์ของฉันดังนั้นให้ googling ต่อไปหรือรอจนถึงวันอาทิตย์
Osama Javed

-8

ตอนนี้ฉันแก้ไขปัญหานี้ด้วยวิธีนี้

import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.io.OutputStream;
// Create a trust manager that does not validate certificate chains like the 
default TrustManager[] trustAllCerts = new TrustManager[] {
    new X509TrustManager() {
        public java.security.cert.X509Certificate[] getAcceptedIssuers() {
            return null;
        }
        public void checkClientTrusted(java.security.cert.X509Certificate[] certs, String authType) {
            //No need to implement. 
        }
        public void checkServerTrusted(java.security.cert.X509Certificate[] certs, String authType) {
            //No need to implement. 
        }
    }
};
// Install the all-trusting trust manager
try {
    SSLContext sc = SSLContext.getInstance("SSL");
    sc.init(null, trustAllCerts, new java.security.SecureRandom());
    HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
} catch (Exception e) {
    System.out.println(e);
}

4
'ไม่จำเป็นต้องใช้งาน' นั้นไม่ถูกต้องและสมบูรณ์อย่างที่สุด คุณเพิ่งทำการเชื่อมต่อ SSL ของคุณไม่ปลอดภัย อย่าทำอย่างนี้
Marquis of Lorne
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.