Paypal Sandbox API: javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure

18,717

Solution 1

The issue is that PayPal recently (19th or 20th of Jan) switched the sandbox to TLS 1.2 and do not support TLS 1 anymore. I guess that you are running on Java SE 7 or older. If you search the net a bit you will find this:

Although SunJSSE in the Java SE 7 release supports TLS 1.1 and TLS 1.2, neither version is enabled by default for client connections. Some servers do not implement forward compatibility correctly and refuse to talk to TLS 1.1 or TLS 1.2 clients. For interoperability, SunJSSE does not enable TLS 1.1 or TLS 1.2 by default for client connections.

Link

To enable TLS 1.2 you can use the following setting of the VM: -Dhttps.protocols=TLSv1.1,TLSv1.2 Then it will work.

If you want to see more details about the handshake you can use this VM option: -Djavax.net.debug=all

Then you may confirm that TLS 1.2 is disabled if you see something like this:

*** ClientHello, TLSv1

After the server response is read the exception will be thrown. Java 8 has no such problem because TLS 1.2 is enabled by default.

Currently only the sandbox is affected. You can read on PayPal's site:

PayPal is updating its services to require TLS v1.2 for all HTTPS connections on June 17, 2016. After that date, all TLS v1.0 and TLS v1.1 API connections will be refused.

Most likely this will be postponed even more.

You may reproduce the problem and experiment with this simple throw-away code:

URL sandbox = new URL("https://api.sandbox.paypal.com/v1/oauth2/token");
URLConnection yc = sandbox.openConnection();

BufferedReader in = new BufferedReader(new InputStreamReader(
    yc.getInputStream()));
String inputLine;
while ((inputLine = in.readLine()) != null)
   System.out.println(inputLine);
in.close();

Solution 2

http-bio-9090-exec-1, RECV TLSv1 ALERT: fatal, handshake_failure

Looks like you're using an older version of TLS. PayPal recently (a few days ago) made a change to their sandbox to require all connections be done over HTTP 1.1 and TLS 1.2. Try setting your code to use the newer version of TLS (1.2) and see if that helps. Apparently it's worked for a lot of people on here.

Link to the change description on PayPal dev blog

https://devblog.paypal.com/upcoming-security-changes-notice/

Solution 3

If you are calling https request from a web service or in a standalone application in JAVA, use the following line to make it work :

System.setProperty(“https.protocols”, “TLSv1,TLSv1.1,TLSv1.2”);

Solution 4

I have been dealing with the same problem (Paypal TLS 1.2 Vs JRE 1.6) this week. After some crazy hours, I managed to solve the problem using BouncyCastle and a some code I am not proud of (just as a patch. lasting solution will be upgrading to JRE 1.8).

BouncyCastle dependency:

<dependency>
    <groupId>org.bouncycastle</groupId>
    <artifactId>bcprov-jdk15on</artifactId>
    <version>1.54</version>
</dependency>

My working code:

package es.webtools.eencuesta.redsys.component.impl;

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.Socket;
import java.net.URL;
import java.net.URLDecoder;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;

import org.bouncycastle.crypto.tls.CertificateRequest;
import org.bouncycastle.crypto.tls.DefaultTlsClient;
import org.bouncycastle.crypto.tls.TlsAuthentication;
import org.bouncycastle.crypto.tls.TlsClientProtocol;
import org.bouncycastle.crypto.tls.TlsCredentials;

import es.webtools.eencuesta.common.util.HttpUtils;

public class PayPayAPIHandler {

