2009-05-13 8 views
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

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

उत्तर

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 – skiphoppy

+1

मुझे खुशी है कि यह काम करता है। मुझे खेद है कि आप कुंजी स्टोर के साथ संघर्ष कर रहे हैं; मुझे बस इसे अपने जवाब में शामिल करना चाहिए था। सर्टिफिकेट फैक्टरी के साथ यह बहुत मुश्किल नहीं है। वास्तव में, मुझे लगता है कि मैं बाद में आने वाले किसी के लिए अपडेट करूंगा। – erickson

+0

वाह। पीईएम को जेकेएस में परिवर्तित करने का मतलब मैंने जो किया उससे काफी आसान है! धन्यवाद! :) – skiphoppy

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 का उपयोग करके थोड़ा सा बिंदु है। स्थानीय रूप से परीक्षण के लिए यह ठीक है, लेकिन अगर आप बाहर कनेक्ट करना चाहते हैं। – Bruno

+0

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

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

मुझे नहीं लगता कि यह मूल प्रश्न हल करता है, लेकिन यह * मेरी * समस्या हल हो गया, तो धन्यवाद! –

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

संबंधित मुद्दे

 संबंधित मुद्दे