My task is to establish a connection to JIRA in java using RESTAPI. I’m facing an error with the SSL security certificate. I have tried many times and looked on google, but I didn’t find any solution to my problem. Can anyone help me to fix this error?
APOD.java
package com.jiraconnection; import com.fasterxml.jackson.annotation.JsonProperty; public class APOD { public final String expand; public final String id; public final String key; public final String self; public APOD(@JsonProperty("expand") String expand, @JsonProperty("id") String id, @JsonProperty("key") String key, @JsonProperty("self") String self) { this.expand = expand; this.id = id; this.key = key; this.self = self; } }
JavaHttpURLConnectionDemo.java
import com.fasterxml.jackson.databind.ObjectMapper; import java.io.IOException; import java.io.InputStream; import java.net.HttpURLConnection; import java.net.URL; public class JavaHttpURLConnectionDemo { public static void main(String[] args) throws IOException { // Create a neat value object to hold the URL URL url = new URL("https://jira.atlassian.com/rest/api/latest/issue/JRA-9"); // Open a connection(?) on the URL(?) and cast the response(??) HttpsURLConnection connection = (HttpsURLConnection) url.openConnection(); // Now it's "open", we can set the request method, headers etc. connection.setRequestProperty("accept", "application/json"); // This line makes the request InputStream responseStream = connection.getInputStream(); // Manually converting the response body InputStream to APOD using Jackson ObjectMapper mapper = new ObjectMapper(); APOD apod = mapper.readValue(responseStream, APOD.class); // Finally we have the response System.out.println(apod.expand); } }
Error
Exception in thread "main" javax.net.ssl.SSLHandshakeException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target at java.base/sun.security.ssl.Alert.createSSLException(Alert.java:131) at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:324) at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:267) at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:262) at java.base/sun.security.ssl.CertificateMessage$T13CertificateConsumer.checkServerCerts(CertificateMessage.java:1340) at java.base/sun.security.ssl.CertificateMessage$T13CertificateConsumer.onConsumeCertificate(CertificateMessage.java:1215) at java.base/sun.security.ssl.CertificateMessage$T13CertificateConsumer.consume(CertificateMessage.java:1158)
Advertisement
Answer
HttpsURLConnection
is using by default the JDK trusted certificates to validate the server certificate whether it is known and trusted. If it is present over there it won’t throw a SSLHandshakeException
Jira has currently the following certificate chain:
You can easily verify whether your HttpsURLConnection
has the trusted certificate by adding a breakpoint after initializing the HttpsURLConnection
See below for an example:
So my assumption is that in your case the certificate is not present in the JDK truststore. What you can do is extract the Jira certificate, just like shown here: Using openssl to get the certificate from a server afterwords either import it into cacert
file here: $JAVA_HOME/lib/security/cacerts
. But I think it will be better to provide a custom ssl configuration to your HttpsUrlConnection as it would be more maintainable in my opinion. But either options will work.
So the following code snippet is for option 2 when using custom trustore:
Option 2
import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLSocketFactory; import javax.net.ssl.TrustManagerFactory; import java.io.BufferedReader; import java.io.InputStream; import java.io.InputStreamReader; import java.net.URL; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.security.KeyStore; import java.util.Objects; import java.util.stream.Collectors; public class App { public static void main(String[] args) throws Exception { Path trustStorePath = Paths.get("/path/to/your/truststore.jks"); char[] trustStorePassword = "my-password".toCharArray(); KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType()); try(InputStream inputStream = Objects.requireNonNull(Files.newInputStream(trustStorePath))) { trustStore.load(inputStream, trustStorePassword); } TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); trustManagerFactory.init(trustStore); SSLContext sslContext = SSLContext.getInstance("TLS"); sslContext.init(null, trustManagerFactory.getTrustManagers(), null); SSLSocketFactory socketFactory = sslContext.getSocketFactory(); URL url = new URL("https://jira.atlassian.com/rest/api/latest/issue/JRA-9"); HttpsURLConnection connection = (HttpsURLConnection) url.openConnection(); connection.setSSLSocketFactory(socketFactory); connection.setRequestProperty("accept", "application/json"); try(InputStream inputStream = connection.getInputStream(); InputStreamReader inputStreamReader = new InputStreamReader(inputStream); BufferedReader bufferedReader = new BufferedReader(inputStreamReader)) { String responseBody = bufferedReader.lines().collect(Collectors.joining(System.lineSeparator())); System.out.println(responseBody); } } }
Or you can do everything inline without the custom truststore from your filesystem as shown below. In this way you have the the root ca as pem, which is DigiCert High Assurance EV ROOT CA
and load it programatically into your in memory truststore.
Option 3
import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLSocketFactory; import javax.net.ssl.TrustManagerFactory; import java.io.BufferedInputStream; import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.InputStream; import java.io.InputStreamReader; import java.net.URL; import java.security.KeyStore; import java.security.cert.Certificate; import java.security.cert.CertificateFactory; import java.util.Arrays; import java.util.Base64; import java.util.UUID; import java.util.stream.Collectors; public class App { public static void main(String[] args) throws Exception { String atlassian = "" + "MIIFdzCCBP6gAwIBAgIQAgzZlKL4HKlpT4RkDXUi8TAKBggqhkjOPQQDAzBWMQsw" + "CQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMTAwLgYDVQQDEydEaWdp" + "Q2VydCBUTFMgSHlicmlkIEVDQyBTSEEzODQgMjAyMCBDQTEwHhcNMjIwNTEwMDAw" + "MDAwWhcNMjMwNjEwMjM1OTU5WjBuMQswCQYDVQQGEwJBVTEYMBYGA1UECBMPTmV3" + "IFNvdXRoIFdhbGVzMQ8wDQYDVQQHEwZTeWRuZXkxGjAYBgNVBAoTEUF0bGFzc2lh" + "biBQdHkgTHRkMRgwFgYDVQQDDA8qLmF0bGFzc2lhbi5jb20wWTATBgcqhkjOPQIB" + "BggqhkjOPQMBBwNCAAR7p4KtlAjEKMIH66rdbCXtkR5nO20hqZco8B/L+EuJ9mqJ" + "PT4dmaDR8OZWzlLXfqiKKhtxuPckC5dtwns4kXyAo4IDlDCCA5AwHwYDVR0jBBgw" + "FoAUCrwIKReMpTlteg7OM8cus+37w3owHQYDVR0OBBYEFMw8XjgJmgk4VGCBWOyG" + "Lr/FMrRZMCkGA1UdEQQiMCCCDyouYXRsYXNzaWFuLmNvbYINYXRsYXNzaWFuLmNv" + "bTAOBgNVHQ8BAf8EBAMCB4AwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMC" + "MIGbBgNVHR8EgZMwgZAwRqBEoEKGQGh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9E" + "aWdpQ2VydFRMU0h5YnJpZEVDQ1NIQTM4NDIwMjBDQTEtMS5jcmwwRqBEoEKGQGh0" + "dHA6Ly9jcmw0LmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRMU0h5YnJpZEVDQ1NIQTM4" + "NDIwMjBDQTEtMS5jcmwwPgYDVR0gBDcwNTAzBgZngQwBAgIwKTAnBggrBgEFBQcC" + "ARYbaHR0cDovL3d3dy5kaWdpY2VydC5jb20vQ1BTMIGFBggrBgEFBQcBAQR5MHcw" + "JAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBPBggrBgEFBQcw" + "AoZDaHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VExTSHlicmlk" + "RUNDU0hBMzg0MjAyMENBMS0xLmNydDAJBgNVHRMEAjAAMIIBgQYKKwYBBAHWeQIE" + "AgSCAXEEggFtAWsAdwCt9776fP8QyIudPZwePhhqtGcpXc+xDCTKhYY069yCigAA" + "AYCt4OEbAAAEAwBIMEYCIQC37AW4L7CCrKn0+kTydWf3zn6tdkFOg/ZI3mUU4/P3" + "CwIhAOnIlT0eX2nzpr6+d3GReTVlVf5+coiyYsSOJWOANM2ZAHcANc8ZG7+xbFe/" + "D61MbULLu7YnICZR6j/hKu+oA8M71kwAAAGAreDg7wAABAMASDBGAiEAj67xO2t2" + "OVtwSjLdsD8RknexjRqu+Ifwp5wO/2p8a84CIQDw9OKwzRnQ4cxYPKPrIYGm5hbH" + "KfVwcBMmo0u0XQ2YlQB3ALNzdwfhhFD4Y4bWBancEQlKeS2xZwwLh9zwAw55NqWa" + "AAABgK3g4SEAAAQDAEgwRgIhAP/l3SLBl5/9RHQvd5GjApgGAne4J/XnA68l/vQp" + "x7jHAiEAxWnEzde1lf4a1kMuFUyc6fBUE88GVb+zC9rjv4KCDcQwCgYIKoZIzj0E" + "AwMDZwAwZAIwFcy1X+o/HkXrM8rFdcjBGCieA0oBeRSSifale32U36xquKPBtSvm" + "2g/HAZh2N3DDAjBM4zmAiD0WTA0o3Fnh03mIwP/98RqXvjiDUL/bzovejseo8eRp" + "FDjNl90IcJuAoGc="; String digicertCa = "" + "MIIEFzCCAv+gAwIBAgIQB/LzXIeod6967+lHmTUlvTANBgkqhkiG9w0BAQwFADBh" + "MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3" + "d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD" + "QTAeFw0yMTA0MTQwMDAwMDBaFw0zMTA0MTMyMzU5NTlaMFYxCzAJBgNVBAYTAlVT" + "MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxMDAuBgNVBAMTJ0RpZ2lDZXJ0IFRMUyBI" + "eWJyaWQgRUNDIFNIQTM4NCAyMDIwIENBMTB2MBAGByqGSM49AgEGBSuBBAAiA2IA" + "BMEbxppbmNmkKaDp1AS12+umsmxVwP/tmMZJLwYnUcu/cMEFesOxnYeJuq20ExfJ" + "qLSDyLiQ0cx0NTY8g3KwtdD3ImnI8YDEe0CPz2iHJlw5ifFNkU3aiYvkA8ND5b8v" + "c6OCAYIwggF+MBIGA1UdEwEB/wQIMAYBAf8CAQAwHQYDVR0OBBYEFAq8CCkXjKU5" + "bXoOzjPHLrPt+8N6MB8GA1UdIwQYMBaAFAPeUDVW0Uy7ZvCj4hsbw5eyPdFVMA4G" + "A1UdDwEB/wQEAwIBhjAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwdgYI" + "KwYBBQUHAQEEajBoMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5j" + "b20wQAYIKwYBBQUHMAKGNGh0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdp" + "Q2VydEdsb2JhbFJvb3RDQS5jcnQwQgYDVR0fBDswOTA3oDWgM4YxaHR0cDovL2Ny" + "bDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0R2xvYmFsUm9vdENBLmNybDA9BgNVHSAE" + "NjA0MAsGCWCGSAGG/WwCATAHBgVngQwBATAIBgZngQwBAgEwCAYGZ4EMAQICMAgG" + "BmeBDAECAzANBgkqhkiG9w0BAQwFAAOCAQEAR1mBf9QbH7Bx9phdGLqYR5iwfnYr" + "6v8ai6wms0KNMeZK6BnQ79oU59cUkqGS8qcuLa/7Hfb7U7CKP/zYFgrpsC62pQsY" + "kDUmotr2qLcy/JUjS8ZFucTP5Hzu5sn4kL1y45nDHQsFfGqXbbKrAjbYwrwsAZI/" + "BKOLdRHHuSm8EdCGupK8JvllyDfNJvaGEwwEqonleLHBTnm8dqMLUeTF0J5q/hos" + "Vq4GNiejcxwIfZMy0MJEGdqN9A57HSgDKwmKdsp33Id6rHtSJlWncg+d0ohP/rEh" + "xRqhqjn1VtvChMQ1H3Dau0bwhr9kAMQ+959GG50jBbl9s08PqUU643QwmA=="; String digicertRootCa = "" + "MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBh" + "MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3" + "d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD" + "QTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAwMDAwMDBaMGExCzAJBgNVBAYTAlVT" + "MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j" + "b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkqhkiG" + "9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsB" + "CSDMAZOnTjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97" + "nh6Vfe63SKMI2tavegw5BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt" + "43C/dxC//AH2hdmoRBBYMql1GNXRor5H4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7P" + "T19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y7vrTC0LUq7dBMtoM1O/4" + "gdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQABo2MwYTAO" + "BgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbR" + "TLtm8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUw" + "DQYJKoZIhvcNAQEFBQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/Esr" + "hMAtudXH/vTBH1jLuG2cenTnmCmrEbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg" + "06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIttep3Sp+dWOIrWcBAI+0tKIJF" + "PnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886UAb3LujEV0ls" + "YSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk" + "CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4="; KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType()); trustStore.load(null); for (String certificateContent : Arrays.asList(atlassian, digicertCa, digicertRootCa)) { byte[] decodedCertificate = Base64.getDecoder().decode(certificateContent); try(ByteArrayInputStream certificateAsInputStream = new ByteArrayInputStream(decodedCertificate); BufferedInputStream bufferedCertificateStream = new BufferedInputStream(certificateAsInputStream)) { CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509"); Certificate certificate = certificateFactory.generateCertificate(bufferedCertificateStream); trustStore.setCertificateEntry(UUID.randomUUID().toString(), certificate); } } TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); trustManagerFactory.init(trustStore); SSLContext sslContext = SSLContext.getInstance("TLS"); sslContext.init(null, trustManagerFactory.getTrustManagers(), null); SSLSocketFactory socketFactory = sslContext.getSocketFactory(); URL url = new URL("https://jira.atlassian.com/rest/api/latest/issue/JRA-9"); HttpsURLConnection connection = (HttpsURLConnection) url.openConnection(); connection.setSSLSocketFactory(socketFactory); connection.setRequestProperty("accept", "application/json"); try(InputStream inputStream = connection.getInputStream(); InputStreamReader inputStreamReader = new InputStreamReader(inputStream); BufferedReader bufferedReader = new BufferedReader(inputStreamReader)) { String responseBody = bufferedReader.lines().collect(Collectors.joining(System.lineSeparator())); System.out.println(responseBody); } } }
Last resort would be disable the certificate validation, but I would not recommend that. Below is an example for that:
Option 4
import java.io.BufferedReader; import java.io.InputStream; import java.io.InputStreamReader; import java.net.Socket; import java.net.URL; import java.security.cert.X509Certificate; import java.util.stream.Collectors; import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLEngine; import javax.net.ssl.SSLSocketFactory; import javax.net.ssl.TrustManager; import javax.net.ssl.X509ExtendedTrustManager; public class App { public static void main(String[] args) throws Exception { X509ExtendedTrustManager unsafeTrustManager = new X509ExtendedTrustManager() { @Override public void checkClientTrusted(X509Certificate[] x509Certificates, String s, Socket socket) {} @Override public void checkServerTrusted(X509Certificate[] x509Certificates, String s, Socket socket) {} @Override public void checkClientTrusted(X509Certificate[] x509Certificates, String s, SSLEngine sslEngine) {} @Override public void checkServerTrusted(X509Certificate[] x509Certificates, String s, SSLEngine sslEngine) {} @Override public void checkClientTrusted(X509Certificate[] x509Certificates, String s) {} @Override public void checkServerTrusted(X509Certificate[] x509Certificates, String s) {} @Override public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[0]; } }; SSLContext sslContext = SSLContext.getInstance("TLS"); sslContext.init(null, new TrustManager[]{ unsafeTrustManager }, null); SSLSocketFactory socketFactory = sslContext.getSocketFactory(); URL url = new URL("https://jira.atlassian.com/rest/api/latest/issue/JRA-9"); HttpsURLConnection connection = (HttpsURLConnection) url.openConnection(); connection.setSSLSocketFactory(socketFactory); connection.setRequestProperty("accept", "application/json"); try(InputStream inputStream = connection.getInputStream(); InputStreamReader inputStreamReader = new InputStreamReader(inputStream); BufferedReader bufferedReader = new BufferedReader(inputStreamReader)) { String responseBody = bufferedReader.lines().collect(Collectors.joining(System.lineSeparator())); System.out.println(responseBody); } } }