サンドボックスモードで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
問題は、最近(1月19日または20日)Paypalがサンドボックスを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を有効にしません。
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は、2016年6月17日にすべてのHTTPS接続にTLS v1.2を要求するようにサービスを更新しています。その日以降、すべての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/
WebサービスまたはJavaのスタンドアロンアプリケーションからhttps要求を呼び出す場合は、次の行を使用して動作させます。
System.setProperty(“https.protocols”, “TLSv1,TLSv1.1,TLSv1.2”);
私は今週も同じ問題(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;
}
}
}
私の問題は、Java 7がインストールされていたため、Java 8と出来上がり、例外はもうありませんでした:)