Bagaimana cara mengatasi Error javax.net.ssl.SSLHandshakeException?

101

Saya terhubung dengan VPN untuk menyiapkan API inventaris untuk mendapatkan daftar produk dan berfungsi dengan baik. Setelah saya mendapatkan hasil dari layanan web dan saya mengikat ke UI. Dan juga saya mengintegrasikan PayPal dengan aplikasi saya untuk melakukan pembayaran Ekspres ketika saya melakukan panggilan untuk pembayaran, saya menghadapi kesalahan ini. Saya menggunakan servlet untuk proses back-end. Adakah yang bisa mengatakan bagaimana memperbaiki masalah ini?

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
Selladurai
sumber
1
apa yang ingin saya ketahui, target yang mana sebenarnya dalam kasus itu ... Anda mendapatkan pengecualian, tetapi Anda tidak mendapatkan informasi tentang target, yang mungkin berbeda dari apa yang Anda harapkan .. Saya punya kasus seperti itu, saya yakin tautan saya memiliki sertifikat, dan saya masih mendapatkan pengecualian ini
ante.sabo

Jawaban:

151

Pertama, Anda perlu mendapatkan sertifikat publik dari server yang Anda coba sambungkan. Itu dapat dilakukan dengan berbagai cara, seperti menghubungi admin server dan memintanya, menggunakan OpenSSL untuk mengunduhnya , atau, karena ini tampak seperti server HTTP, menghubungkannya dengan browser apa pun, melihat info keamanan halaman , dan menyimpan salinan sertifikat. (Google seharusnya dapat memberi tahu Anda apa yang harus dilakukan untuk browser spesifik Anda.)

Sekarang setelah Anda memiliki sertifikat yang disimpan dalam sebuah file, Anda perlu menambahkannya ke penyimpanan kepercayaan JVM Anda. Di $JAVA_HOME/jre/lib/security/untuk JRE atau $JAVA_HOME/lib/securityuntuk JDK, ada file bernama cacerts, yang disertakan dengan Java dan berisi sertifikat publik dari Otoritas Pemberi Sertifikat yang terkenal. Untuk mengimpor sertifikat baru, jalankan keytool sebagai pengguna yang memiliki izin untuk menulis ke cacerts:

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

Kemungkinan besar akan meminta Anda untuk memasukkan kata sandi. Kata sandi default yang dikirimkan dengan Java adalah changeit. Hampir tidak ada yang mengubahnya. Setelah Anda menyelesaikan langkah-langkah yang relatif sederhana ini, Anda akan berkomunikasi dengan aman dan dengan jaminan bahwa Anda berbicara dengan server yang tepat dan hanya server yang tepat (selama mereka tidak kehilangan kunci privatnya).

Ryan Stewart
sumber
12
Saya membutuhkan sedikit lebih banyak detail daripada "tidak berfungsi". Coba perbarui pertanyaan Anda dengan apa yang telah Anda coba dan beberapa keluaran kesalahan. Sayangnya, waktu tidur saya sudah lewat, jadi mungkin orang lain yang bisa menjawab pertanyaan Anda. Ini juga merupakan situasi yang sangat umum. Anda dapat menemukan banyak informasi di dalamnya secara online, termasuk dokumen keytool .
Ryan Stewart
Pada dasarnya saya perlu menyertakan sertifikat PayPal. Saya melakukan langkah Anda dan sertifikat juga ditambahkan di lokasi yang sesuai. lalu saya menjalankan aplikasi itu mengatakan kesalahan yang sama?
selladurai
Sebenarnya ini berfungsi dengan baik, tidak ada masalah browser dan saya menghubungkan VPN untuk mendapatkan daftar inventaris pada saat itu saya melakukan pembayaran dengan PayPal, dikatakan kesalahan SSL tetapi saya menggunakan data offline atau data kode panas berarti berfungsi.
selladurai
4
@selladurai: Anda tidak perlu mengimpor sertifikat untuk paypal. Kecuali jika file cacerts Anda telah rusak atau dimodifikasi, itu harus berisi semua sertifikat dasar tepercaya, dan sertifikat paypal harus dapat dilacak kembali ke salah satunya. Anda dapat mencoba mendapatkan salinan cacert yang Anda tahu bagus dan mencoba yang itu. Jika masalah berlanjut, Anda mungkin tidak benar-benar terhubung ke paypal.
Ryan Stewart
@RyanStewart apakah saya perlu mengimpornya ke glassfish entah bagaimana? Karena saya menambahkan file .cer ke cacert saya di direktori home java saya tetapi glassfish sepertinya tidak menggunakannya jadi saya masih mendapatkan kesalahan.
Menyerahkan
15

Sekarang saya memecahkan masalah ini dengan cara ini,

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

Tentu saja solusi ini hanya boleh digunakan dalam skenario, di mana tidak mungkin untuk menginstal sertifikat yang diperlukan menggunakan keytoolmisalnya pengujian lokal dengan sertifikat sementara.

Selladurai
sumber
52
Wow, saya akan ragu untuk menyebutnya sebagai "perbaikan". Pada dasarnya Anda baru saja mematikan keamanan. Ya, data akan tetap dienkripsi, tetapi Anda tidak memiliki jaminan bahwa Anda sedang berbicara dengan siapa yang Anda harapkan. Solusi yang tepat adalah mendapatkan kunci publik dari server target Anda dan mengimpornya ke penyimpanan kepercayaan JVM yang membuat sambungan.
Ryan Stewart
1
lihat jawaban saya untuk solusi yang tepat
Ryan Stewart
Contoh apa? Jawaban saya harus memiliki semua langkah yang Anda butuhkan.
Ryan Stewart
3
Kode yang ditampilkan di sini terlihat membantu jika Anda menulis pengujian yang sangat sederhana terhadap server dev, saya tidak akan mengandalkannya untuk Produksi.
Jason D
12

