मैं विशिष्ट कनेक्शन पर विभिन्न प्रमाणपत्रों का उपयोग कैसे कर सकता हूं?


147

एक मॉड्यूल जो मैं अपने बड़े जावा एप्लिकेशन में जोड़ रहा हूं उसे किसी अन्य कंपनी की एसएसएल-सुरक्षित वेबसाइट के साथ बातचीत करना है। समस्या यह है कि साइट एक स्व-हस्ताक्षरित प्रमाणपत्र का उपयोग करती है। मेरे पास यह प्रमाणित करने के लिए प्रमाण पत्र की एक प्रति है कि मुझे मैन-इन-द-बीच हमले का सामना नहीं करना पड़ रहा है, और मुझे इस प्रमाणपत्र को हमारे कोड में इस तरह से शामिल करने की आवश्यकता है कि सर्वर से कनेक्शन सफल रहेगा।

यहाँ बुनियादी कोड है:

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

स्व-हस्ताक्षरित प्रमाणपत्र के लिए जगह में किसी भी अतिरिक्त हैंडलिंग बिना, इस निम्न अपवादों के साथ conn.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 

आदर्श रूप में , मेरे कोड को इस एक स्व-हस्ताक्षरित प्रमाण पत्र को स्वीकार करने के लिए जावा को सिखाने की जरूरत है, एप्लिकेशन में इस स्थान के लिए, और कहीं और नहीं।

मुझे पता है कि मैं जेआरई के प्रमाणपत्र प्राधिकरण स्टोर में प्रमाणपत्र आयात कर सकता हूं, और इससे जावा इसे स्वीकार करने की अनुमति देगा। यह एक दृष्टिकोण नहीं है जिसे मैं लेना चाहता हूं यदि मैं मदद कर सकता हूं; ऐसा लगता है कि हमारे सभी ग्राहक की मशीनों पर एक मॉड्यूल के लिए बहुत आक्रामक लगता है, जिसका उपयोग नहीं हो सकता है; यह एक ही जेआरई का उपयोग कर अन्य सभी जावा अनुप्रयोगों को प्रभावित करेगा, और मुझे यह पसंद नहीं है भले ही इस साइट तक पहुंचने वाले किसी भी अन्य जावा एप्लिकेशन की बाधाएं शून्य हों। यह एक मामूली ऑपरेशन भी नहीं है: यूनिक्स पर मुझे इस तरह जेआरई को संशोधित करने के लिए एक्सेस अधिकार प्राप्त करना होगा।

मैंने यह भी देखा है कि मैं एक ट्रस्टमैनेजर उदाहरण बना सकता हूं जो कुछ कस्टम जांच करता है। ऐसा लगता है कि मैं एक ट्रस्टमैनेजर भी तैयार कर सकता हूं जो इस प्रमाण पत्र को छोड़कर सभी मामलों में असली ट्रस्टमैनेजर को प्रतिनिधि करता है। लेकिन ऐसा लगता है कि ट्रस्टमैनेजर वैश्विक रूप से स्थापित हो जाता है, और मुझे लगता है कि हमारे आवेदन से अन्य सभी कनेक्शन प्रभावित होंगे, और यह मेरे लिए बिल्कुल सही गंध नहीं करता है।

एक स्व-हस्ताक्षरित प्रमाणपत्र स्वीकार करने के लिए जावा एप्लिकेशन सेट अप करने के लिए पसंदीदा, मानक या सर्वोत्तम तरीका क्या है? क्या मैं ऊपर दिए गए सभी लक्ष्यों को पूरा कर सकता हूं, या क्या मुझे समझौता करना होगा? क्या फाइलों और निर्देशिकाओं और कॉन्फ़िगरेशन सेटिंग्स, और कम-से-कम कोड शामिल करने का कोई विकल्प है?

  0

छोटा, काम करने वाला फ़िक्स: http://www.rgagnon.com/javadetails/java-fix-certificate-problem-in-HTTPS।एचटीएमएल 16 aug. 112011-08-16 14:06:49

