Làm thế nào để giải quyết lỗi javax.net.ssl.SSLHandshakeException?


101

Tôi đã kết nối với VPN để thiết lập API kiểm kê để lấy danh sách sản phẩm và nó hoạt động tốt. Khi tôi nhận được kết quả từ dịch vụ web và tôi liên kết với giao diện người dùng. Và tôi cũng đã tích hợp PayPal với ứng dụng của mình để thực hiện thanh toán Nhanh khi thực hiện cuộc gọi thanh toán, tôi đang gặp phải lỗi này. Tôi sử dụng servlet cho quá trình back-end. Bất kỳ ai có thể cho biết làm thế nào để khắc phục sự cố này?

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
những gì tôi muốn biết, mục tiêu chính xác là trong trường hợp nào ... Bạn có một ngoại lệ, nhưng bạn không nhận được thông tin về mục tiêu, có thể khác với những gì bạn mong đợi .. Tôi có trường hợp như vậy, tôi chắc chắn rằng liên kết của tôi có giấy chứng nhận, và tôi vẫn đang nhận được ngoại lệ này
ante.sabo

Câu trả lời:


151

Trước tiên, bạn cần lấy chứng chỉ công khai từ máy chủ mà bạn đang cố kết nối. Điều đó có thể được thực hiện theo nhiều cách, chẳng hạn như liên hệ với quản trị viên máy chủ và yêu cầu nó, sử dụng OpenSSL để tải xuống hoặc, vì đây có vẻ là một máy chủ HTTP, kết nối với nó bằng bất kỳ trình duyệt nào, xem thông tin bảo mật của trang và lưu một bản sao của chứng chỉ. (Google sẽ có thể cho bạn biết chính xác những gì cần làm cho trình duyệt cụ thể của bạn.)

Bây giờ bạn đã lưu chứng chỉ trong một tệp, bạn cần thêm chứng chỉ đó vào kho lưu trữ tin cậy của JVM. Tại $JAVA_HOME/jre/lib/security/đối với JRE hoặc $JAVA_HOME/lib/securityđối với JDK, có một tệp có tên cacerts, đi kèm với Java và chứa các chứng chỉ công khai của Cơ quan cấp chứng chỉ nổi tiếng. Để nhập chứng chỉ mới, hãy chạy keytool với tư cách người dùng có quyền ghi vào cacerts:

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

Rất có thể nó sẽ yêu cầu bạn nhập mật khẩu. Mật khẩu mặc định được vận chuyển với Java là changeit. Hầu như không ai thay đổi nó. Sau khi hoàn thành các bước tương đối đơn giản này, bạn sẽ giao tiếp an toàn và đảm bảo rằng bạn đang nói chuyện với đúng máy chủ và chỉ máy chủ phù hợp (miễn là họ không bị mất khóa riêng).


12
Tôi sẽ cần một chút chi tiết hơn là "không hoạt động". Hãy thử cập nhật câu hỏi của bạn với những gì bạn đã thử và một số lỗi xuất ra. Thật không may, đã quá giờ đi ngủ của tôi, vì vậy có thể ai đó sẽ có thể trả lời câu hỏi của bạn. Đây cũng là một tình huống rất phổ biến. Bạn có thể tìm thấy rất nhiều thông tin về nó trực tuyến, bao gồm cả các tài liệu về keytool .
Ryan Stewart,

Về cơ bản, tôi cần bao gồm chứng chỉ PayPal. Tôi đã thực hiện các bước của bạn và chứng chỉ cũng được thêm vào vị trí thích hợp. sau đó tôi chạy ứng dụng nó báo lỗi giống nhau?
selladurai

Trên thực tế, nó hoạt động tốt không có vấn đề trình duyệt và tôi đã kết nối VPN để lấy danh sách hàng tồn kho tại thời điểm đó tôi thanh toán bằng PayPal, nó báo lỗi SSL nhưng tôi sử dụng dữ liệu ngoại tuyến hoặc dữ liệu được mã hóa nóng có nghĩa là nó hoạt động.
selladurai,

