كيف يمكنني استخدام مختلف الشهادات المحددة الاتصالات ؟

StackOverflow https://stackoverflow.com/questions/859111

سؤال

وحدة انا إضافة إلى كبيرة جافا التطبيق التحدث مع شركة أخرى SSL المضمون الموقع.المشكلة أن الموقع يستخدم شهادة موقعة ذاتيا.لدي نسخة من شهادة للتحقق من أنني لا تواجه رجل-في-الوسط الهجوم و أريد أن تتضمن هذه الشهادة إلى رمز لنا في مثل هذه الطريقة أن الاتصال إلى الملقم سوف تكون ناجحة.

هنا هو رمز الأساسية:

void sendRequest(String dataPacket) {
  String urlStr = "https://host.example.com/";
  URL url = new URL(urlStr);
  HttpURLConnection conn = (HttpURLConnection)url.openConnection();
  conn.setMethod("POST");
  conn.setRequestProperty("Content-Length", data.length());
  conn.setDoOutput(true);
  OutputStreamWriter o = new OutputStreamWriter(conn.getOutputStream());
  o.write(data);
  o.flush();
}

دون أي معالجة إضافية في مكان شهادة موقعة ذاتيا, هذا يموت في كونيتيكت.getOutputStream() مع استثناء التالية:

Exception in thread "main" 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
....
Caused by: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
....
Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target

من الناحية المثالية, يا رمز يحتاج إلى تعليم جافا لقبول هذه الشهادة الموقعة ذاتيا ، لهذا بقعة واحدة في التطبيق ، وليس في أي مكان آخر.

وأنا أعلم أنني يمكن أن استيراد الشهادة إلى JRE شهادة سلطة المخزن ، و التي من شأنها أن تسمح جافا لقبول ذلك.هذا ليس نهج أريد أن تأخذ إذا كنت يمكن أن تساعد ؛ يبدو الغازية للغاية أن تفعل على جميع العملاء لدينا آلات وحدة واحدة قد لا الاستخدام ؛ فإنه يؤثر على جميع تطبيقات جافا باستخدام نفس JRE, و أنا لا أحب ذلك على الرغم من الصعاب من أي تطبيق جافا من أي وقت مضى الوصول إلى هذا الموقع النيل.كما أنها ليست تافهة العملية:على UNIX يجب الحصول على حقوق الوصول إلى تعديل JRE في هذا السبيل.

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

ما هو المفضل, القياسية, أو أفضل طريقة انشاء تطبيق جافا قبول شهادة موقعة ذاتيا?هل يمكنني إنجاز كل الأهداف التي يجب في العقل أعلاه, أو أنا ذاهب إلى وسط ؟ هل هناك خيار إشراك الملفات و الدلائل و إعدادات التكوين و القليل من الكود ؟

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

المحلول

وإنشاء مصنع SSLSocket نفسك، ووضعه على HttpsURLConnection قبل الاتصال.

...
HttpsURLConnection conn = (HttpsURLConnection)url.openConnection();
conn.setSSLSocketFactory(sslFactory);
conn.setMethod("POST");
...

وأنت تريد إنشاء SSLSocketFactory واحدة والحفاظ حولها. وفيما يلي رسم تخطيطي لكيفية تهيئة ما يلي:

/* Load the keyStore that includes self-signed cert as a "trusted" entry. */
KeyStore keyStore = ... 
TrustManagerFactory tmf = 
  TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(keyStore);
SSLContext ctx = SSLContext.getInstance("TLS");
ctx.init(null, tmf.getTrustManagers(), null);
sslFactory = ctx.getSocketFactory();

إذا كنت بحاجة إلى مساعدة خلق مخزن المفاتيح، يرجى التعليق.


وهنا مثال من تحميل مخزن المفاتيح:

KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(trustStore, trustStorePassword);
trustStore.close();

لإنشاء مخزن المفاتيح مع شهادة تنسيق PEM، يمكنك كتابة التعليمات البرمجية الخاصة بك باستخدام CertificateFactory، أو مجرد استيراده مع keytool من JDK (keytool <م> سوف لا العمل من أجل "إدخال مفتاح "، ولكن على ما يرام ل" دخول ثقة ").

keytool -import -file selfsigned.pem -alias server -keystore server.jks

نصائح أخرى

