Search code examples
javarestauthenticationoauth-2.0oauth

PKIX path building failed in OAuth Authentication in Java


I am trying to Authenticate in discogs:

https://www.discogs.com/developers/#page:authentication,header:authentication-oauth-flow

on the Point 2: SEND A GET REQUEST TO THE DISCOGS REQUEST TOKEN URL, I get this:

oauth_token=tnMYYwCBsvoecGyBsANXyVKQtICTdDnnzRPeGUfa
oauth_token_secret=xwHpnTRYNJIpdkkTJLMsfXECdHgXeQUrDjzmktPw
oauth_callback_confirmed=true

on the POINT 3: REDIRECT YOUR USER TO THE DISCOGS AUTHORIZE PAGE,

I've created this piece of code:

private static void redirectUserToAuthorizePage(String consumerKey) throws IOException {

    String yourUrl = "https://discogs.com/oauth/authorize?oauth_token=" + consumerKey;
    URL url = new URL(yourUrl);
    HttpURLConnection conn = (HttpURLConnection) url.openConnection();
    conn.setDoOutput(true);
    conn.setRequestMethod("GET");

    int statusCode = conn.getResponseCode();
    System.out.println("Status Code: " + statusCode);

    conn.disconnect();

}

But I got this error:

but I get this error:

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
    at sun.security.ssl.Alerts.getSSLException(Alerts.java:192)
    at sun.security.ssl.SSLSocketImpl.fatal(SSLSocketImpl.java:1949)
    at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:302)
    at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:296)
    at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1509)
    at sun.security.ssl.ClientHandshaker.processMessage(ClientHandshaker.java:216)
    at sun.security.ssl.Handshaker.processLoop(Handshaker.java:979)
    at sun.security.ssl.Handshaker.process_record(Handshaker.java:914)
    at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:1062)
    at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1375)
    at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1403)
    at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1387)
    at sun.net.www.protocol.https.HttpsClient.afterConnect(HttpsClient.java:559)
    at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(AbstractDelegateHttpsURLConnection.java:185)
    at sun.net.www.protocol.http.HttpURLConnection.getInputStream0(HttpURLConnection.java:1513)
    at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1441)
    at java.net.HttpURLConnection.getResponseCode(HttpURLConnection.java:480)
    at sun.net.www.protocol.https.HttpsURLConnectionImpl.getResponseCode(HttpsURLConnectionImpl.java:338)