4
@selladurai: Bạn không cần phải nhập chứng chỉ cho paypal. Trừ khi tệp cacerts của bạn đã bị hỏng hoặc bị sửa đổi, nó phải chứa tất cả các chứng chỉ gốc đáng tin cậy và chứng chỉ của paypal sẽ có thể được truy xuất trở lại một trong những chứng chỉ đó. Bạn có thể thử lấy một bản sao của cacerts mà bạn biết là tốt và thay vào đó hãy thử bản sao. Nếu sự cố vẫn tiếp diễn, thì bạn có thể không thực sự kết nối với paypal.
Ryan Stewart,

@RyanStewart tôi có cần nhập nó vào glassfish bằng cách nào đó không? Bởi vì tôi đã thêm tệp .cer vào cacert của mình trong thư mục chính java của mình nhưng glassfish dường như không sử dụng nó nên tôi vẫn gặp lỗi.
Ced

15

Bây giờ tôi đã giải quyết vấn đề này theo cách này,

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

Tất nhiên, giải pháp này chỉ nên được sử dụng trong các trường hợp không thể cài đặt các chứng chỉ bắt buộc bằng cách sử dụng keytoolví dụ: thử nghiệm cục bộ với các chứng chỉ tạm thời.


52
Chà, tôi sẽ ngần ngại gọi đó là một "bản sửa lỗi". Về cơ bản, bạn vừa tắt bảo mật. Có, dữ liệu sẽ vẫn được mã hóa, nhưng bạn không có gì đảm bảo rằng bạn đang nói chuyện với người mà bạn mong đợi được nói chuyện. Giải pháp chính xác là lấy khóa công khai của máy chủ đích của bạn và nhập nó vào kho lưu trữ tin cậy của JVM tạo kết nối.
Ryan Stewart,

1
thấy tôi trả lời cho một giải pháp thích hợp
Ryan Stewart

Ví dụ về cái gì? Câu trả lời của tôi nên có tất cả các bước bạn cần.
Ryan Stewart,


3
Mã hiển thị ở đây có vẻ hữu ích nếu bạn đang viết một bài kiểm tra rất đơn giản với máy chủ nhà phát triển, mặc dù vậy, tôi sẽ không dựa vào nó cho Sản xuất.
Jason D

12

Bất cứ khi nào chúng tôi cố gắng kết nối với URL,

nếu máy chủ tại trang web khác đang chạy trên giao thức https và bắt buộc chúng tôi phải giao tiếp thông qua thông tin được cung cấp trong chứng chỉ thì chúng tôi có tùy chọn sau:

1) yêu cầu chứng chỉ (tải xuống chứng chỉ), nhập chứng chỉ này vào cửa hàng tin cậy. Bạn có thể tìm thấy cách sử dụng java đáng tin cậy mặc định trong \ Java \ jdk1.6.0_29 \ jre \ lib \ security \ cacerts, sau đó nếu chúng tôi thử kết nối lại với kết nối URL sẽ được chấp nhận.

2) Trong các trường hợp kinh doanh thông thường, chúng tôi có thể đang kết nối với các URL nội bộ trong các tổ chức và chúng tôi biết rằng chúng đúng. Trong những trường hợp như vậy, bạn tin tưởng rằng đó là URL chính xác, Trong những trường hợp như vậy ở trên, mã có thể được sử dụng mà sẽ không bắt buộc phải lưu trữ chứng chỉ để kết nối với URL cụ thể.

đối với điểm số 2, chúng ta phải làm theo các bước sau:

1) viết bên dưới phương thức đặt HostnameVerifier cho HttpsURLConnection trả về true cho mọi trường hợp có nghĩa là chúng tôi đang tin tưởng 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) viết phương thức dưới đây, gọi doTrustToCertificates trước khi cố gắng kết nối với 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());
   }

Cuộc gọi này sẽ trả về mã phản hồi = 200 nghĩa là kết nối thành công.

Để biết thêm chi tiết và ví dụ mẫu, bạn có thể tham khảo URL .


