سؤال

يبدو وكأنه سؤال قياسي ، لكنني لم أتمكن من العثور على اتجاهات واضحة في أي مكان.

لدي رمز Java يحاول الاتصال بخادم بشهادة موقعة ذاتيًا (أو منتهية الصلاحية). تقارير الكود عن الخطأ التالي:

[HttpMethodDirector] I/O exception (javax.net.ssl.SSLHandshakeException) caught 
when processing request: sun.security.validator.ValidatorException: PKIX path 
building failed: sun.security.provider.certpath.SunCertPathBuilderException: 
unable to find valid certification path to requested target

كما أفهمها ، يجب أن أستخدمها KeyTool وأخبر جافا أنه لا بأس في السماح لهذا الاتصال.

تفترض جميع الإرشادات اللازمة لإصلاح هذه المشكلة أنني أتقن تمامًا مع KeyTool ، مثل

إنشاء مفتاح خاص للخادم واستيراده إلى keystore

هل هناك أي شخص يمكنه نشر تعليمات مفصلة؟

أنا أقوم بتشغيل Unix ، لذا سيكون النصي Bash هو الأفضل.

لست متأكدًا مما إذا كان الأمر مهمًا ، ولكن تم تنفيذ التعليمات البرمجية في JBOSS.

هل كانت مفيدة؟

المحلول

لديك خياران هنا بشكل أساسي: أضف الشهادة الموقعة ذاتيًا إلى JVM TrustStore أو تكوين عميلك إلى

الخيار 1

قم بتصدير الشهادة من متصفحك واستيرادها في JVM TrustStore (لإنشاء سلسلة من الثقة):

<JAVA_HOME>\bin\keytool -import -v -trustcacerts
-alias server-alias -file server.cer
-keystore cacerts.jks -keypass changeit
-storepass changeit 

الخيار 2

تعطيل التحقق من صحة الشهادة:

// Create a trust manager that does not validate certificate chains
TrustManager[] trustAllCerts = new TrustManager[] { 
    new X509TrustManager() {     
        public java.security.cert.X509Certificate[] getAcceptedIssuers() { 
            return new X509Certificate[0];
        } 
        public void checkClientTrusted( 
            java.security.cert.X509Certificate[] certs, String authType) {
            } 
        public void checkServerTrusted( 
            java.security.cert.X509Certificate[] certs, String authType) {
        }
    } 
}; 

// 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 (GeneralSecurityException e) {
} 
// Now you can access an https URL without having the certificate in the truststore
try { 
    URL url = new URL("https://hostname/index.html"); 
} catch (MalformedURLException e) {
} 

لاحظ أن لا أوصي بالخيار رقم 2 على الإطلاق. تعطيل مدير الثقة يهزم بعض أجزاء SSL ويجعلك عرضة للإنسان في الهجمات الوسطى. تفضل الخيار رقم 1 أو ، حتى أفضل ، أن يستخدم الخادم شهادة "حقيقية" موقعة بواسطة CA معروفة جيدًا.

نصائح أخرى

لقد طاردت هذه المشكلة لمزود الشهادة الذي ليس جزءًا من المضيفين الموثوق بهم JVM الافتراضي اعتبارًا من JDK 8u74. المزود هو www.identrust.com, ، لكن هذا لم يكن المجال الذي كنت أحاول الاتصال به. حصل هذا المجال على شهادة من هذا المزود. نرى هل ستثقة تغطية الجذر عبر القائمة الافتراضية في JDK/JRE؟ - اقرأ إدخالات زوجين. انظر أيضا التي تدعم المستعرضات وأنظمة التشغيل دعونا نشفير.

لذلك ، من أجل الاتصال بالمجال الذي كنت مهتمًا به ، والذي تم إصدار شهادة من identrust.com فعلت الخطوات التالية. في الأساس ، اضطررت إلى الحصول على Identrust.com (DST Root CA X3) شهادة الوثوق بها من قبل JVM. تمكنت من القيام بذلك باستخدام Apache httpcomponents 4.5 مثل SO:

1: الحصول على الشهادة من IndetTrust في تعليمات تنزيل سلسلة الشهادة. اضغط على DST ROOT CA X3 حلقة الوصل.

2: احفظ السلسلة إلى ملف يسمى "DST ROUT CA X3.PEM". تأكد من إضافة الأسطر "----- شهادة البدء -----" و "----- نهاية الشهادة -----" في الملف في البداية والنهاية.

3: إنشاء ملف java keystore ، cacerts.jks مع الأمر التالي:

keytool -import -v -trustcacerts -alias IdenTrust -keypass yourpassword -file dst_root_ca_x3.pem -keystore cacerts.jks -storepass yourpassword

