क्या जावा सर्टिफिकेट को एनक्रिप्ट करने का समर्थन करता है?


125

मैं एक जावा एप्लिकेशन विकसित कर रहा हूं जो HTTP पर एक रिमोट सर्वर पर REST API को क्वेरी करता है। सुरक्षा कारणों से इस संचार को HTTPS में बदल दिया जाना चाहिए।

अब जब कि Encrypt ने अपना सार्वजनिक बीटा शुरू कर दिया है, मैं जानना चाहूंगा कि क्या जावा वर्तमान में काम करता है (या भविष्य में काम करने की पुष्टि की जाती है) डिफ़ॉल्ट रूप से उनके प्रमाण पत्र के साथ।

आइए एनक्रिप्ट को IdenTrust द्वारा अपने मध्यवर्ती क्रॉस-साइन किए गए , जो अच्छी खबर होनी चाहिए। हालाँकि, मैं इन दोनों में से किसी को भी इस कमांड के आउटपुट में नहीं पा सकता:

keytool -keystore "..\lib\security\cacerts" -storepass changeit -list

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


1
एक की जाँच भी कर सकते हैं चलो यहाँ संगतता एन्क्रिप्ट करें letencrypt.org/docs/certificate-compatibility
potame

@potame "जावा 8u131 के साथ आपको अभी भी अपने प्रमाणपत्र को अपने ट्रस्टस्टोर में जोड़ना है" इसलिए यदि आपको लेट्स एनक्रिप्ट से प्रमाण पत्र प्राप्त होता है, तो आपको उस प्रमाणपत्र को जोड़ना होगा जो आपको ट्रस्टस्टोर में मिला है? क्या यह पर्याप्त नहीं होना चाहिए कि उनका सीए शामिल है?
mxro

1
@mxro हाय - इस पर मेरा ध्यान आकर्षित करने के लिए धन्यवाद। ऊपर मेरी टिप्पणी बिल्कुल सही नहीं है (वास्तव में समस्या उस से अधिक जटिल थी और हमारे बुनियादी ढांचे से संबंधित थी) और मैं उन्हें हटाने जा रहा हूं क्योंकि वे वास्तव में केवल भ्रम की ओर अग्रसर हैं। तो अगर आपके पास jdk> Java 8u101 है, तो आइए Encrypt सर्टिफिकेट काम करें और ठीक से पहचाने और भरोसेमंद बनें।
पोटा नाम

@ छोटा नाम उत्कृष्ट है। सफाई देने के लिए धन्यवाद!
mxro

जवाबों:


142

