ChatGPT解决这个技术问题 Extra ChatGPT

Accept server's self-signed ssl certificate in Java client

It looks like a standard question, but I couldn't find clear directions anywhere.

I have java code trying to connect to a server with probably self-signed (or expired) certificate. The code reports the following error :

[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

As I understand it, I have to use keytool and tell java that it's OK to allow this connection.

All instructions to fix this problem assume I'm fully proficient with keytool, such as

generate private key for server and import it into keystore

Is there anybody who could post detailed instructions?

I'm running unix, so bash script would be best.

Not sure if it's important, but code executed in jboss.

See How do I accept a self-signed certificate with a Java HttpsURLConnection?. Obviously, it would be better if you can get the site to use a valid cert.
Thanks for the link, I didn't see it while searching. But both solutions there involve special code to send a request and I'm using existing code (amazon ws client for java). Respectively, it's their site I'm connecting and I can't fix its certificate problems.
@MatthewFlaschen - "Obviously, it would be better if you can get the site to use a valid cert..." - A self signed certificate is a valid certificate if the client trusts it. Many think conferring trust to the CA/Browser cartel is a security defect.
Related, see The most dangerous code in the world: validating SSL certificates in non-browser software. (The link is provided since you seem to be getting those spammy answers that disable validation).

I
Idos

You have basically two options here: add the self-signed certificate to your JVM truststore or configure your client to

Option 1

Export the certificate from your browser and import it in your JVM truststore (to establish a chain of trust):

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

Option 2

Disable Certificate Validation:

// 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) {
} 

Note that I do not recommend the Option #2 at all. Disabling the trust manager defeats some parts of SSL and makes you vulnerable to man in the middle attacks. Prefer Option #1 or, even better, have the server use a "real" certificate signed by a well known CA.


Not just the MIM attack. It renders you vulnerable to connecting to the wrong site. It is completely insecure. See RFC 2246. I am opposed to posting this TrustManager at all times. It's not even correct w.r.t. its own specification.
@EJP I'm really not recommending the second option (I've updated my answer to make it clear). However, not posting it won't solve anything (this is public information) and doesn't IMHO deserve a downvote.
@EJP It's by teaching people that you educate them, not by hiding things. So keeping things secret or in obscurity is not a solution at all. This code is public, the Java API is public, it's better to talk about it than to ignore it. But I can live with you not agreeing.
The other option – the one you don't mention – is to get the server's certificate fixed either by fixing it yourself or by calling up the relevant support people. Single host certificates are really very cheap; futzing around with self-signed stuff is penny-wise pound-foolish (i.e., for those not familiar with that English idiom, a totally stupid set of priorities that costs lots to save almost nothing).
@Rich, 6 years late, but you can also get hold of the server cert in firefox by clicking the lock icon -> more information -> View Certificate -> Details tab -> Export... It's well hidden.
J
Johannes Brodwall

There's a better alternative to trusting all certificates: Create a TrustStore that specifically trusts a given certificate and use this to create a SSLContext from which to get the SSLSocketFactory to set on the HttpsURLConnection. Here's the complete code:

File crtFile = new File("server.crt");
Certificate certificate = CertificateFactory.getInstance("X.509").generateCertificate(new FileInputStream(crtFile));
// Or if the crt-file is packaged into a jar file:
// CertificateFactory.getInstance("X.509").generateCertificate(this.class.getClassLoader().getResourceAsStream("server.crt"));


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

You can alternatively load the KeyStore directly from a file or retrieve the X.509 Certificate from any trusted source.

Note that with this code, the certificates in cacerts will not be used. This particular HttpsURLConnection will only trust this specific certificate.