4: انسخ Cacerts.jks keystore في دليل الموارد لتطبيق Java/(Maven).

5: استخدم الكود التالي لتحميل هذا الملف وإرفاقه بـ Apache 4.5 httpclient. سيؤدي هذا إلى حل المشكلة لجميع المجالات التي تحتوي على شهادات صادرة من indetrust.com يتضمن Util Oracle الشهادة في keystore الافتراضي JRE.

SSLContext sslcontext = SSLContexts.custom()
        .loadTrustMaterial(new File(CalRestClient.class.getResource("/cacerts.jks").getFile()), "yourpasword".toCharArray(),
                new TrustSelfSignedStrategy())
        .build();
// Allow TLSv1 protocol only
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
        sslcontext,
        new String[] { "TLSv1" },
        null,
        SSLConnectionSocketFactory.getDefaultHostnameVerifier());
CloseableHttpClient httpclient = HttpClients.custom()
        .setSSLSocketFactory(sslsf)
        .build();

عندما يبني المشروع ، سيتم نسخ cacerts.jks في classpath وتحميلها من هناك. لم أكن ، في هذه المرحلة الزمنية ، الاختبار ضد مواقع SSL الأخرى ، ولكن إذا كانت الكود أعلاه "سلاسل" في هذه الشهادة ، فسيعملون أيضًا ، لكن مرة أخرى ، لا أعرف.

المرجعي: سياق SSL المخصص و كيف أقبل شهادة موقعة ذاتيا مع جافا httpsurlconnection؟

يدعم Apache HttpClient 4.5 قبول الشهادات الموقعة ذاتيا:

SSLContext sslContext = SSLContexts.custom()
    .loadTrustMaterial(new TrustSelfSignedStrategy())
    .build();
SSLConnectionSocketFactory socketFactory =
    new SSLConnectionSocketFactory(sslContext);
Registry<ConnectionSocketFactory> reg =
    RegistryBuilder.<ConnectionSocketFactory>create()
    .register("https", socketFactory)
    .build();
HttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(reg);        
CloseableHttpClient httpClient = HttpClients.custom()
    .setConnectionManager(cm)
    .build();
HttpGet httpGet = new HttpGet(url);
CloseableHttpResponse sslResponse = httpClient.execute(httpGet);

هذا يبني مصنع SSL Socket الذي سيستخدم TrustSelfSignedStrategy, ، يقوم بتسجيله باستخدام مدير اتصال مخصص ، ثم يقوم HTTP باستخدام مدير الاتصال هذا.

وأنا أتفق مع أولئك الذين يهتفون "لا تفعل هذا في الإنتاج" ، ولكن هناك حالات استخدام لقبول الشهادات الموقعة ذاتيا خارج الإنتاج ؛ نستخدمها في اختبارات التكامل الآلية ، بحيث نستخدم SSL (مثل في الإنتاج) حتى عند عدم تشغيل أجهزة الإنتاج.

بدلاً من تعيين مصنع المقبس الافتراضي (وهو IMO شيء سيء) - سوف يؤثر YHIS على الاتصال الحالي بدلاً من كل اتصال SSL الذي تحاول فتحه:

URLConnection connection = url.openConnection();
    // JMD - this is a better way to do it that doesn't override the default SSL factory.
    if (connection instanceof HttpsURLConnection)
    {
        HttpsURLConnection conHttps = (HttpsURLConnection) connection;
        // Set up a Trust all manager
        TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager()
        {

            public java.security.cert.X509Certificate[] getAcceptedIssuers()
            {
                return null;
            }

            public void checkClientTrusted(
                java.security.cert.X509Certificate[] certs, String authType)
            {
            }

            public void checkServerTrusted(
                java.security.cert.X509Certificate[] certs, String authType)
            {
            }
        } };

        // Get a new SSL context
        SSLContext sc = SSLContext.getInstance("TLSv1.2");
        sc.init(null, trustAllCerts, new java.security.SecureRandom());
        // Set our connection to use this SSL context, with the "Trust all" manager in place.
        conHttps.setSSLSocketFactory(sc.getSocketFactory());
        // Also force it to trust all hosts
        HostnameVerifier allHostsValid = new HostnameVerifier() {
            public boolean verify(String hostname, SSLSession session) {
                return true;
            }
        };
        // and set the hostname verifier.
        conHttps.setHostnameVerifier(allHostsValid);
    }
InputStream stream = connection.getInputStream();

ثق بجميع شهادات SSL:- يمكنك تجاوز SSL إذا كنت ترغب في اختبار خادم الاختبار. ولكن لا تستخدم هذا الرمز للإنتاج.