[ अपडेट २०१६-०६-०en : https://bugs.openjdk.java.net/browse/JDK-8154757 के अनुसार IdenTrust CA को Oracle Java 8u101 में शामिल किया जाएगा।]

[ अपडेट २०१६-० and-०५ : जावा 101११०१ जारी किया गया है और इसमें वास्तव में IdenTrust CA: रिलीज़ नोट शामिल हैं ]


क्या जावा सर्टिफिकेट को एनक्रिप्ट करने का समर्थन करता है?

हाँ। लेट एनक्रिप्ट सर्टिफिकेट सिर्फ एक नियमित सार्वजनिक कुंजी प्रमाणपत्र है। जावा इसे सपोर्ट करता है ( लेट्स एनक्रिप्ट सर्टिफिकेशन कम्पैटिबिलिटी के अनुसार , जावा 7> = 7u111 और जावा 8> = 8u101 के लिए)।

क्या जावा ट्रस्ट बॉक्स से बाहर प्रमाण पत्र एन्क्रिप्ट करता है?

नहीं / यह जेवीएम पर निर्भर करता है। Oracle JDK / JRE के 8u66 तक के ट्रस्टस्टोर में न तो लेट्स एनक्रिप्ट क्रिप्ट विशेष रूप से है और न ही IdenTrust CA कि क्रॉस ने इस पर हस्ताक्षर किए हैं। new URL("https://letsencrypt.org/").openConnection().connect();उदाहरण के लिए में परिणाम javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException

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

https://community.letsencrypt.org/t/will-the-cross-root-cover-trust-by-the-default-list-in-the-jdk-jre/134/10 विषय पर भी चर्चा करता है।


यहाँ कुछ उदाहरण कोड है जो दिखाता है कि रनटाइम के दौरान डिफ़ॉल्ट ट्रस्टस्टोर में प्रमाणपत्र कैसे जोड़ा जाए। आपको बस प्रमाणपत्र जोड़ना होगा (फ़ायरफ़ॉक्स से .der के रूप में निर्यात किया जाता है और क्लासपाथ में डाला जाता है)

के आधार पर मैं कैसे जावा में विश्वसनीय रूट प्रमाणपत्रों की एक सूची प्राप्त कर सकते हैं? और http://developer.android.com/training/articles/security-ssl.html#UnognCa

import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.KeyStore;
import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
import java.security.cert.PKIXParameters;
import java.security.cert.TrustAnchor;
import java.security.cert.X509Certificate;

import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLHandshakeException;
import javax.net.ssl.TrustManagerFactory;

public class SSLExample {
    // BEGIN ------- ADDME
    static {
        try {
            KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
            Path ksPath = Paths.get(System.getProperty("java.home"),
                    "lib", "security", "cacerts");
            keyStore.load(Files.newInputStream(ksPath),
                    "changeit".toCharArray());

            CertificateFactory cf = CertificateFactory.getInstance("X.509");
            try (InputStream caInput = new BufferedInputStream(
                    // this files is shipped with the application
                    SSLExample.class.getResourceAsStream("DSTRootCAX3.der"))) {
                Certificate crt = cf.generateCertificate(caInput);
                System.out.println("Added Cert for " + ((X509Certificate) crt)
                        .getSubjectDN());

                keyStore.setCertificateEntry("DSTRootCAX3", crt);
            }

            if (false) { // enable to see
                System.out.println("Truststore now trusting: ");
                PKIXParameters params = new PKIXParameters(keyStore);
                params.getTrustAnchors().stream()
                        .map(TrustAnchor::getTrustedCert)
                        .map(X509Certificate::getSubjectDN)
                        .forEach(System.out::println);
                System.out.println();
            }

            TrustManagerFactory tmf = TrustManagerFactory
                    .getInstance(TrustManagerFactory.getDefaultAlgorithm());
            tmf.init(keyStore);
            SSLContext sslContext = SSLContext.getInstance("TLS");
            sslContext.init(null, tmf.getTrustManagers(), null);
            SSLContext.setDefault(sslContext);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
    // END ---------- ADDME

    public static void main(String[] args) throws IOException {
        // signed by default trusted CAs.
        testUrl(new URL("https://google.com"));
        testUrl(new URL("https://www.thawte.com"));

        // signed by letsencrypt
        testUrl(new URL("https://helloworld.letsencrypt.org"));
        // signed by LE's cross-sign CA
        testUrl(new URL("https://letsencrypt.org"));
        // expired
        testUrl(new URL("https://tv.eurosport.com/"));
        // self-signed
        testUrl(new URL("https://www.pcwebshop.co.uk/"));

    }

    static void testUrl(URL url) throws IOException {
        URLConnection connection = url.openConnection();
        try {
            connection.connect();
            System.out.println("Headers of " + url + " => "
                    + connection.getHeaderFields());
        } catch (SSLHandshakeException e) {
            System.out.println("Untrusted: " + url);
        }
    }

}

एक और बात: मुझे किस फ़ाइल का उपयोग करने की आवश्यकता है? आइए एनक्रिप्ट डाउनलोड के लिए एक रूट और चार इंटरमीडिएट प्रमाणपत्र प्रदान करते हैं। मैंने कोशिश की isrgrootx1.derऔर lets-encrypt-x1-cross-signed.der, लेकिन उनमें से कोई भी सही नहीं लगता है।
Hexaholic

@ हेक्साहॉलिक इस बात पर निर्भर करता है कि आप किस साइट पर भरोसा करना चाहते हैं और आपके द्वारा उपयोग की जाने वाली साइट को कैसे कॉन्फ़िगर करना है। https://helloworld.letsencrypt.orgउदाहरण के लिए जाएं और ब्राउज़र में प्रमाण पत्र श्रृंखला (हरी आइकन पर क्लिक करके) का निरीक्षण करें। इसके लिए आपको साइट विशिष्ट प्रमाणपत्र की आवश्यकता है, X1 इंटरमीडिएट एक (IdenTrust द्वारा हस्ताक्षरित क्रॉस) या DSTRootCAX3 एक। ISRG Root X1क्योंकि यह श्रृंखला, वैकल्पिक श्रृंखला है कि में नहीं है HelloWorld साइट के लिए काम नहीं करता। मैं DSTRoot एक का उपयोग करेगा, बस ब्राउज़र के माध्यम से निर्यात किया जाता है क्योंकि मैंने इसे कहीं भी डाउनलोड करने के लिए नहीं देखा है।
जपं

1
कोड के लिए धन्यवाद। InputStream को keyStore.load (Files.newInputStream (ksPath)) में बंद करना न भूलें।
माइकल वायराज

3
@ अनुकूलन-देव नहीं, सॉफ्टवेयर को अच्छे प्रमाणपत्रों की आपूर्ति के लिए एक अज्ञात प्रणाली पर भरोसा क्यों करना चाहिए? सॉफ़्टवेयर को उन प्रमाणपत्रों पर भरोसा करने में सक्षम होना चाहिए जो वास्तव में भरोसा करते हैं। यह एक सुरक्षा छेद होगा यदि इसका अर्थ यह होगा कि मेरा जावा प्रोग्राम किसी अन्य कार्यक्रम के लिए प्रमाणपत्र स्थापित कर सकता है। लेकिन यहाँ ऐसा नहीं होता है, कोड केवल रनटाइम के दौरान ही
सर्टिफिकेट जोड़ता

3
+1 कोड दिखाने के लिए जो javax.net.ssl.trustStoreसिस्टम प्रॉपर्टी को सेट करके सिर्फ धोखा नहीं देता है, लेकिन फिर JVM के डिफ़ॉल्ट को सेट करने के लिए -1 SSLContext। एक नया बनाना बेहतर होगा SSLSocketFactoryऔर फिर HttpUrlConnectionवीएम-वाइड एसएसएल कॉन्फ़िगरेशन को बदलने के बजाय विभिन्न कनेक्शन (जैसे ) के लिए इसका उपयोग करें। (मुझे लगता है कि यह परिवर्तन केवल चल रहे जेवीएम के लिए प्रभावी है और अन्य कार्यक्रमों को बनाए रखने या प्रभावित नहीं करता है। मुझे लगता है कि यह एक बेहतर प्रोग्रामिंग अभ्यास है जहां आपका कॉन्फ़िगरेशन लागू होता है, इसके बारे में स्पष्ट होना चाहिए।)
क्रिस्टोफर शुल्त्स

65

मुझे पता है कि ओपी ने स्थानीय कॉन्फ़िगरेशन परिवर्तन के बिना एक समाधान के लिए कहा था, लेकिन अगर आप किस्टोर में ट्रस्ट चेन को स्थायी रूप से जोड़ना चाहते हैं:

$ keytool -trustcacerts \
    -keystore $JAVA_HOME/jre/lib/security/cacerts \
    -storepass changeit \
    -noprompt \
    -importcert \
    -file /etc/letsencrypt/live/hostname.com/chain.pem

स्रोत: https://community.letsencrypt.org/t/will-the-cross-root-cover-trust-by-the-default-list-in-the-jdk-jre/134/13


6
हां, ओपी ने इसके लिए नहीं पूछा, लेकिन यह मेरे लिए सबसे सरल उपाय था!
शुभ अंक

मैंने अपने पुराने java 8 बिल्ड में यह सर्टिफिकेट letencrypt.org/certs/lets-encrypt-x3-cross-signed.pem.txt आयात किया है और वेब सेवा जो एन्क्रिप्ट सर्टिफिकेट का उपयोग कर रही है, वह उपलब्ध नहीं है! यह समाधान तेज और आसान था।
ईज़राइटर

56

हम में से उन लोगों के लिए विस्तृत उत्तर स्थानीय विन्यास परिवर्तन करने के लिए तैयार है, जिसमें विन्यास फाइल का बैकअप शामिल है:

1. परीक्षण अगर यह परिवर्तन से पहले काम कर रहा है

यदि आपके पास पहले से कोई परीक्षण कार्यक्रम नहीं है, तो आप मेरे जावा SSLPing पिंग प्रोग्राम का उपयोग कर सकते हैं जो TLS हैंडशेक का परीक्षण करता है (केवल HTTPS नहीं, बल्कि किसी भी SSL / TLS पोर्ट के साथ काम करेगा)। मैं पूर्वनिर्मित SSLPing.jar का उपयोग करूंगा, लेकिन कोड पढ़ना और इसे स्वयं बनाना एक त्वरित और आसान काम है:

$ git clone https://github.com/dimalinux/SSLPing.git
Cloning into 'SSLPing'...
[... output snipped ...]

चूँकि मेरा जावा संस्करण 1.8.0_101 (इस लेखन के समय जारी नहीं किया गया) से पहले है, एक लेट्स एनक्रिप्ट प्रमाण पत्र डिफ़ॉल्ट रूप से सत्यापित नहीं करेगा। आइए देखें कि फिक्स लगाने से पहले क्या विफलता दिखती है:

$ java -jar SSLPing/dist/SSLPing.jar helloworld.letsencrypt.org 443
About to connect to 'helloworld.letsencrypt.org' on port 443
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
[... output snipped ...]

2. प्रमाण पत्र आयात करें

मैं JAVA_HOME पर्यावरण चर सेट के साथ Mac OS X पर हूँ। बाद में आदेश मान लेंगे कि यह चर जावा इंस्टॉलेशन के लिए सेट है जिसे आप संशोधित कर रहे हैं:

$ echo $JAVA_HOME 
/Library/Java/JavaVirtualMachines/jdk1.8.0_92.jdk/Contents/Home/

Cacerts फ़ाइल का एक बैकअप बनाएं जिसे हम संशोधित करेंगे ताकि आप JDK को फिर से स्थापित किए बिना किसी भी परिवर्तन को वापस कर सकें:

$ sudo cp -a $JAVA_HOME/jre/lib/security/cacerts $JAVA_HOME/jre/lib/security/cacerts.orig

आयात करने के लिए हमें हस्ताक्षरित प्रमाणपत्र डाउनलोड करना होगा:

$ wget https://letsencrypt.org/certs/lets-encrypt-x3-cross-signed.der

आयात करें:

$ sudo keytool -trustcacerts -keystore $JAVA_HOME/jre/lib/security/cacerts -storepass changeit -noprompt -importcert -alias lets-encrypt-x3-cross-signed -file lets-encrypt-x3-cross-signed.der 
Certificate was added to keystore

3. सत्यापित करें कि यह परिवर्तनों के बाद काम कर रहा है

सत्यापित करें कि जावा अब SSL पोर्ट से जुड़कर खुश है:

$ java -jar SSLPing/dist/SSLPing.jar helloworld.letsencrypt.org 443
About to connect to 'helloworld.letsencrypt.org' on port 443
Successfully connected

8

JDK के लिए जो अभी तक आइए एन्क्रिप्ट प्रमाण पत्र का समर्थन नहीं करते हैं, तो आप JDK करने के लिए उन में जोड़ सकते हैं cacertsइस प्रक्रिया (करने के लिए धन्यवाद निम्नलिखित इस )।

सभी प्रमाणपत्र https://letsencrypt.org/certports/ ( der format को चुनें) पर डाउनलोड करें और उन्हें इस तरह के कमांड के साथ एक-एक करके जोड़ें (उदाहरण के लिए letsencryptauthorityx1.der):

keytool -import -keystore PATH_TO_JDK\jre\lib\security\cacerts -storepass changeit -noprompt -trustcacerts -alias letsencryptauthorityx1 -file PATH_TO_DOWNLOADS\letsencryptauthorityx1.der

इससे स्थिति में सुधार होता है, लेकिन फिर मुझे कनेक्शन त्रुटि मिलती है: javax.net.ssl.SSLException: java.lang.RuntimeException: DH keypair उत्पन्न नहीं कर सका
nafg
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.