وقرأت من خلال الكثير من الأماكن على الانترنت لحل هذا الامر. هذا هو رمز كتبت والعمل على انجاحه:

                ByteArrayInputStream derInputStream = new ByteArrayInputStream(app.certificateString.getBytes());
                CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
                X509Certificate cert = (X509Certificate) certificateFactory.generateCertificate(derInputStream);
                String alias = "alias";//cert.getSubjectX500Principal().getName();

                KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
                trustStore.load(null);
                trustStore.setCertificateEntry(alias, cert);
                KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
                kmf.init(trustStore, null);
                KeyManager[] keyManagers = kmf.getKeyManagers();

                TrustManagerFactory tmf = TrustManagerFactory.getInstance("X509");
                tmf.init(trustStore);
                TrustManager[] trustManagers = tmf.getTrustManagers();

                SSLContext sslContext = SSLContext.getInstance("TLS");
                sslContext.init(keyManagers, trustManagers, null);
                URL url = new URL(someURL);
                conn = (HttpsURLConnection) url.openConnection();
                conn.setSSLSocketFactory(sslContext.getSocketFactory());

وapp.certificateString هو سلسلة الذي يحتوي على شهادة، على سبيل المثال:

            static public String certificateString=
            "-----BEGIN CERTIFICATE-----\n" +
            "MIIGQTCCBSmgAwIBAgIHBcg1dAivUzANBgkqhkiG9w0BAQsFADCBjDELMAkGA1UE" +
            "BhMCSUwxFjAUBgNVBAoTDVN0YXJ0Q29tIEx0ZC4xKzApBgNVBAsTIlNlY3VyZSBE" +
            ... a bunch of characters...
            "5126sfeEJMRV4Fl2E5W1gDHoOd6V==\n" +
            "-----END CERTIFICATE-----";

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

إذا خلق SSLSocketFactory ليس خيارا ، فقط استيراد المفتاح في JVM

  1. استرداد المفتاح العمومي:$openssl s_client -connect dev-server:443, ثم إنشاء ملف dev-الخادم.pem التي تبدو مثل

    -----BEGIN CERTIFICATE----- 
    lklkkkllklklklklllkllklkl
    lklkkkllklklklklllkllklkl
    lklkkkllklk....
    -----END CERTIFICATE-----
    
  2. الواردات الرئيسية: #keytool -import -alias dev-server -keystore $JAVA_HOME/jre/lib/security/cacerts -file dev-server.pem.كلمة المرور: changeit

  3. إعادة تشغيل JVM

المصدر: كيفية حل javax.صافي.ssl.SSLHandshakeException?

ونحن نسخ truststore على JRE وإضافة شهادات عادتنا إلى أن truststore، ثم نقول للتطبيق لاستخدام truststore مخصص مع خاصية النظام. بهذه الطريقة نترك truststore JRE الافتراضي وحده.

والجانب السلبي هو أنه عندما تقوم بتحديث JRE لم تحصل truststore الجديدة تلقائيا اندمجت مع عهدك واحد.

هل يمكن معالجة هذا السيناريو ربما من خلال وجود المثبت أو بدء التشغيل روتين الذي يتحقق من truststore / جدك والشيكات لعدم تطابق أو بتحديث truststore تلقائيا. أنا لا أعرف ماذا يحدث إذا قمت بتحديث truststore أثناء تشغيل التطبيق.

وهذا الحل ليس 100٪ أنيقة أو مضمونا لكنه بسيط، يعمل، ولا يحتاج إلى التعليمات البرمجية.

ولقد كان علي أن أفعل شيئا من هذا القبيل عند استخدام المشاعات-HttpClient وإصلاحه الوصول إلى الخادم الشبكي الداخلي مع شهادة موقعة ذاتيا. نعم، كان لدينا حل لإنشاء TrustManager المخصصة التي مرت ببساطة كل شيء (تسجيل رسالة التصحيح).

وهذا يأتي الى وجود SSLSocketFactory منطقتنا التي تخلق مآخذ SSL من وجهة نظرنا SSLContext المحلي، والتي تم إعدادها لديك فقط لدينا TrustManager المحلية المرتبطة به. لا تحتاج للذهاب بالقرب من تخزين المفاتيح / certstore على الإطلاق.

وهكذا وهذا هو في LocalSSLSocketFactory لدينا:

static {
    try {
        SSL_CONTEXT = SSLContext.getInstance("SSL");
        SSL_CONTEXT.init(null, new TrustManager[] { new LocalSSLTrustManager() }, null);
    } catch (NoSuchAlgorithmException e) {
        throw new RuntimeException("Unable to initialise SSL context", e);
    } catch (KeyManagementException e) {
        throw new RuntimeException("Unable to initialise SSL context", e);
    }
}

public Socket createSocket(String host, int port) throws IOException, UnknownHostException {
    LOG.trace("createSocket(host => {}, port => {})", new Object[] { host, new Integer(port) });

    return SSL_CONTEXT.getSocketFactory().createSocket(host, port);
}

وجنبا إلى جنب مع غيرها من الأساليب تنفيذ SecureProtocolSocketFactory. LocalSSLTrustManager هو دمية تنفيذ مدير الثقة المذكور.

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