public static class NukeSSLCerts {
protected static final String TAG = "NukeSSLCerts";

public static void nuke() {
    try {
        TrustManager[] trustAllCerts = new TrustManager[] { 
            new X509TrustManager() {
                public X509Certificate[] getAcceptedIssuers() {
                    X509Certificate[] myTrustedAnchors = new X509Certificate[0];  
                    return myTrustedAnchors;
                }

                @Override
                public void checkClientTrusted(X509Certificate[] certs, String authType) {}

                @Override
                public void checkServerTrusted(X509Certificate[] certs, String authType) {}
            }
        };

        SSLContext sc = SSLContext.getInstance("SSL");
        sc.init(null, trustAllCerts, new SecureRandom());
        HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
        HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() {
            @Override
            public boolean verify(String arg0, SSLSession arg1) {
                return true;
            }
        });
    } catch (Exception e) { 
    }
}

}

يرجى استدعاء هذه الوظيفة في وظيفة onCreate () في النشاط أو في فئة التطبيق الخاصة بك.

NukeSSLCerts.nuke();

هذا يمكن استخدامه في Volley في Android.

إذا كانوا يستخدمون شهادة موقعة ذاتيا ، فهذا الأمر متروك لهم لاتخاذ الخطوات المطلوبة لجعل الخادم قابلاً للاستخدام. على وجه التحديد ، هذا يعني تقديم شهادتهم لك في وضع عدم الاتصال بطريقة جديرة بالثقة. لذا اجعلهم يفعلون ذلك. يمكنك بعد ذلك استيراد ذلك إلى ائتمانك باستخدام keytool كما هو موضح في دليل مرجع JSSE. لا تفكر حتى في عدم الأمان المنشور هنا.

تعديل لصالح سبعة عشر (!) Downvoters ، والعديد من المعلقين أدناه ، الذين من الواضح أنهم لم يقرأوا بالفعل ما كتبته هنا ، هذا هو ليس جيريمياد ضد الشهادات الموقعة ذاتيا. لا حرج في الشهادات الموقعة ذاتيا عند تنفيذها بشكل صحيح. ولكن، الطريقة الصحيحة لتنفيذها هي تسليم الشهادة بشكل آمن عبر عملية غير متصلة بالإنترنت ، بدلاً من عبر القناة غير المصادقة ، سيتم استخدامها للمصادقة. بالتأكيد هذا واضح؟ من الواضح بالتأكيد لكل منظمة مدركة للأمن عملت من أجلها ، من البنوك التي لديها آلاف الفروع إلى شركاتي الخاصة. "حل" الكود من جانب العميل "حل" الثقة الكل الشهادات ، بما في ذلك الشهادات الموقعة ذاتيا موقعة من قبل أي شخص على الإطلاق ، أو أي هيئة محرجة تضع نفسها على أنها كاليفورنيا IPSO بحكم الواقع غير امن. إنه مجرد اللعب في الأمن. من غير المجدي. لديك محادثة خاصة ، مقاومة للعبث ، مقاومة للرد ، مع ... شخص ما. اي شخص. رجل في الوسط. منتحل. اي شخص. يمكنك كذلك استخدام النص العادي.

الإجابة المقبولة على ما يرام ، لكنني أرغب في إضافة شيء إلى هذا لأنني كنت أستخدم Intellij على Mac ولم أستطع تشغيله باستخدام JAVA_HOME متغير المسار.

اتضح أن Java Home كان مختلفًا عند تشغيل التطبيق من Intellij.

لمعرفة مكان وجوده بالضبط ، يمكنك فقط القيام به System.getProperty("java.home") لأن هذا هو المكان الذي تتم فيه قراءة الشهادات الموثوقة.

هناك بديل أفضل للثقة في جميع الشهادات: إنشاء أ TrustStore يثق على وجه التحديد شهادة معينة واستخدم هذا لإنشاء أ SSLContext من الذي يحصل على SSLSocketFactory لتعيين على HttpsURLConnection. إليك الرمز الكامل:

File crtFile = new File("server.crt");
Certificate certificate = CertificateFactory.getInstance("X.509").generateCertificate(new FileInputStream(crtFile));

KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(null, null);
keyStore.setCertificateEntry("server", certificate);

TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(keyStore);

SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, trustManagerFactory.getTrustManagers(), null);

HttpsURLConnection connection = (HttpsURLConnection) new URL(url).openConnection();
connection.setSSLSocketFactory(sslContext.getSocketFactory());

يمكنك بدلاً من ذلك تحميل KeyStore مباشرة من ملف أو استرداد شهادة X.509 من أي مصدر موثوق به.

لاحظ أنه مع هذا الرمز ، الشهادات في cacerts لن يتم استخدامه. هذا بالذات HttpsURLConnection سوف تثق فقط هذه الشهادة المحددة.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top