The [android documentation] (developer.android.com/training/articles/security-ssl#SelfSigned) basically gives your explanation. It just gives a bit more of an explanation, code and warnings.
In my opinion this should be the accepted answer! The advantage of this method is that you do everything programatically and still maintain security and you do not alter the JRE's trust store. Option #2 from the accepted answer is completely wrong from security purposes. Thank you for providing this solution!
If someone is going to pack the certificate into jar file, then replace InputStream in the method generateCertificate to this: this.class.getClassLoader().getResourceAsStream("server.crt")
s
spiffy

Apache HttpClient 4.5 supports accepting self-signed certificates:

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

This builds an SSL socket factory which will use the TrustSelfSignedStrategy, registers it with a custom connection manager then does an HTTP GET using that connection manager.

I agree with those who chant "don't do this in production", however there are use-cases for accepting self-signed certificates outside production; we use them in automated integration tests, so that we're using SSL (like in production) even when not running on the production hardware.


C
Community

I chased down this problem to a certificate provider that is not part of the default JVM trusted hosts as of JDK 8u74. The provider is www.identrust.com, but that was not the domain I was trying to connect to. That domain had gotten its certificate from this provider. See Will the cross root cover trust by the default list in the JDK/JRE? -- read down a couple entries. Also see Which browsers and operating systems support Let’s Encrypt.

So, in order to connect to the domain I was interested in, which had a certificate issued from identrust.com I did the following steps. Basically, I had to get the identrust.com (DST Root CA X3) certificate to be trusted by the JVM. I was able to do that using Apache HttpComponents 4.5 like so:

1: Obtain the certificate from indettrust at Certificate Chain Download Instructions. Click on the DST Root CA X3 link.

2: Save the string to a file named "DST Root CA X3.pem". Be sure to add the lines "-----BEGIN CERTIFICATE-----" and "-----END CERTIFICATE-----" in the file at the beginning and the end.

3: Create a java keystore file, cacerts.jks with the following command:

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

4: Copy the resulting cacerts.jks keystore into the resources directory of your java/(maven) application.

5: Use the following code to load this file and attach it to the Apache 4.5 HttpClient. This will solve the problem for all domains that have certificates issued from indetrust.com util oracle includes the certificate into the JRE default keystore.

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

When the project builds then the cacerts.jks will be copied into the classpath and loaded from there. I didn't, at this point in time, test against other ssl sites, but if the above code "chains" in this certificate then they will work too, but again, I don't know.

Reference: Custom SSL context and How do I accept a self-signed certificate with a Java HttpsURLConnection?


The question is about a self-signed certificate. This answer isn't.
Read the question (and answer) a little closer.
J
Jon Daniel

Rather than setting the default socket factory (which IMO is a bad thing) - yhis will just affect the current connection rather than every SSL connection you try to open:

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

Your checkServerTrusted does not implement the necessary logic to actually trust the certificate and ensure non-trusted certificates are rejected. This might make some good reading: The most dangerous code in the world: validating SSL certificates in non-browser software.
Your getAcceptedIssuers() method does not conform to the specifcation, and this 'solution' remains radically insecure.
A
Ashish Saini

Trust all SSL certificates:- You can bypass SSL if you want to test on the testing server. But do not use this code for production.

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) { 
    }
}

}

Please call this function in onCreate() function in Activity or in your Application Class.

NukeSSLCerts.nuke();

This can be used for Volley in Android.


Not entirely sure of why you are being down voted, unless the code doesn't work. Perhaps it's the assumption that Android is somehow involved when the original question didn't tag Android.
u
user3132194

Download your self-signed certificate with your browser from target page and add it to default storage with default password:

keytool -import -v -trustcacerts -file selfsigned.crt -alias myserver -keystore /etc/alternatives/jre/lib/security/cacerts -storepass changeit

Use file $JAVA_HOME/jre/lib/security/cacerts , my example here is from Oracle linux 7.7 .


u
user207421

If 'they' are using a self-signed certificate it is up to them to take the steps required to make their server usable. Specifically that means providing their certificate to you offline in a trustworthy way. So get them to do that. You then import that into your truststore using the keytool as described in the JSSE Reference Guide. Don't even think about the insecure TrustManager posted here.

EDIT For the benefit of the seventeen (!) downvoters, and numerous commenters below, who clearly have not actually read what I have written here, this is not a jeremiad against self-signed certificates. There is nothing wrong with self-signed certificates when implemented correctly. But, the correct way to implement them is to have the certificate delivered securely via an offline process, rather than via the unauthenticated channel they are going to be used to authenticate. Surely this is obvious? It is certainly obvious to every security-aware organization I have ever worked for, from banks with thousands of branches to my own companies. The client-side code-base 'solution' of trusting all certificates, including self-signed certificates signed by absolutely anybody, or any arbitary body setting itself up as a CA, is ipso facto not secure. It is just playing at security. It is pointless. You are having a private, tamperproof, reply-proof, injection-proof conversation with ... somebody. Anybody. A man in the middle. An impersonator. Anybody. You may as well just use plaintext.