Setiap kali kami mencoba menyambung ke URL,

jika server di situs lain berjalan pada protokol https dan mengamanatkan bahwa kami harus berkomunikasi melalui informasi yang diberikan dalam sertifikat, maka kami memiliki opsi berikut:

1) minta sertifikat (unduh sertifikat), impor sertifikat ini di trustore. Penggunaan java trustore default dapat ditemukan di \ Java \ jdk1.6.0_29 \ jre \ lib \ security \ cacerts, maka jika kita mencoba kembali untuk menyambung ke sambungan URL akan diterima.

2) Dalam kasus bisnis normal, kami mungkin terhubung ke URL internal dalam organisasi dan kami tahu bahwa itu benar. Dalam kasus seperti itu, Anda percaya bahwa itu adalah URL yang benar. Dalam kasus seperti di atas, kode dapat digunakan yang tidak akan mengamanatkan untuk menyimpan sertifikat untuk terhubung ke URL tertentu.

untuk poin no 2 kita harus mengikuti langkah-langkah di bawah ini:

1) tulis di bawah metode yang menetapkan HostnameVerifier untuk HttpsURLConnection yang mengembalikan nilai true untuk semua kasus yang berarti kita mempercayai 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) tulis metode di bawah ini, yang memanggil doTrustToCertificates sebelum mencoba menyambung ke 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());
   }

Panggilan ini akan mengembalikan kode respons = 200 berarti koneksi berhasil.

Untuk detail lebih lanjut dan contoh contoh Anda dapat merujuk ke URL .

Shirishkumar Bari
sumber
1
Google Play Store akan menolak ini. Mereka akan melihat bahwa Anda mengabaikan X509Certificate selama pemindaian APK.
Oliver Dixon
0

SSLHandshakeException dapat diselesaikan dengan 2 cara.

  1. Memasukkan SSL

    • Dapatkan SSL (dengan meminta administrator sistem sumber, juga dapat diunduh dengan perintah openssl, atau browser apa pun mengunduh sertifikat)

    • Tambahkan sertifikat ke truststore (cacerts) yang terletak di JRE / lib / security

    • berikan lokasi truststore dalam argumen vm sebagai "-Djavax.net.ssl.trustStore ="

  2. Mengabaikan SSL

    Untuk # 2 ini, silakan kunjungi jawaban saya yang lain di situs web stackoverflow lainnya: Cara menggunakan verifikasi SSL Abaikan Kesalahan Sertifikat SSL dengan Java

Amit Kaneria
sumber
-1

Saya yakin Anda mencoba menyambung ke sesuatu menggunakan SSL tetapi ada sesuatu yang memberikan sertifikat yang tidak diverifikasi oleh otoritas sertifikasi root seperti verisign .. Intinya secara default, sambungan aman hanya dapat dibuat jika orang yang mencoba menyambungkan tahu kunci counterparties atau beberapa verndor lain seperti verisign dapat turun tangan dan mengatakan bahwa kunci publik yang diberikan memang benar ..

SEMUA OS mempercayai segelintir otoritas sertifikasi dan penerbit sertifikat yang lebih kecil perlu disertifikasi oleh salah satu pemberi sertifikasi besar yang membuat rantai pemberi sertifikasi jika Anda mengerti maksud saya ...

Kembali ke intinya .. Saya memiliki masalah yang sama ketika memprogram applet java dan server java (Mudah-mudahan suatu hari nanti saya akan menulis posting blog lengkap tentang bagaimana saya mendapatkan semua keamanan untuk bekerja :))

Intinya apa yang harus saya lakukan adalah mengekstrak kunci publik dari server dan menyimpannya di keystore di dalam applet saya dan ketika saya terhubung ke server saya menggunakan penyimpanan kunci ini untuk membuat pabrik kepercayaan dan pabrik kepercayaan itu untuk membuat ssl koneksi. Ada juga prosedur alterante seperti menambahkan kunci ke host tepercaya JVM dan mengubah penyimpanan kepercayaan default saat memulai ..

Saya melakukan ini sekitar dua bulan yang lalu dan tidak memiliki kode sumber pada saya sekarang .. gunakan google dan Anda harus dapat memecahkan masalah ini. Jika Anda tidak dapat mengirim pesan kepada saya kembali dan saya dapat memberikan Anda kode sumber yang relevan untuk proyek tersebut .. Tidak tahu apakah ini menyelesaikan masalah Anda karena Anda belum memberikan kode yang menyebabkan pengecualian ini. Selanjutnya saya bekerja dengan applet pikir saya tidak bisa melihat mengapa itu tidak berfungsi di Serverlets ...

PS Saya tidak bisa mendapatkan kode sumber sebelum akhir pekan karena SSH eksternal dinonaktifkan di kantor saya :(

Osama Javed
sumber
1
PS Saya menemukan kode java ini secara online untuk mengekstrak dan menyimpan kunci publik server saya jadi terus googling atau tunggu sampai minggu
Osama Javed
-8

Sekarang saya memecahkan masalah ini dengan cara ini,

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);
}
Evelyn Condes
sumber
4
'Tidak perlu menerapkan' sepenuhnya dan sama sekali tidak benar. Anda baru saja membuat koneksi SSL Anda tidak aman. Jangan lakukan ini.
Marquis dari Lorne