Paypal Sandbox API:javax.net.ssl.SSLHandshakeException:收到致命警报:handshake_failure
我在沙盒模式下使用PayPal MassPay API
时收到异常。Paypal Sandbox API:javax.net.ssl.SSLHandshakeException:收到致命警报:handshake_failure
相同的代码工作较早,现在它给了我一个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
的问题是,贝宝日前(19日或1月20日)切换的沙箱TLS 1.2,不支持TLS 1了。我猜你正在运行Java SE 7或更早的版本。如果您搜索的净一点,你会发现这一点:
虽然SunJSSE在Java SE 7的版本支持TLS 1.1和TLS 1.2 ,既不版本默认情况下,客户端连接启用。某些服务器不能正确实现向前兼容性,并且拒绝与TLS 1.1或TLS 1.2客户端进行通信。对于互操作性, SunJSSE默认不会为客户端 连接启用TLS 1.1或TLS 1.2。
要启用TLS 1.2,您可以使用虚拟机的以下设置:-Dhttps.protocols=TLSv1.1,TLSv1.2
然后它会奏效。
如果您想了解您可以使用此VM选项握手的详细信息:如果你看到这样的事情-Djavax.net.debug=all
然后你可以确认TLS 1.2无效:
*** ClientHello, TLSv1
读取服务器响应后,将引发异常。 Java 8没有这样的问题,因为默认情况下启用了TLS 1.2。
目前只有沙箱受到影响。您可以在PayPal的网站上写着:
贝宝正在更新其服务要求TLS V1.2所有HTTPS对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();
HTTP-BIO-9090-EXEC-1,RECV的TLSv1警告:致命,handshake_failure
看起来像您使用的是旧版本的TLS。 PayPal最近(几天前)对他们的沙盒进行了更改,要求通过HTTP 1.1和TLS 1.2完成所有连接。尝试设置您的代码以使用更新版本的TLS(1.2)并查看是否有帮助。显然这对很多人来说都很合适。
链接到的变化说明在PayPal开发博客
https://devblog.paypal.com/upcoming-security-changes-notice/
我一直在处理同样的问题(贝宝TLS 1.2 Vs的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;
}
}
}
如果要调用HTTPS从Web服务或Java中的一个独立的应用程序请求,请使用以下行使它工作:
System.setProperty(“https.protocols”, “TLSv1,TLSv1.1,TLSv1.2”);
我的问题是,我有java 7安装,所以我升级到jav一个8和瞧,异常不在那里:)
不,不工作 – maxivis
这实际上工作。我刚刚添加了'System.setProperty(“https.protocols”,“TLSv1.1,TLSv1.2,SSLv3,SSLv2Hello”);' 这是我的解决方案 –