    public static Map<String, String> doAPIRequest(Map<String, String> paramMap) {

    StringBuilder data = new StringBuilder();
    Iterator<Entry<String, String>> paramIt = paramMap.entrySet().iterator();
    while (paramIt.hasNext()) {
        Entry<String, String> param = paramIt.next();
        data.append(param.getKey()).append("=").append(HttpUtils.encodeUTF8(param.getValue()));
        if (paramIt.hasNext()) {
            data.append("&");
        }
    }

    try {
        URL url = new URL("https://api-3t.sandbox.paypal.com/nvp");

        // TODO #39 Utilizar HttpConnection (Java 8 implementa TLS 1.2, necesaria para comunicación con PayPal)
        java.security.SecureRandom secureRandom = new java.security.SecureRandom();
        Socket socket = new Socket(java.net.InetAddress.getByName(url.getHost()), 443);
        TlsClientProtocol protocol = new TlsClientProtocol(socket.getInputStream(), socket.getOutputStream(), secureRandom);
        DefaultTlsClient client = new DefaultTlsClient() {
            public TlsAuthentication getAuthentication() throws IOException {
                TlsAuthentication auth = new TlsAuthentication() {
                    // Capture the server certificate information!
                    public void notifyServerCertificate(org.bouncycastle.crypto.tls.Certificate serverCertificate) throws IOException {
                    }

                    public TlsCredentials getClientCredentials(CertificateRequest certificateRequest) throws IOException {
                        return null;
                    }
                };
                return auth;
            }
        };

        protocol.connect(client);
        java.io.OutputStream output2 = protocol.getOutputStream();
        output2.write(("POST " + url.getPath() + " HTTP/1.1\r\n").getBytes("UTF-8"));
        output2.write(("Host: " + url.getHost() + "\r\n").getBytes("UTF-8"));
        output2.write("Connection: close\r\n".getBytes("UTF-8")); // So the server will close socket immediately.
        output2.write(("Content-Length: " + data.length() + "\r\n").getBytes("UTF-8")); // So the server will close socket immediately.
        output2.write("Content-Type:text/plain; charset=UTF-8\r\n".getBytes("UTF-8")); // So the server will close socket immediately.
        output2.write("\r\n".getBytes("UTF-8")); // HTTP1.1 requirement: last line must be empty line.
        output2.write(data.toString().getBytes("UTF-8"));
        output2.flush();

        InputStream input2 = protocol.getInputStream();
        StringBuilder stringBuffer = new StringBuilder();
        try {
            InputStreamReader reader = new InputStreamReader(input2, "UTF-8");
            int ch;
            while ((ch = reader.read()) > -1) {
                stringBuffer.append((char) ch);
            }
            reader.close();
        } catch (Exception e) {
            // Log some messages...
        }

        Map<String, String> result = new HashMap<String, String>();
        String[] lines = stringBuffer.toString().split("\r\n");
        String paramsLine = "";
        for (int i = 0; i < lines.length; i++) {
            if (lines[i].equals("")) {
                paramsLine = lines[i + 1];
                i = lines.length;
            }
        }

        // El contenido de la respuesta vendrá después del salto de linea
        for (String param : paramsLine.split("&")) {
            String[] keyValue = param.split("=");
            result.put(keyValue[0], URLDecoder.decode(keyValue[1], "UTF-8"));
        }

        return result;
    } catch (Exception e) {
        // Log some messages....
        return null;
    }
}

}

Solution 5

My problem was that I had java 7 installed so I upgraded to java 8 and voila, the exception was not there anymore :)

Share:
18,717
Syam Danda
Author by

Syam Danda

Updated on June 05, 2022

Comments

  • Syam Danda
    Syam Danda almost 2 years

    I am getting an exception while using PayPal MassPay API in sandbox mode.

    The same code worked earlier, now it is giving me an SSLHandshakeException. I think this is because of PayPal's latest SSL certification updates. Could someone can help me fix this issue?

    The following is my exception log:

    http-bio-9090-exec-1, READ: TLSv1 Alert, length = 2
    http-bio-9090-exec-1, RECV TLSv1 ALERT:  fatal, handshake_failure
    http-bio-9090-exec-1, called closeSocket()
    http-bio-9090-exec-1, handling exception: javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure
    com.paypal.core.rest.PayPalRESTException: Received fatal alert: handshake_failure
        at com.paypal.core.rest.PayPalResource.execute(PayPalResource.java:374)
        at com.paypal.core.rest.PayPalResource.configureAndExecute(PayPalResource.java:225)
        at com.paypal.sdk.openidconnect.Tokeninfo.createFromAuthorizationCode(Tokeninfo.java:245)
        at com.oldwallet.controllers.CouponPaymentController.redeemed(CouponPaymentController.java:321)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:606)
        at org.springframework.web.method.support.InvocableHandlerMethod.invoke(InvocableHandlerMethod.java:215)
        at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:132)
        at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:104)
        at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandleMethod(RequestMappingHandlerAdapter.java:745)
        at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:686)
        at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:80)
        at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:925)
        at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:856)
        at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:953)
        at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:844)
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:621)
        at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:829)
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:728)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:305)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
        at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:222)
        at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:123)
        at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:472)
        at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:171)
        at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:99)
        at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:953)
        at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118)
        at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:408)
        at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1008)
        at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:589)
        at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:310)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
        at java.lang.Thread.run(Thread.java:745)
    Caused by: javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure
        at sun.security.ssl.Alerts.getSSLException(Alerts.java:192)
        at sun.security.ssl.Alerts.getSSLException(Alerts.java:154)
        at sun.security.ssl.SSLSocketImpl.recvAlert(SSLSocketImpl.java:1959)
        at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:1077)
        at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1312)
        at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1339)
        at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1323)
        at sun.net.www.protocol.https.HttpsClient.afterConnect(HttpsClient.java:563)
        at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(AbstractDelegateHttpsURLConnection.java:185)
        at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1300)
        at java.net.HttpURLConnection.getResponseCode(HttpURLConnection.java:468)
        at sun.net.www.protocol.https.HttpsURLConnectionImpl.getResponseCode(HttpsURLConnectionImpl.java:338)
        at com.paypal.core.HttpConnection.execute(HttpConnection.java:93)
        at com.paypal.core.rest.PayPalResource.execute(PayPalResource.java:367)
        ... 36 more
    
  • Simon Mokhele
    Simon Mokhele over 7 years
    This actually worked. I just added System.setProperty("https.protocols", "TLSv1.1,TLSv1.2,SSLv3,SSLv2Hello"); This was the solution for me