Just because some server decided to use https, doesn't mean that the person with the client gives a crap about security for their own purposes.
I'm surprised this answer got downvoted so much. I would love to understand a little more why. It seems like EJP is suggesting that you shouldn't be doing option 2 because it allows for a major security flaw. Could someone explain why (other than pre-deployment settings) why this answer is low quality?
Well, I guess all of it. What does "it is up to them to take the steps required to make their server usable" mean? What does a server operator have to do to make it usable? What are the steps you have in mind? Can you provide a list of them? But stepping back to 1,000 feet, how does that even relate to the problem the OP asked? He wants to know how to make the client accept the self-signed certificate in Java. That's a simple matter of conferring trust in the code since the OP finds the certificate acceptable.
@EJP - Correct me if I am wrong, but accepting a self-signed certificate is a client side policy decision. It has nothing to do with server side operations. The client must make the decision. The parities must work together to solve the key distribution problem, but that has nothing to do with making the server usable.
@EJP - I certainly did not say, "trust all certificates". Perhaps I am missing something (or you are reading too much into things)... The OP has the certificate, and its acceptable to him. He wants to know how to trust it. I'm probably splitting hairs, but the cert does not need to be delivered offline. An out-of-band verification should be used, but that's not the same as "must be delivered to the client offline". He might even be the shop producing the server and the client, so he has the requisite a priori knowledge.
S
Syntle

I had the issue that I was passing a URL into a library which was calling url.openConnection(); I adapted jon-daniel's answer,

public class TrustHostUrlStreamHandler extends URLStreamHandler {

    private static final Logger LOG = LoggerFactory.getLogger(TrustHostUrlStreamHandler.class);

    @Override
    protected URLConnection openConnection(final URL url) throws IOException {

        final URLConnection urlConnection = new URL(url.getProtocol(), url.getHost(), url.getPort(), url.getFile()).openConnection();

        // adapated from
        // https://stackoverflow.com/questions/2893819/accept-servers-self-signed-ssl-certificate-in-java-client
        if (urlConnection instanceof HttpsURLConnection) {
            final HttpsURLConnection conHttps = (HttpsURLConnection) urlConnection;

            try {
                // Set up a Trust all manager
                final TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() {

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

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

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

                // Get a new SSL context
                final 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
                final HostnameVerifier allHostsValid = new HostnameVerifier() {
                    @Override
                    public boolean verify(final String hostname, final SSLSession session) {
                        return true;
                    }
                };

                // and set the hostname verifier.
                conHttps.setHostnameVerifier(allHostsValid);

            } catch (final NoSuchAlgorithmException e) {
                LOG.warn("Failed to override URLConnection.", e);
            } catch (final KeyManagementException e) {
                LOG.warn("Failed to override URLConnection.", e);
            }

        } else {
            LOG.warn("Failed to override URLConnection. Incorrect type: {}", urlConnection.getClass().getName());
        }

        return urlConnection;
    }

}

Using this class it is possible to create a new URL with:

trustedUrl = new URL(new URL(originalUrl), "", new TrustHostUrlStreamHandler());
trustedUrl.openConnection();

This has the advantage that it is localized and not replacing the default URL.openConnection.


Thanks a lot. It worked for me, was struggling to find an HTTPS GET client for my self-signing Spring Boot HTTPS server.
C
Christopher Schneider

The accepted answer is fine, but I'd like to add something to this as I was using IntelliJ on Mac and couldn't get it to work using the JAVA_HOME path variable.

It turns out Java Home was different when running the application from IntelliJ.

To figure out exactly where it is, you can just do System.getProperty("java.home") as that's where the trusted certificates are read from.


s
skanga

The accepted answer needs an Option 3

ALSO Option 2 is TERRIBLE. It should NEVER be used (esp. in production) since it provides a FALSE sense of security. Just use HTTP instead of Option 2.

OPTION 3

Use the self-signed certificate to make the Https connection.

Here is an example:

import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManagerFactory;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.URL;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.KeyStore;

/*
 * Use a SSLSocket to send a HTTP GET request and read the response from an HTTPS server.
 * It assumes that the client is not behind a proxy/firewall
 */

public class SSLSocketClientCert
{
    private static final String[] useProtocols = new String[] {"TLSv1.2"};
    public static void main(String[] args) throws Exception
    {
        URL inputUrl = null;
        String certFile = null;
        if(args.length < 1)
        {
            System.out.println("Usage: " + SSLSocketClient.class.getName() + " <url>");
            System.exit(1);
        }
        if(args.length == 1)
        {
            inputUrl = new URL(args[0]);
        }
        else
        {
            inputUrl = new URL(args[0]);
            certFile = args[1];
        }
        SSLSocket sslSocket = null;
        PrintWriter outWriter = null;
        BufferedReader inReader = null;
        try
        {
            SSLSocketFactory sslSocketFactory = getSSLSocketFactory(certFile);

            sslSocket = (SSLSocket) sslSocketFactory.createSocket(inputUrl.getHost(), inputUrl.getPort() == -1 ? inputUrl.getDefaultPort() : inputUrl.getPort());
            String[] enabledProtocols = sslSocket.getEnabledProtocols();
            System.out.println("Enabled Protocols: ");
            for(String enabledProtocol : enabledProtocols) System.out.println("\t" + enabledProtocol);

            String[] supportedProtocols = sslSocket.getSupportedProtocols();
            System.out.println("Supported Protocols: ");
            for(String supportedProtocol : supportedProtocols) System.out.println("\t" + supportedProtocol + ", ");

            sslSocket.setEnabledProtocols(useProtocols);

            /*
             * Before any data transmission, the SSL socket needs to do an SSL handshake.
             * We manually initiate the handshake so that we can see/catch any SSLExceptions.
             * The handshake would automatically  be initiated by writing & flushing data but
             * then the PrintWriter would catch all IOExceptions (including SSLExceptions),
             * set an internal error flag, and then return without rethrowing the exception.
             *
             * This means any error messages are lost, which causes problems here because
             * the only way to tell there was an error is to call PrintWriter.checkError().
             */
            sslSocket.startHandshake();
            outWriter = sendRequest(sslSocket, inputUrl);
            readResponse(sslSocket);
            closeAll(sslSocket, outWriter, inReader);
        }
        catch(Exception e)
        {
            e.printStackTrace();
        }
        finally
        {
            closeAll(sslSocket, outWriter, inReader);
        }
    }