+16

@ हसनप्रिएस्टर: कृपया इस पृष्ठ का सुझाव न दें। यह सभी ट्रस्ट सत्यापन अक्षम करता है। आप केवल अपने द्वारा हस्ताक्षरित स्व-हस्ताक्षरित प्रमाणपत्र को स्वीकार नहीं करेंगे, आप किसी भी प्रमाण पत्र को स्वीकार करने वाले भी हैं जो एमआईटीएम हमलावर आपको पेश करेगा। 23 dec. 112011-12-23 13:23:14

150

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 साथ आयात जेडीके (keytool "कुंजी प्रविष्टि" के लिए काम नहीं करेगा, लेकिन "विश्वसनीय प्रविष्टि" के लिए ठीक है)।

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

बहुत बहुत धन्यवाद! अन्य लोगों ने मुझे सही दिशा में इंगित करने में मदद की, लेकिन अंत में आपका दृष्टिकोण मैंने लिया। मुझे पीईएम प्रमाणपत्र फ़ाइल को जेकेएस जावा कीस्टोर फ़ाइल में परिवर्तित करने का एक कठिन काम था, और मुझे इसके लिए सहायता मिली: http://stackoverflow.com/questions/722931/ssl-socket-php-code-needs-to- be-convert-to-java 13 may. 092009-05-13 21:37:13

+1

मुझे खुशी है कि यह काम करता है। मुझे खेद है कि आप कुंजी स्टोर के साथ संघर्ष कर रहे हैं; मुझे बस इसे अपने जवाब में शामिल करना चाहिए था। सर्टिफिकेट फैक्टरी के साथ यह बहुत मुश्किल नहीं है। वास्तव में, मुझे लगता है कि मैं बाद में आने वाले किसी के लिए अपडेट करूंगा। 13 may. 092009-05-13 21:40:51

  0

वाह। पीईएम को जेकेएस में परिवर्तित करने का मतलब मैंने जो किया उससे काफी आसान है! धन्यवाद! :) 14 may. 092009-05-14 19:19:00

+1

यह सभी कोड जेएसएसई रिफर्न्सेंस गाइड में वर्णित तीन सिस्टम गुणों को सेट करके आप जो कर सकते हैं उसे दोहराएं। 31 may. 122012-05-31 19:36:53

+1

@EJP: यह थोड़ी देर पहले था, इसलिए मुझे निश्चित रूप से याद नहीं है, लेकिन मुझे तर्क है कि "बड़े जावा एप्लिकेशन" में, अन्य HTTP कनेक्शन स्थापित होने की संभावना है। वैश्विक गुणों को सेट करने से कामकाजी कनेक्शन में हस्तक्षेप हो सकता है, या इस पार्टी को सर्वर को खराब करने की अनुमति मिल सकती है। यह केस-दर-मामले आधार पर अंतर्निहित ट्रस्ट मैनेजर का उपयोग करने का एक उदाहरण है। 31 may. 122012-05-31 22:26:58

+2

ओपी ने कहा, "मेरे कोड को इस एक स्व-हस्ताक्षरित प्रमाणपत्र को स्वीकार करने के लिए जावा को सिखाने की जरूरत है, आवेदन में इस स्थान के लिए, और कहीं और नहीं।" 31 may. 122012-05-31 22:29:09

  0

मैंने आपके समाधान को लागू करने का प्रयास नहीं किया है कृपया मेरी मदद करें http://stackoverflow.com/questions/20190364/how-to-bypass-certificateexception-by-java 25 nov. 132013-11-25 10:59:37

  0

यदि आप इसे "वैश्विक रूप से" उपयोग स्वीकार करना चाहते हैं cacerts फ़ाइल में एक विश्वसनीय प्रमाण के रूप में प्रमाण पत्र स्थापित करने के लिए keytool। फिर किसी भी वीएम के लिए यह भरोसा किया जाएगा कि जेआरई का उपयोग करता है। 06 dec. 132013-12-06 19:59:08

  0

