java Paypal Sandbox API:javax.net.ssl.SSLHandshakeException:收到致命警报:handshake_failure
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/34963083/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Paypal Sandbox API: javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure
提问by Syam Danda
I am getting an exception while using PayPal MassPay APIin sandbox mode.
PayPal MassPay API在沙盒模式下使用时出现异常。
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?
相同的代码之前工作过,现在它给了我一个SSLHandshakeException. 我认为这是因为 PayPal 的最新 SSL 认证更新。有人可以帮我解决这个问题吗?
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
回答by Lachezar Balev
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:
问题是 PayPal 最近(1 月 19 日或 20 日)将沙箱切换到 TLS 1.2,不再支持 TLS 1。我猜您正在运行 Java SE 7 或更早版本。如果你稍微搜索一下网络,你会发现:
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.
尽管 Java SE 7 发行版中的 SunJSSE 支持 TLS 1.1 和 TLS 1.2,但默认情况下这两个版本都未为客户端连接启用。一些服务器没有正确实现前向兼容性并拒绝与 TLS 1.1 或 TLS 1.2 客户端对话。对于互操作性,默认情况下,SunJSSE 不会为客户端连接启用 TLS 1.1 或 TLS 1.2。
To enable TLS 1.2 you can use the following setting of the VM: -Dhttps.protocols=TLSv1.1,TLSv1.2Then it will work.
要启用 TLS 1.2,您可以使用 VM 的以下设置:-Dhttps.protocols=TLSv1.1,TLSv1.2然后它将起作用。
If you want to see more details about the handshake you can use this VM option: -Djavax.net.debug=all
如果您想查看有关握手的更多详细信息,可以使用此 VM 选项: -Djavax.net.debug=all
Then you may confirm that TLS 1.2 is disabled if you see something like this:
然后,如果您看到以下内容,则可以确认 TLS 1.2 已禁用:
*** ClientHello, TLSv1
*** 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.
读取服务器响应后,将抛出异常。Java 8 没有这样的问题,因为默认启用了 TLS 1.2。
Currently only the sandbox is affected. You can read on PayPal's site:
目前只有沙箱受到影响。您可以在 PayPal 的网站上阅读:
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.
2016 年 6 月 17 日,PayPal 将更新其服务,要求所有 HTTPS 连接都使用 TLS v1.2。在该日期之后,所有 TLS v1.0 和 TLS v1.1 API 连接都将被拒绝。
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();
回答by 0kay
http-bio-9090-exec-1, RECV TLSv1 ALERT: fatal, handshake_failure
http-bio-9090-exec-1,RECV TLSv1 警报:致命,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.
看起来您使用的是旧版本的 TLS。PayPal 最近(几天前)对其沙箱进行了更改,要求所有连接都通过 HTTP 1.1 和 TLS 1.2 完成。尝试将您的代码设置为使用较新版本的 TLS (1.2),看看是否有帮助。显然,它对这里的很多人都有效。
Link to the change description on PayPal dev blog
链接到 PayPal 开发博客上的更改说明
https://devblog.paypal.com/upcoming-security-changes-notice/
https://devblog.paypal.com/upcoming-security-changes-notice/
回答by Basa
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).
本周我一直在处理同样的问题(Paypal TLS 1.2 Vs JRE 1.6)。经过几个疯狂的小时后,我设法使用 BouncyCastle 和一些我不引以为豪的代码解决了这个问题(就像一个补丁。持久的解决方案将升级到 JRE 1.8)。
BouncyCastle dependency:
BouncyCastle 依赖项:
<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;
}
}
}
回答by Rohit S
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 :
如果您从 Web 服务或 JAVA 中的独立应用程序调用 https 请求,请使用以下行使其工作:
System.setProperty(“https.protocols”, “TLSv1,TLSv1.1,TLSv1.2”);
回答by joeabala
My problem was that I had java 7 installed so I upgraded to java 8 and voila, the exception was not there anymore :)
我的问题是我安装了 java 7,所以我升级到 java 8,瞧,异常不再存在:)