    private static PrintWriter sendRequest(SSLSocket sslSocket, URL inputUrl) throws IOException
    {
        PrintWriter outWriter = new PrintWriter(new BufferedWriter(new OutputStreamWriter(sslSocket.getOutputStream())));
        outWriter.println("GET " + inputUrl.getPath() + " HTTP/1.1");
        outWriter.println("Host: " + inputUrl.getHost());
        outWriter.println("Connection: Close");
        outWriter.println();
        outWriter.flush();
        if(outWriter.checkError())        // Check for any PrintWriter errors
            System.out.println("SSLSocketClient: PrintWriter error");
        return outWriter;
    }

    private static void readResponse(SSLSocket sslSocket) throws IOException
    {
        BufferedReader inReader = new BufferedReader(new InputStreamReader(sslSocket.getInputStream()));
        String inputLine;
        while((inputLine = inReader.readLine()) != null)
            System.out.println(inputLine);
    }

    // Terminate all streams
    private static void closeAll(SSLSocket sslSocket, PrintWriter outWriter, BufferedReader inReader) throws IOException
    {
        if(sslSocket != null) sslSocket.close();
        if(outWriter != null) outWriter.close();
        if(inReader != null) inReader.close();
    }

    // Create an SSLSocketFactory based on the certificate if it is available, otherwise use the JVM default certs
    public static SSLSocketFactory getSSLSocketFactory(String certFile)
        throws CertificateException, KeyStoreException, IOException, NoSuchAlgorithmException, KeyManagementException
    {
        if (certFile == null) return (SSLSocketFactory) SSLSocketFactory.getDefault();
        Certificate certificate = CertificateFactory.getInstance("X.509").generateCertificate(new FileInputStream(new File(certFile)));

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

        return sslContext.getSocketFactory();
    }
}


V
VSK

This is not a solution to the complete problem but oracle has good detailed documentation on how to use this keytool. This explains how to

use keytool. generate certs/self signed certs using keytool. import generated certs to java clients.

https://docs.oracle.com/cd/E54932_01/doc.705/e54936/cssg_create_ssl_cert.htm#CSVSG178


u
user66660

Instead of using keytool as suggested by the top comment, on RHEL you can use update-ca-trust starting in newer versions of RHEL 6. You'll need to have the cert in pem format. Then

trust anchor <cert.pem>

Edit /etc/pki/ca-trust/source/cert.p11-kit and change "certificate category: other-entry" to "certificate category: authority". (Or use sed to do this in a script.) Then do

update-ca-trust

A couple caveats:

I couldn't find "trust" on my RHEL 6 server and yum didn't offer to install it. I ended up using it on an RHEL 7 server and copying the .p11-kit file over.

To make this work for you, you may need to do update-ca-trust enable. This will replace /etc/pki/java/cacerts with a symbolic link pointing to /etc/pki/ca-trust/extracted/java/cacerts. (So you might want to back up the former first.)

If your java client uses cacerts stored in some other location, you'll want to manually replace it with a symlink to /etc/pki/ca-trust/extracted/java/cacerts, or replace it with that file.


I
IL_Agent

The variant in Kotlin