@ केलीम यह वैश्विक स्वीकृति के लिए एक अच्छा दृष्टिकोण नहीं है। 06 dec. 132013-12-06 20:03:33

  0

यही कारण है कि मैंने उद्धरणों में "वैश्विक" रखा और समझाया कि यह वास्तव में कैसे काम करता है। किसी और ने ऐसा करने का उल्लेख नहीं किया हालांकि यह एक बहुत ही आम और स्वीकार्य विधि है। 06 dec. 132013-12-06 20:07:48

  0

कई बड़े संगठन उस विधि को स्वीकार नहीं करते हैं। गंभीर परिणामों के साथ यह एक सुरक्षा उल्लंघन होगा। 06 dec. 132013-12-06 21:09:37

  0

@ एरिक्सन हाय - यह चार साल बाद है और जिस दृष्टिकोण से आपने हमें सिखाया है वह अच्छी तरह से हमला कर रहा है और हम कई अलग-अलग सर्वरों के साथ संवाद कर रहे हैं जिनके साथ हम संवाद करते हैं। अब मुझे एक नई चाल मिली है जिसे मुझे समझने की आवश्यकता है (इसके लिए एक नया स्टैक ओवरफ्लो प्रश्न बना सकता है): मैं सर्वर के स्वयं-हस्ताक्षरित प्रमाणपत्र के साथ डिफ़ॉल्ट कीस्टोर और कस्टम कीस्टोर दोनों पर भरोसा करना चाहता हूं। हमारे पास एक ऐसा मामला था जहां सर्वर को वास्तविक प्रमाणपत्र में अपग्रेड किया गया था और कनेक्शन तोड़ दिया गया था, लेकिन अगर हम डिफ़ॉल्ट कीस्टोर पर भरोसा कर रहे थे तो यह ठीक रहेगा। 13 feb. 142014-02-13 19:25:02

  0

@erickson एपीआई थोड़ा भ्रामक है: इसका तात्पर्य है कि आप TrustManagers को SSLContext.init() पर पास कर सकते हैं, लेकिन उस विधि पर जावाडोक इंगित करता है कि सरणी में केवल पहला ट्रस्टमैनेजर उपयोग किया जाता है। मुझे आपके द्वारा साझा की जाने वाली किसी अंतर्दृष्टि को सुनना अच्छा लगेगा। 13 feb. 142014-02-13 19:26:18

  0

@skiphoppy मुझे इसके साथ थोड़ा सा प्रयोग करना होगा और आपको वापस मिलना होगा। मैंने पहले यह नहीं किया है, लेकिन मुझे एक ऐसा विचार है जो काम कर सकता है। 13 feb. 142014-02-13 20:03:55

+1

@skiphoppy हम्म, मैंने पाया है कि एकमात्र तरीका अस्थायी 'कीस्टोर' (स्मृति में) बनाना है और अपने सभी "असली" कुंजी स्टोर से रूट कर्ट जोड़ना है, या (लगभग समतुल्य रूप से) 'सेट <TrustAnchor>' अपने विश्वसनीय कुंजी स्टोर में सभी प्रविष्टियों से और 'PKIXParameters' को प्रारंभ करने के लिए इसका उपयोग करें, और इसके साथ ही,' TrustManagerFactory'। 13 feb. 142014-02-13 21:48:48

  0

धन्यवाद @ एरिक्सन मैं अभी भी इसके साथ खेल रहा हूं और दस्तावेज़ीकरण की एक लंबी भूलभुलैया के माध्यम से घूम रहा हूं। मैं इसे एक शॉट दूंगा। 13 feb. 142014-02-13 22:03:09


12

हम जेआरई ट्रस्टस्टोर की प्रतिलिपि बनाते हैं और उस ट्रस्टस्टोर में हमारे कस्टम प्रमाणपत्र जोड़ते हैं, फिर एप्लिकेशन को कस्टम प्रोस्टस्टोर का उपयोग सिस्टम प्रॉपर्टी के साथ करने के लिए बताएं। इस तरह हम अकेले डिफ़ॉल्ट जेआरई ट्रस्टस्टोर छोड़ देते हैं।