Solution

  • The error provided indicates that your application is unable to establish a SSL secure communication with the remote server, discogs.com, because it is unable to find a valid certificate for that server among the ones configured to trust.

    Under the hood, HttpURLConnection will use Java Secure Socket Extension for establishing secure SSL communications.

    In order to solve the problem you have several options, mainly:

    • Run your application with the javax.net.ssl.trustStore and javax.net.ssl.trustStorePassword system properties pointing to a keystore and respectively password that contains your server certificates.
    • Include the server certificate in a trusted, default, keystore, one of:
      • <java-home>/lib/security/jssecacerts
      • <java-home>/lib/security/cacerts

    No matter the chosen mechanism, be sure that the desired keystore contains all the necessary certificates to trust the remote server, not only the SSL certificate itself, but all the certificates in the certificate chain.

    openssl provides an useful command that allows you to obtain all the certificates used in the SSL connection. In this case, it will provide the following information, among other:

    depth=0 O = Acme Co, CN = Kubernetes Ingress Controller Fake Certificate
    verify error:num=20:unable to get local issuer certificate
    verify return:1
    depth=0 O = Acme Co, CN = Kubernetes Ingress Controller Fake Certificate
    verify error:num=21:unable to verify the first certificate
    verify return:1
    ---
    Certificate chain
     0 s:/O=Acme Co/CN=Kubernetes Ingress Controller Fake Certificate
       i:/O=Acme Co/CN=Kubernetes Ingress Controller Fake Certificate
    -----BEGIN CERTIFICATE-----
    MIIDcDCCAligAwIBAgIRALyiewLACTbxSO/mUgPt2z8wDQYJKoZIhvcNAQELBQAw
    SzEQMA4GA1UEChMHQWNtZSBDbzE3MDUGA1UEAxMuS3ViZXJuZXRlcyBJbmdyZXNz
    IENvbnRyb2xsZXIgRmFrZSBDZXJ0aWZpY2F0ZTAeFw0yMTAzMTYyMjQ5MDVaFw0y
    MjAzMTYyMjQ5MDVaMEsxEDAOBgNVBAoTB0FjbWUgQ28xNzA1BgNVBAMTLkt1YmVy
    bmV0ZXMgSW5ncmVzcyBDb250cm9sbGVyIEZha2UgQ2VydGlmaWNhdGUwggEiMA0G
    CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDxBj7SvSJpus5+2s4HLhheJhmKEmcV
    MTFIVf3xRDfGpVFeYKAc/o8oB3/OXrqr16kuv2g+bE2d59qVri1uTXX/CRHM6G0E
    St/gEdUsGiO1e3VueYGkdgr//s7idEMD0tBHp9ITiR0XUFK75YdWCT6H24fetNzp
    bOSMImEeSQsRexKqwcCvM6l4rNshJQ1BVD7NOYPBO9BJuEKU0wCd8yDF20Ig6Qwh
    D8B6kRIfNIR1jaQbXGwsvowx4ZHtE4ETd5ftPvKFLfjTMRNFOenJMOeA9te3f2vA
    f4jm8furOytI7+sQhZqstxbwPV9OI96rilq4P+ZjP3CW9bfubdyE8ccHAgMBAAGj
    TzBNMA4GA1UdDwEB/wQEAwIFoDATBgNVHSUEDDAKBggrBgEFBQcDATAMBgNVHRMB
    Af8EAjAAMBgGA1UdEQQRMA+CDWluZ3Jlc3MubG9jYWwwDQYJKoZIhvcNAQELBQAD
    ggEBADFd9W49IsGKTm6WBSsSPLuKn1NFlQmfuRljcZyB01seN7N8fvX4OG/fD+/A
    g1nogwDbTl3g1AAyiU6iCKerZevolS2akLdbKwxg/olFRjgwdTN5668Dbz4o3MVY
    21AJJ9qO14Oxdu5x+3VhMnsuQdcOzdpwVPmNWIOGQMPD+sOZ8d1hqFp6+3gFQpIL
    4kEs2RhjZqwySYoQmBnURx8kbyrs3s/sH06v+CzJwVomf7amOaywJ/VuxmkF9JPp
    pxxmlHfbSzlWZGRC4K0ij1+91r6EALVUGgonQGY3FsN2+JTTfg8J9FGqvjb7F+BO
    PMPqU/pmCW5h8ej8eyNflagzLFE=
    -----END CERTIFICATE-----
    ---
    Server certificate
    subject=/O=Acme Co/CN=Kubernetes Ingress Controller Fake Certificate
    issuer=/O=Acme Co/CN=Kubernetes Ingress Controller Fake Certificate
    ---
    

    In this case, it seems that the server is using a self-signed certificate to identify itself. You new to configure Java to trust that certificate.

    Copy and paste the certificate text outputted in the command, including —–BEGIN CERTIFICATE—– and —–END CERTIFICATE—–, and save to a new file, discogs.pem, for example.

    In order to be imported in a Java keystore, it should be converted to DER format first. You can do that with openssl as well:

    openssl x509 -outform der -in discogs.pem -out discogs.der
    

    Then, import the certificate in a keystore. As indicated, you can import it in the default JVM kystore, or in a new one. Let's consider the later case and create a new keystore named, for example, discogs:

    keytool -import -keystore discogs -file discogs.der
    

    Provide a password of your convenience, let's assume changeme for example.

    Now, run the application with the above-mentioned system properties:

    java -Djavax.net.ssl.trustStore=discogs \
         -Djavax.net.ssl.trustStorePassword=changeme \
         your.Application
    

    If necessary, in order to troubleshoot the problem, you can use the javax.net.debug system property with an appropriate value, all or ssl:

    java -Djavax.net.debug=ssl \
         -Djavax.net.ssl.trustStore=discogs \
         -Djavax.net.ssl.trustStorePassword=password \
         your.Application
    

    Please, consider review this related SO question, it may be of help.