PayPal в песочнице API-интерфейс: класса javax.чистая.протокол SSL.SSLHandshakeException: получил роковое предупреждение: ошибка квитирования
Я получаю исключение при использовании PayPal MassPay API
в песочнице.
Тот же код работал раньше, теперь он дает мне SSLHandshakeException
. Я думаю, что это из-за последних обновлений сертификации SSL PayPal. Может ли кто - нибудь помочь мне решить эту проблему?
Ниже приведен мой журнал исключений:
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
5 ответов:
Проблема в том, что PayPal недавно (19 или 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:
PayPal обновляет свои сервисы, требуя TLS v1. 2 для всех HTTPS соединения 17 июня 2016 года. После этой даты все TLS v1. 0 и TLS В1.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 ALERT: fatal, handshake_failure
Похоже, вы используете более старую версию TLS. PayPal недавно (несколько дней назад) внес изменения в свою песочницу, требуя, чтобы все соединения выполнялись через HTTP 1.1 и TLS 1.2. Попробуйте настроить свой код на использование более новой версии TLS (1.2) и посмотрите, поможет ли это. Очевидно, это сработало для многих людей здесь.
Ссылка на описание изменений в блоге PayPal dev
Https://devblog.paypal.com/upcoming-security-changes-notice/
На этой неделе я имел дело с той же проблемой (Paypal 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; } } }