नकारात्मकता यह है कि जब आप जेआरई अपडेट करते हैं तो आपको अपने नए ट्रस्टस्टोर को स्वचालित रूप से आपके कस्टम के साथ विलय नहीं मिलता है।

आप शायद इस परिदृश्य को इंस्टॉलर या स्टार्टअप दिनचर्या के साथ संभाल सकते हैं जो ट्रस्टस्टोर/जेडीके की पुष्टि करता है और किसी मेल के लिए जांच करता है या ट्रस्टस्टोर को स्वचालित रूप से अपडेट करता है। मुझे नहीं पता कि क्या होता है यदि आप ट्रस्टस्टोर को अपडेट करते समय ट्रस्टस्टोर अपडेट करते हैं।

यह समाधान 100% सुरुचिपूर्ण या मूर्ख नहीं है लेकिन यह आसान है, काम करता है, और कोई कोड की आवश्यकता नहीं है।


12

मुझे स्वयं-हस्ताक्षरित प्रमाणपत्र के साथ एक आंतरिक https सर्वर तक पहुंचने के लिए कॉमन्स-httpclient का उपयोग करते समय ऐसा कुछ करना पड़ा। हां, हमारा समाधान कस्टम ट्रस्टमैनेजर बनाना था जो बस सबकुछ पास कर चुका था (डीबग संदेश लॉगिंग)।

यह हमारे स्वयं के SSLSocketFactory होने के लिए नीचे आता है जो हमारे स्थानीय SSLContext से SSL सॉकेट बनाता है, जो कि केवल हमारे स्थानीय ट्रस्टमैनेजर से जुड़ा हुआ है। आपको एक कीस्टोर/प्रमाण पत्र के पास जाने की आवश्यकता नहीं है।

तो यह हमारे 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 उपरोक्त डमी ट्रस्ट मैनेजर कार्यान्वयन है।

+7

यदि आप सभी ट्रस्ट सत्यापन अक्षम करते हैं, तो पहले स्थान पर SSL/TLS का उपयोग करके थोड़ा सा बिंदु है। स्थानीय रूप से परीक्षण के लिए यह ठीक है, लेकिन अगर आप बाहर कनेक्ट करना चाहते हैं। 23 dec. 112011-12-23 13:26:31

  0

मुझे जावा 7 पर चलते समय यह अपवाद मिलता है। Javax.net.ssl.SSLHandshakeException: सामान्य में कोई सिफर सुइट्स आप सहायता कर सकते हैं? 19 dec. 132013-12-19 12:55:10


14

तो एक SSLSocketFactory बनाने एक विकल्प नहीं है, बस JVM

  1. में कुंजी आयात सार्वजनिक कुंजी प्राप्त करें: $openssl s_client -connect dev-server:443, तो एक फ़ाइल देव-server.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

स्रोत: How to solve javax.net.ssl.SSLHandshakeException?

+1

मुझे नहीं लगता कि यह मूल प्रश्न हल करता है, लेकिन यह * मेरी * समस्या हल हो गया, तो धन्यवाद! 15 may. 132013-05-15 15:10:40


11

मैं अतः में स्थानों की बहुत सारी के माध्यम से चला गया और वेब इस बात को हल करने के।

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

मैं परीक्षण किया है कि आप प्रमाण पत्र स्ट्रिंग में कोई भी वर्ण डाल सकते हैं:

   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 एक स्ट्रिंग है कि उदाहरण के लिए, प्रमाणपत्र शामिल है: यह कोड है कि मेरे लिए काम किया है , यदि यह स्वयं हस्ताक्षरित है, तब तक जब तक आप ऊपर की सटीक संरचना रखते हैं। मैंने अपने लैपटॉप की टर्मिनल कमांड लाइन के साथ प्रमाणपत्र स्ट्रिंग प्राप्त की।