    @SuppressLint("CustomX509TrustManager", "TrustAllX509TrustManager")
    fun ignoreSsl() {
        val trustAllCerts: Array<TrustManager> = arrayOf(
            object : X509TrustManager {
                override fun getAcceptedIssuers(): Array<X509Certificate>? = null
                override fun checkClientTrusted(certs: Array<X509Certificate?>?, authType: String?) {}
                override fun checkServerTrusted(certs: Array<X509Certificate?>?, authType: String?) {}
            })
        val sc = SSLContext.getInstance("SSL")
        sc.init(null, trustAllCerts, SecureRandom())
        HttpsURLConnection.setDefaultSSLSocketFactory(sc.socketFactory)
    }

c
comsky

Late to the party, you can do so by disabling it at the RestTemplate level. Note that this TrustStrategy will trust all certificates, and you disable hostname verification with NoopHostnameVerifier().

public RestTemplate getRestTemplate() throws KeyStoreException, NoSuchAlgorithmException, KeyManagementException {
    TrustStrategy acceptingTrustStrategy = (x509Certificates, s) -> true;
    SSLContext sslContext = org.apache.http.ssl.SSLContexts.custom().loadTrustMaterial(null, acceptingTrustStrategy).build();
    SSLConnectionSocketFactory csf = new SSLConnectionSocketFactory(sslContext, new NoopHostnameVerifier());
    CloseableHttpClient httpClient = HttpClients.custom().setSSLSocketFactory(csf).build();
    HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();
    requestFactory.setHttpClient(httpClient);
    return new RestTemplate(requestFactory);
}       

y
yuansu

Inspired by below annser, I find a way to trust self-signed and keep trust default CA.

    File file = new File(System.getProperty("java.home"), "lib/security/cacerts");
    KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
    keyStore.load(new FileInputStream(file), "changeit".toCharArray());


    InputStream resourceAsStream = getClass().getClassLoader().getResourceAsStream("testCer.cer");
    Certificate certificate = CertificateFactory.getInstance("X.509").generateCertificate(resourceAsStream);
    keyStore.setCertificateEntry("my-server-alias", certificate);

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

    SSLContext sslContext = SSLContexts.createDefault();
    sslContext.init(null, trustManagerFactory.getTrustManagers(), null);


    // check domain
    // SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(sslContext);

    // not check domain
    SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(sslContext,
            new String[]{"TLSv1","TLSv1.1","TLSv1.2","SSLv3"},null, NoopHostnameVerifier.INSTANCE);

    CloseableHttpClient httpClient = HttpClients.custom().setSSLSocketFactory(socketFactory).build();
    factory.setHttpClient(httpClient);
    RestTemplate restTemplate = new RestTemplate(factory);

d
devOp_in_hiding

This is the first ancient question with far too many answers where I think I can provide a more helpful idea: This option is what I would use if the server owner refuses to provide their certificate to me offline in a trustworthy way:

Retrieve the cert from the server itself (using command line tools instead of the browser) Add that cert to the java keystore to trust it. You will be displayed with the certificate's details to verify it.

# HOSTNAME_PORT is the host that you want to connect to - example: HOSTNAME_PORT=stackoverflow.com:443
HOSTNAME_PORT=hostname_part_of_url_without_https:port

# whatever you want to call the key within the Java key store
MY_KEY_ALIAS=the_key_I_added_with_help_from_stackoverflow

openssl s_client -showcerts -connect $HOSTNAME_PORT </dev/null 2>/dev/null|openssl x509 -outform PEM >mycertfile.pem
sudo keytool -trustcacerts -keystore $JAVA_HOME/jre/lib/security/cacerts/pki/java/cacerts -storepass changeit -importcert -alias $MY_KEY_ALIAS -file mycertfile.pem 

Type in yes when prompted, but only if you really trust the certificate displayed to you and want to add it to the global java keystore of your computer.

FYI:$JAVA_HOME/jre/lib/security/cacerts is in my case (CentOS 7) pointing at: /etc/pki/java/cacerts