Android javax.net.ssl.SSLException: hostname in certificate didn't match...
来源:互联网 发布:刷人气软件免费版 编辑:程序博客网 时间:2024/05/17 01:17
最近在开发过程中,遇到一个bug,我们的App在下载一个文件的时候,服务器端返回ConnectionException:
javax.net.ssl.SSLException: hostname in certificate didn't match: <foo.net> != <bar.com> OR <bar.com> OR <www.bar.com>我的代码很简单:
request = new HttpPost("https://foo.net/api/v1/baz");request.setHeader("Authorization", "user:pass");response = httpClient.execute(request);
为什么会有这个错误呢?这主要跟server 的ssl 验证方式有关。
The most likely cause for this problem is that the server uses Server Name Indication to choose which certificate to send. If the client doesn't support SNI, the server cannot choose which certificate to send during the SSL/TLS handshake (before any HTTP traffic is sent).SNI is required when you want to use multiple certificates on the same IP address and port, but not all clients support it.
Apache HTTP Client library support SNI is quite recent, and certainly hasn't made it into the Android release.
Apache HttpClient supports SNI on Oracle’s Java 1.7 since 4.3.2, but this is not useable with Android’s Java flavour and it doesn’t seem that Google is willing to update the library. So, HttpClient doesn’t support SNI on Android by default.
两种解决方法:
1. Server 修改:to use a distinct IP address for each host (so as not to need SNI), if you're in control of server
2. 客户端修改:to use another HTTP Client library (e.g. HttpsURLConnection). HttpsURLConnection which supports SNI by default since Android 2.3.x (see Android’s HTTP Clients).
参考:[Android] Using SNI and TLSv1.2 with Apache HttpClient library
If you want to use SSL/TLS with SNI (Server Name Indication) on Android, you’re basically encouraged to use HttpsURLConnection which supports SNI by default since Android 2.3.x (see Android’s HTTP Clients).
However, if you want to use other HTTP verbs than OPTIONS, HEAD, TRACE, GET, POST, PUT or DELETE (look for “HTTP Methods” in the docs), HttpsURLConnection is not an option. So you will have to stick with the HttpClient library, i.e. the DefaultHttpClient / AndroidHttpClient classes.
Apache HttpClient 4.0-alpha is shipped with Android, and it doesn’t seem that Google is willing to update the library. So, if you need a newer HttpClient version (and there are good reasons why you might need it), the HttpClient package names (Java name spaces) have to be changed. httpclientandroidlib is such a repackaging of recent HttpClient libraries to Android. In the meanwhile, there’s an official Android port of HttpClient.
HttpClient supports SNI on Oracle’s Java 1.7 since 4.3.2, but this is not useable with Android’s Java flavour. So, HttpClient doesn’t support SNI on Android by default.
However, there are two ways to get SNI:
- Since Android 4.2+ (API level 17) they have officially added SNI support to a class called SSLCertificateSocketFactory.
- Since Android 2.3 (Gingerbread), SNI is available in the OpenSSL implementation used by Android’s Java flavour. Sockets created by SSLCertificateSocketFactory are instances of SSLSocketImpl, and this class has a method called setHostname(String) that enables SNI and sets the SNI hostname for this socket. However, this feature is not documented and can only be used by reflection. It works on all devices I have seen, but theoretically, there might be Android variants (for instance, by certain vendors) that don’t provide this method because it’s not documented.
TLS v1.1/v1.2
Android versions >= 4.1and < 5.0 support TLS 1.1 and TLS 1.2, but these (newer and more secure) TLS versions are disabled, while only SSLv3 and TLSv1 stay enabled by default. This is fixed in Android 5.0, but for the versions between you’ll have to enable TLSv1.1 and TLSv1.2 manually:
ssl.setEnabledProtocols(ssl.getSupportedProtocols());
This line is for demonstration only! Actually, you should exclude SSLv3, too. You should also care about the used algorithms (read the NIST recommendations and other related documented) and maybe set them manually. Be sure that you know what you do, otherwise you can’t expect much security.
Working example for stock Android HttpClient
This code is for the HttpClient version which is shipped with Android (HttpClient 4.0-alpha). Look at the links below for an example using a recent HttpClient version.// create new HTTP clientclient = new ApacheHttpClient();// use our own, SNI-capable LayeredSocketFactory for https://SchemeRegistry schemeRegistry = client.getConnectionManager().getSchemeRegistry();schemeRegistry.register(new Scheme("https", new TlsSniSocketFactory(), 443));Then define your TlsSniSocketFactory:
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)public class TlsSniSocketFactory implements LayeredSocketFactory { private static final String TAG = "davdroid.SNISocketFactory"; final static HostnameVerifier hostnameVerifier = new StrictHostnameVerifier(); // Plain TCP/IP (layer below TLS) @Override public Socket connectSocket(Socket s, String host, int port, InetAddress localAddress, int localPort, HttpParams params) throws IOException { return null; } @Override public Socket createSocket() throws IOException { return null; } @Override public boolean isSecure(Socket s) throws IllegalArgumentException { if (s instanceof SSLSocket) return ((SSLSocket)s).isConnected(); return false; } // TLS layer @Override public Socket createSocket(Socket plainSocket, String host, int port, boolean autoClose) throws IOException, UnknownHostException { if (autoClose) { // we don't need the plainSocket plainSocket.close(); } // create and connect SSL socket, but don't do hostname/certificate verification yet SSLCertificateSocketFactory sslSocketFactory = (SSLCertificateSocketFactory) SSLCertificateSocketFactory.getDefault(0); SSLSocket ssl = (SSLSocket)sslSocketFactory.createSocket(InetAddress.getByName(host), port); // enable TLSv1.1/1.2 if available // (see https://github.com/rfc2822/davdroid/issues/229) ssl.setEnabledProtocols(ssl.getSupportedProtocols()); // set up SNI before the handshake if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { Log.i(TAG, "Setting SNI hostname"); sslSocketFactory.setHostname(ssl, host); } else { Log.d(TAG, "No documented SNI support on Android <4.2, trying with reflection"); try { java.lang.reflect.Method setHostnameMethod = ssl.getClass().getMethod("setHostname", String.class); setHostnameMethod.invoke(ssl, host.getHostName()); } catch (Exception e) { Log.w(TAG, "SNI not useable", e); } } // verify hostname and certificate SSLSession session = ssl.getSession(); if (!hostnameVerifier.verify(host, session)) throw new SSLPeerUnverifiedException("Cannot verify hostname: " + host); Log.i(TAG, "Established " + session.getProtocol() + " connection with " + session.getPeerHost() + " using " + session.getCipherSuite()); return ssl; }}
- javax.net.ssl.SSLException: hostname in certificate didn't match:
- javax.net.ssl.SSLException: hostname in certificate didn't match:
- Android 链接https出现 javax.net.ssl.SSLException: hostname in certificate didn't match:
- Android 链接https出现 javax.net.ssl.SSLException: hostname in certificate didn't match:
- Android 链接https出现 javax.net.ssl.SSLException: hostname in certificate didn't match:
- Android javax.net.ssl.SSLException: hostname in certificate didn't match...
- Android 链接https出现 javax.net.ssl.SSLException: hostname in certificate didn't match:
- Android 异常:javax.net.ssl.SSLException: hostname in certificate didn't match
- Android 链接https出现 javax.net.ssl.SSLException: hostname in certificate didn't match:
- 12. javax.net.ssl.SSLException: hostname in certificate didn't match证书不匹配的主机名
- HttpClient请求https,抛出javax.net.ssl.SSLException: hostname in certificate didn't match:
- HttpsURLConnection请求https,抛出javax.net.ssl.SSLException: hostname in certificate didn't match:
- HttpClient访问报错 javax.net.ssl.SSLException: hostname in certificate didn't match
- javax.net.ssl.SSLException: hostname in certificate didn't match证书不匹配的主机名
- httpclient对象请求:如果报这个错:javax.net.ssl.SSLException: hostname in certificate didn't match证书不匹配的主机名
- android SDK 更新出现“hostname in certificate didn't match: <dl-ssl.google.com>”解决方法
- android SDK 更新出现“hostname in certificate didn't match: <dl-ssl.google.com>”解决方法
- Android SDK下载异常(hostname in certificate didn't match)
- hdu5605(基本不等式)
- 小灶第二次作业
- 安装与语法
- 代码中修改ImageView的高度和宽度
- PHP设计模式之JS实现观察者模式
- Android javax.net.ssl.SSLException: hostname in certificate didn't match...
- 动态规划-POJ 3666 Making the Grade
- kibana 搜索操作
- 深入源码之SLF4J
- linux如何关闭防火墙
- yii const static 的区别
- Lesson 2: Triangle rasterization and back face culling
- 【SDUT 3494】 简单题III(树链剖分)
- Java 8大排序算法