1
Cửa hàng Google Play sẽ từ chối điều này. Họ sẽ thấy rằng bạn đang bỏ qua X509Certificate trong quá trình quét APK.
Oliver Dixon

0

Tôi tin rằng bạn đang cố gắng kết nối với một thứ gì đó sử dụng SSL nhưng một thứ gì đó đang cung cấp chứng chỉ không được xác minh bởi cơ quan cấp chứng chỉ gốc như verisign .. Về bản chất, các kết nối an toàn mặc định chỉ có thể được thiết lập nếu người đang cố gắng kết nối biết các khóa đối tác hoặc một số cơ quan khác như verisign có thể bước vào và nói rằng khóa công khai đang được cung cấp thực sự là đúng ..

ALL OS's tin tưởng một số ít các tổ chức chứng nhận và các tổ chức cấp chứng chỉ nhỏ hơn cần được chứng nhận bởi một trong những nhà chứng nhận lớn tạo nên một chuỗi các nhà chứng nhận nếu bạn hiểu ý tôi ...

Dù sao thì quay lại vấn đề .. Tôi đã gặp một vấn đề tương tự khi lập trình applet java và máy chủ java (Hy vọng một ngày nào đó tôi sẽ viết một bài đăng blog hoàn chỉnh về cách tôi có tất cả bảo mật để hoạt động :))

Về bản chất những gì tôi phải làm là trích xuất khóa công khai từ máy chủ và lưu trữ nó trong kho khóa bên trong applet của mình và khi tôi kết nối với máy chủ, tôi đã sử dụng kho khóa này để tạo nhà máy tin cậy và nhà máy tin cậy đó để tạo ssl kết nối. Có các thủ tục thay đổi cũng như thêm khóa vào máy chủ đáng tin cậy của JVM và sửa đổi kho lưu trữ tin cậy mặc định khi khởi động ..

Tôi đã làm điều này khoảng hai tháng trước và không có mã nguồn trên tôi ngay bây giờ .. sử dụng google và bạn sẽ có thể giải quyết vấn đề này. Nếu bạn không thể nhắn lại cho tôi và tôi có thể cung cấp cho bạn mã nguồn phù hợp cho dự án .. Không biết điều này có giải quyết được vấn đề của bạn không vì bạn đã không cung cấp mã gây ra những ngoại lệ này. Hơn nữa, tôi đang làm việc với các applet wiht nghĩ rằng tôi không thể hiểu tại sao nó không hoạt động trên Serverlets ...

Tái bút Tôi không thể lấy mã nguồn trước cuối tuần vì SSH bên ngoài bị vô hiệu hóa trong văn phòng của tôi :(


1
PS Tôi tìm thấy mã java này trực tuyến để giải nén và lưu trữ các khóa công khai của máy chủ của tôi để giữ googling hoặc chờ cho đến khi sundayish
Osama Javed

0

SSLHandshakeException có thể được giải quyết theo 2 cách.

  1. Kết hợp SSL

    • Nhận SSL (bằng cách hỏi quản trị viên hệ thống nguồn, cũng có thể được tải xuống bằng lệnh openssl hoặc bất kỳ trình duyệt nào tải xuống chứng chỉ)

    • Thêm chứng chỉ vào kho tin cậy (cacerts) có tại JRE / lib / security

    • cung cấp vị trí cửa hàng tin cậy trong đối số vm là "-Djavax.net.ssl.trustStore ="

  2. Bỏ qua SSL

    Đối với số 2 này, vui lòng truy cập câu trả lời khác của tôi trên một trang web stackoverflow khác: Cách nhập xác minh SSL Bỏ qua lỗi chứng chỉ SSL với Java


-8

Bây giờ tôi đã giải quyết vấn đề này theo cách này,

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
'Không cần phải thực hiện' là hoàn toàn và hoàn toàn không chính xác. Bạn vừa đặt kết nối SSL của mình không an toàn. Đừng làm điều này.
Marquis of Lorne,
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.