我们在android中编写了客户端应用程序,它使用HttpSurlConnectionAPI与https服务器连接。由于Poodle漏洞,我们需要在调用任何请求时从启用的协议列表中禁用SSLv3。
我们遵循了oracle提供的指导原则
并在调用url连接之前添加了以下行
java.lang.System.setProperty("https.protocols", "TLSv1");
此解决方案与普通java程序配合使用效果良好
当我们尝试连接一个只在SSLv3协议上工作的服务器时,我们得到了SSLHandShakeException
。
但令人担忧的是:同样的修复程序不适用于Android。我错过了什么,还是我应该尝试另一种方法为Android?请建议。
起初,我尝试了Bhavit S. Sengar的答案,它适用于大多数情况。但有时甚至当SSLv3协议从Android 4.4.4设备上的启用协议中删除时也会出现问题。所以汉斯-克里斯托夫·施泰纳的NetCipher库非常适合解决这个问题,我可以测试它。
我们使用jsoup在不同的服务器上进行大量的web抓取,因此无法设置HttpsURLConnection connection=NetCipher。getHttpsURLConnection(源URL)
。如果使用OkHttp,我假设这是相同的问题。
我们找到的最佳解决方案是设置
信息。瓜迪亚项目。网络密码。客户TlsOnlySocketFactory
来自NetCipher,作为静态块中的默认SSLSocketFactory
。因此,它是为我们的应用程序的整个运行时设置的:
SSLContext sslcontext = SSLContext.getInstance("TLSv1");
sslcontext.init(null, null, null);
SSLSocketFactory noSSLv3Factory = new TlsOnlySocketFactory(sc.getSocketFactory());
HttpsURLConnection.setDefaultSSLSocketFactory(noSSLv3Factory);
如果您想查看完整的详细信息(使用
trustAllCertificates
),可以在此处进行。
受Bhavit S. Sengar回答的启发,它将该技术捆绑到一个死简单的方法调用中。当使用Android的HttpsURLConnection
时,您可以使用NetCipher库来获取现代TLS配置。NetCipher配置HttpsURLConnection
实例以使用最佳支持的TLS版本,删除SSLv3支持,并为该TLS版本配置最佳密码套件。首先,将其添加到您的build.gradle:
compile 'info.guardianproject.netcipher:netcipher:1.2'
或者您可以下载netcipher-1.2。jar并将其直接包含在应用程序中。然后,不要打电话:
HttpURLConnection connection = (HttpURLConnection) sourceUrl.openConnection();
叫这个:
HttpsURLConnection connection = NetCipher.getHttpsURLConnection(sourceUrl);
我通过使用wireshark分析数据包找到了解决方案。我发现,在进行安全连接时,Android从TLSv1退回到SSLv3。这是一个bug在Android版本
/*Copyright 2015 Bhavit Singh Sengar
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.*/
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;
import java.net.SocketAddress;
import java.net.SocketException;
import java.nio.channels.SocketChannel;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javax.net.ssl.HandshakeCompletedListener;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
public class NoSSLv3SocketFactory extends SSLSocketFactory{
private final SSLSocketFactory delegate;
public NoSSLv3SocketFactory() {
this.delegate = HttpsURLConnection.getDefaultSSLSocketFactory();
}
public NoSSLv3SocketFactory(SSLSocketFactory delegate) {
this.delegate = delegate;
}
@Override
public String[] getDefaultCipherSuites() {
return delegate.getDefaultCipherSuites();
}
@Override
public String[] getSupportedCipherSuites() {
return delegate.getSupportedCipherSuites();
}
private Socket makeSocketSafe(Socket socket) {
if (socket instanceof SSLSocket) {
socket = new NoSSLv3SSLSocket((SSLSocket) socket);
}
return socket;
}
@Override
public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException {
return makeSocketSafe(delegate.createSocket(s, host, port, autoClose));
}
@Override
public Socket createSocket(String host, int port) throws IOException {
return makeSocketSafe(delegate.createSocket(host, port));
}
@Override
public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException {
return makeSocketSafe(delegate.createSocket(host, port, localHost, localPort));
}
@Override
public Socket createSocket(InetAddress host, int port) throws IOException {
return makeSocketSafe(delegate.createSocket(host, port));
}
@Override
public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException {
return makeSocketSafe(delegate.createSocket(address, port, localAddress, localPort));
}
private class NoSSLv3SSLSocket extends DelegateSSLSocket {
private NoSSLv3SSLSocket(SSLSocket delegate) {
super(delegate);
}
@Override
public void setEnabledProtocols(String[] protocols) {
if (protocols != null && protocols.length == 1 && "SSLv3".equals(protocols[0])) {
List<String> enabledProtocols = new ArrayList<String>(Arrays.asList(delegate.getEnabledProtocols()));
if (enabledProtocols.size() > 1) {
enabledProtocols.remove("SSLv3");
System.out.println("Removed SSLv3 from enabled protocols");
} else {
System.out.println("SSL stuck with protocol available for " + String.valueOf(enabledProtocols));
}
protocols = enabledProtocols.toArray(new String[enabledProtocols.size()]);
}
super.setEnabledProtocols(protocols);
}
}
public class DelegateSSLSocket extends SSLSocket {
protected final SSLSocket delegate;
DelegateSSLSocket(SSLSocket delegate) {
this.delegate = delegate;
}
@Override
public String[] getSupportedCipherSuites() {
return delegate.getSupportedCipherSuites();
}
@Override
public String[] getEnabledCipherSuites() {
return delegate.getEnabledCipherSuites();
}
@Override
public void setEnabledCipherSuites(String[] suites) {
delegate.setEnabledCipherSuites(suites);
}
@Override
public String[] getSupportedProtocols() {
return delegate.getSupportedProtocols();
}
@Override
public String[] getEnabledProtocols() {
return delegate.getEnabledProtocols();
}
@Override
public void setEnabledProtocols(String[] protocols) {
delegate.setEnabledProtocols(protocols);
}
@Override
public SSLSession getSession() {
return delegate.getSession();
}
@Override
public void addHandshakeCompletedListener(HandshakeCompletedListener listener) {
delegate.addHandshakeCompletedListener(listener);
}
@Override
public void removeHandshakeCompletedListener(HandshakeCompletedListener listener) {
delegate.removeHandshakeCompletedListener(listener);
}
@Override
public void startHandshake() throws IOException {
delegate.startHandshake();
}
@Override
public void setUseClientMode(boolean mode) {
delegate.setUseClientMode(mode);
}
@Override
public boolean getUseClientMode() {
return delegate.getUseClientMode();
}
@Override
public void setNeedClientAuth(boolean need) {
delegate.setNeedClientAuth(need);
}
@Override
public void setWantClientAuth(boolean want) {
delegate.setWantClientAuth(want);
}
@Override
public boolean getNeedClientAuth() {
return delegate.getNeedClientAuth();
}
@Override
public boolean getWantClientAuth() {
return delegate.getWantClientAuth();
}
@Override
public void setEnableSessionCreation(boolean flag) {
delegate.setEnableSessionCreation(flag);
}
@Override
public boolean getEnableSessionCreation() {
return delegate.getEnableSessionCreation();
}
@Override
public void bind(SocketAddress localAddr) throws IOException {
delegate.bind(localAddr);
}
@Override
public synchronized void close() throws IOException {
delegate.close();
}
@Override
public void connect(SocketAddress remoteAddr) throws IOException {
delegate.connect(remoteAddr);
}
@Override
public void connect(SocketAddress remoteAddr, int timeout) throws IOException {
delegate.connect(remoteAddr, timeout);
}
@Override
public SocketChannel getChannel() {
return delegate.getChannel();
}
@Override
public InetAddress getInetAddress() {
return delegate.getInetAddress();
}
@Override
public InputStream getInputStream() throws IOException {
return delegate.getInputStream();
}
@Override
public boolean getKeepAlive() throws SocketException {
return delegate.getKeepAlive();
}
@Override
public InetAddress getLocalAddress() {
return delegate.getLocalAddress();
}
@Override
public int getLocalPort() {
return delegate.getLocalPort();
}
@Override
public SocketAddress getLocalSocketAddress() {
return delegate.getLocalSocketAddress();
}
@Override
public boolean getOOBInline() throws SocketException {
return delegate.getOOBInline();
}
@Override
public OutputStream getOutputStream() throws IOException {
return delegate.getOutputStream();
}
@Override
public int getPort() {
return delegate.getPort();
}
@Override
public synchronized int getReceiveBufferSize() throws SocketException {
return delegate.getReceiveBufferSize();
}
@Override
public SocketAddress getRemoteSocketAddress() {
return delegate.getRemoteSocketAddress();
}
@Override
public boolean getReuseAddress() throws SocketException {
return delegate.getReuseAddress();
}
@Override
public synchronized int getSendBufferSize() throws SocketException {
return delegate.getSendBufferSize();
}
@Override
public int getSoLinger() throws SocketException {
return delegate.getSoLinger();
}
@Override
public synchronized int getSoTimeout() throws SocketException {
return delegate.getSoTimeout();
}
@Override
public boolean getTcpNoDelay() throws SocketException {
return delegate.getTcpNoDelay();
}
@Override
public int getTrafficClass() throws SocketException {
return delegate.getTrafficClass();
}
@Override
public boolean isBound() {
return delegate.isBound();
}
@Override
public boolean isClosed() {
return delegate.isClosed();
}
@Override
public boolean isConnected() {
return delegate.isConnected();
}
@Override
public boolean isInputShutdown() {
return delegate.isInputShutdown();
}
@Override
public boolean isOutputShutdown() {
return delegate.isOutputShutdown();
}
@Override
public void sendUrgentData(int value) throws IOException {
delegate.sendUrgentData(value);
}
@Override
public void setKeepAlive(boolean keepAlive) throws SocketException {
delegate.setKeepAlive(keepAlive);
}
@Override
public void setOOBInline(boolean oobinline) throws SocketException {
delegate.setOOBInline(oobinline);
}
@Override
public void setPerformancePreferences(int connectionTime, int latency, int bandwidth) {
delegate.setPerformancePreferences(connectionTime, latency, bandwidth);
}
@Override
public synchronized void setReceiveBufferSize(int size) throws SocketException {
delegate.setReceiveBufferSize(size);
}
@Override
public void setReuseAddress(boolean reuse) throws SocketException {
delegate.setReuseAddress(reuse);
}
@Override
public synchronized void setSendBufferSize(int size) throws SocketException {
delegate.setSendBufferSize(size);
}
@Override
public void setSoLinger(boolean on, int timeout) throws SocketException {
delegate.setSoLinger(on, timeout);
}
@Override
public synchronized void setSoTimeout(int timeout) throws SocketException {
delegate.setSoTimeout(timeout);
}
@Override
public void setTcpNoDelay(boolean on) throws SocketException {
delegate.setTcpNoDelay(on);
}
@Override
public void setTrafficClass(int value) throws SocketException {
delegate.setTrafficClass(value);
}
@Override
public void shutdownInput() throws IOException {
delegate.shutdownInput();
}
@Override
public void shutdownOutput() throws IOException {
delegate.shutdownOutput();
}
@Override
public String toString() {
return delegate.toString();
}
@Override
public boolean equals(Object o) {
return delegate.equals(o);
}
}
}
在连接时像这样使用此类:
SSLContext sslcontext = SSLContext.getInstance("TLSv1");
sslcontext.init(null,
null,
null);
SSLSocketFactory NoSSLv3Factory = new NoSSLv3SocketFactory(sslcontext.getSocketFactory());
HttpsURLConnection.setDefaultSSLSocketFactory(NoSSLv3Factory);
l_connection = (HttpsURLConnection) l_url.openConnection();
l_connection.connect();
更新:
现在,正确的解决方案是使用Google Play服务安装更新的安全提供商:
ProviderInstaller.installIfNeeded(getApplicationContext());
这有效地使您的应用程序能够访问更新版本的OpenSSL和Java安全提供程序,其中包括对TLSv1的支持。在斯伦金2号。安装新的提供程序后,可以创建支持SSLv3、TLSv1、TLSv1的SSLEngine。1和TLSv1。2.通常的方式:
SSLContext sslContext = SSLContext.getInstance("TLSv1.2");
sslContext.init(null, null, null);
SSLEngine engine = sslContext.createSSLEngine();
或者,您可以使用引擎限制启用的协议。setEnabledProtocols
。
别忘了添加以下依赖项(此处可找到最新版本):
compile 'com.google.android.gms:play-services-auth:11.8.0'
欲了解更多信息,请查看此链接。
问题内容: 我们在android中编写了客户端应用程序,该应用程序使用HttpsUrlConnection API与https服务器连接。由于Poodle漏洞,我们需要在调用任何请求时从启用的协议列表中禁用SSLv3。 我们遵循了oracle捕获的准则 并在调用URL连接之前添加了以下行 该解决方案可以在普通的Java程序中正常工作。当尝试连接仅适用于SSLv3协议的服务器时, 我们遇到了问题SS
我从这里使用了SSLSocketFacory: http://blog.dev-area.net/2015/08/13/Android-4-1-enable-tls-1-1-and-tls-1-2/ 我通过以下示例成功地实现了这一点: 更新:我修改了我的代码如下: 我使用了SSL工厂的以下实现 公共类NoSSLv3SocketFactory扩展SSLSocketFactory{私有最终SSLSoc
我有一个问题,当我尝试读取任何输入时,我的网络连接会抛出一个EOFException。该代码适用于某些网络调用,但不适用于其他网络调用。如果我尝试从连接中读取任何内容,则会失败并出现上述错误。 例: 以下是每个堆栈的跟踪: getResponse: 缓冲输入流: 感谢您的任何帮助, 瑞克 编辑 我找到了我的答案: 这不是一个有据可查的答案。它出现在一些较新版本的android中,存在一个带有回收u
问题内容: 我正在尝试连接到HTTPS URL,但是我需要使用客户端身份验证以及第三方软件在我的系统上放置的证书。 我丝毫不知道如何找到或使用它,我所要做的只是C#示例代码,这与我为此找到的所有Java答案都大不相同。(例如,KeyStore显然需要某种密码吗?) 这是我拥有的C#示例代码 然后,它仅遍历WS2_store_Certs CertificateCollection并一路检查它们。再进
问题内容: 我只想在Webview中进行垂直滚动,而不希望进行水平滚动。 这帮助我解决了滚动问题。 但是使用它使我的webview看起来很奇怪。所有编辑文本的高度都被压缩(垂直),并且看起来很差。 还有什么其他方法可以从客户端禁用水平滚动? 使用SINGLE_COLUMN如何避免webview高度变化的问题?我希望垂直外观与没有SINGLE_COLUMN时的外观相同 问题答案: 这是一种hack,
Android将WIFI详细信息保存到WifiConfiguration类中。当WIFI为ON和SSID时,Pass-key详细信息与WIFI网络匹配,然后Android自动连接特定的WIFI网络。如何禁用该自动连接功能?问这个问题的目的是,我有两个配置的网络(已经保存),我想在按钮单击事件上连接WIFI,但android的自动连接功能与WIFI本身连接。