【问题标题】:Paypal Sandbox API: javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failurePaypal 沙盒 API:javax.net.ssl.SSLHandshakeException:收到致命警报:handshake_failure
【发布时间】:2016-04-30 00:28:51
【问题描述】:

在沙盒模式下使用 PayPal MassPay API 时遇到异常。

同样的代码之前工作过,现在它给了我一个SSLHandshakeException。我认为这是因为 PayPal 最新的 SSL 认证更新。有人可以帮我解决这个问题吗?

以下是我的异常日志:

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

【问题讨论】:

    标签: java paypal ssl-certificate paypal-sandbox masspay


    【解决方案1】:

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

    您使用的似乎是旧版本的 TLS。 PayPal 最近(几天前)对其沙箱进行了更改,要求所有连接都通过 HTTP 1.1 和 TLS 1.2 完成。尝试将您的代码设置为使用较新版本的 TLS (1.2),看看是否有帮助。显然它对这里的很多人都有效。

    链接到 PayPal 开发博客上的更改说明

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

    【讨论】:

      【解决方案2】:

      问题是 PayPal 最近(1 月 19 日或 20 日)将沙箱切换到 TLS 1.2,不再支持 TLS 1。我猜你是在 Java SE 7 或更早版本上运行的。如果你在网上搜索一下,你会发现:

      尽管 Java SE 7 版本中的 SunJSSE 支持 TLS 1.1 和 TLS 1.2,默认情况下,这两个版本都没有为客户端连接启用。一些服务器没有正确实现前向兼容性,并且 拒绝与 TLS 1.1 或 TLS 1.2 客户端通信。为了互操作性, SunJSSE 默认不为客户端启用 TLS 1.1 或 TLS 1.2 连接。

      Link

      要启用 TLS 1.2,您可以使用 VM 的以下设置:-Dhttps.protocols=TLSv1.1,TLSv1.2 然后它将起作用。

      如果您想查看有关握手的更多详细信息,可以使用此 VM 选项:-Djavax.net.debug=all

      如果您看到以下内容,则可以确认 TLS 1.2 已禁用:

      *** ClientHello, TLSv1

      读取服务器响应后,将抛出异常。 Java 8 没有这个问题,因为默认启用了 TLS 1.2。

      目前只有沙盒受到影响。您可以在 PayPal 的网站上阅读:

      PayPal 正在更新其服务以要求所有 HTTPS 都使用 TLS v1.2 2016 年 6 月 17 日的连接。在该日期之后,所有 TLS v1.0 和 TLS v1.1 API 连接将被拒绝。

      这很可能会被进一步推迟。

      您可以重现问题并使用这个简单的一次性代码进行试验:

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

      【讨论】:

        【解决方案3】:

        本周我一直在处理同样的问题(Paypal TLS 1.2 与 JRE 1.6)。经过疯狂的几个小时后,我设法使用 BouncyCastle 和一些我并不引以为豪的代码解决了这个问题(就像一个补丁一样。持久的解决方案将升级到 JRE 1.8)。

        BouncyCastle 依赖:

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

        我的工作代码:

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

        【讨论】:

          【解决方案4】:

          如果您从 Web 服务或 JAVA 中的独立应用程序中调用 https 请求,请使用以下行使其工作:

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

          【讨论】:

          • 这确实有效。我刚刚添加了System.setProperty("https.protocols", "TLSv1.1,TLSv1.2,SSLv3,SSLv2Hello");这就是我的解决方案
          【解决方案5】:

          我的问题是我安装了 java 7,所以我升级到 java 8,瞧,这个异常不再存在了 :)

          【讨论】:

            猜你喜欢
            • 2012-03-18
            • 2015-12-04
            • 2012-10-15
            • 2018-02-09
            • 2019-01-02
            • 2018-08-10
            • 1970-01-01
            相关资源
            最近更新 更多