使用HttpClient通过HTTPS信任所有证书
最近在Https上发布了一个关于HttpClient
的问题( 在这里find )。 我已经取得了一些进展,但是我遇到了新的问题。 和我最后一个问题一样,我似乎无法在任何地方find适合我的例子。 基本上,我希望我的客户端接受任何证书(因为我只是指向一台服务器),但我不断得到一个javax.net.ssl.SSLException: Not trusted server certificate exception.
所以这就是我所拥有的:
public void connect() throws A_WHOLE_BUNCH_OF_EXCEPTIONS { HttpPost post = new HttpPost(new URI(PROD_URL)); post.setEntity(new StringEntity(BODY)); KeyStore trusted = KeyStore.getInstance("BKS"); trusted.load(null, "".toCharArray()); SSLSocketFactory sslf = new SSLSocketFactory(trusted); sslf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER); SchemeRegistry schemeRegistry = new SchemeRegistry(); schemeRegistry.register(new Scheme ("https", sslf, 443)); SingleClientConnManager cm = new SingleClientConnManager(post.getParams(), schemeRegistry); HttpClient client = new DefaultHttpClient(cm, post.getParams()); HttpResponse result = client.execute(post); }
这里是我得到的错误:
W/System.err( 901): javax.net.ssl.SSLException: Not trusted server certificate W/System.err( 901): at org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:360) W/System.err( 901): at org.apache.http.conn.ssl.AbstractVerifier.verify(AbstractVerifier.java:92) W/System.err( 901): at org.apache.http.conn.ssl.SSLSocketFactory.connectSocket(SSLSocketFactory.java:321) W/System.err( 901): at org.apache.http.impl.conn.DefaultClientConnectionOperator.openConnection(DefaultClientConnectionOperator.java:129) W/System.err( 901): at org.apache.http.impl.conn.AbstractPoolEntry.open(AbstractPoolEntry.java:164) W/System.err( 901): at org.apache.http.impl.conn.AbstractPooledConnAdapter.open(AbstractPooledConnAdapter.java:119) W/System.err( 901): at org.apache.http.impl.client.DefaultRequestDirector.execute(DefaultRequestDirector.java:348) W/System.err( 901): at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:555) W/System.err( 901): at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:487) W/System.err( 901): at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:465) W/System.err( 901): at me.harrisonlee.test.ssl.MainActivity.connect(MainActivity.java:129) W/System.err( 901): at me.harrisonlee.test.ssl.MainActivity.access$0(MainActivity.java:77) W/System.err( 901): at me.harrisonlee.test.ssl.MainActivity$2.run(MainActivity.java:49) W/System.err( 901): Caused by: java.security.cert.CertificateException: java.security.InvalidAlgorithmParameterException: the trust anchors set is empty W/System.err( 901): at org.apache.harmony.xnet.provider.jsse.TrustManagerImpl.checkServerTrusted(TrustManagerImpl.java:157) W/System.err( 901): at org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:355) W/System.err( 901): ... 12 more W/System.err( 901): Caused by: java.security.InvalidAlgorithmParameterException: the trust anchors set is empty W/System.err( 901): at java.security.cert.PKIXParameters.checkTrustAnchors(PKIXParameters.java:645) W/System.err( 901): at java.security.cert.PKIXParameters.<init>(PKIXParameters.java:89) W/System.err( 901): at org.apache.harmony.xnet.provider.jsse.TrustManagerImpl.<init>(TrustManagerImpl.java:89) W/System.err( 901): at org.apache.harmony.xnet.provider.jsse.TrustManagerFactoryImpl.engineGetTrustManagers(TrustManagerFactoryImpl.java:134) W/System.err( 901): at javax.net.ssl.TrustManagerFactory.getTrustManagers(TrustManagerFactory.java:226)W/System.err( 901): at org.apache.http.conn.ssl.SSLSocketFactory.createTrustManagers(SSLSocketFactory.java:263) W/System.err( 901): at org.apache.http.conn.ssl.SSLSocketFactory.<init>(SSLSocketFactory.java:190) W/System.err( 901): at org.apache.http.conn.ssl.SSLSocketFactory.<init>(SSLSocketFactory.java:216) W/System.err( 901): at me.harrisonlee.test.ssl.MainActivity.connect(MainActivity.java:107) W/System.err( 901): ... 2 more
注意:不要在您不完全信任的networking上使用的生产代码中实现此function。 特别是任何通过公共互联网的东西。
你的问题正是我想知道的。 我做了一些search之后,结论如下。
在HttpClient的方式,你应该从org.apache.http.conn.ssl.SSLSocketFactory创build一个自定义的类,而不是一个org.apache.http.conn.ssl.SSLSocketFactory本身。 一些线索可以在这篇文章中find自定义SSL处理停止在Android 2.2 FroYo上工作 。
一个例子就是…
import java.io.IOException; import java.net.Socket; import java.net.UnknownHostException; import java.security.KeyManagementException; import java.security.KeyStore; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.UnrecoverableKeyException; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import javax.net.ssl.SSLContext; import javax.net.ssl.TrustManager; import javax.net.ssl.X509TrustManager; import org.apache.http.conn.ssl.SSLSocketFactory; public class MySSLSocketFactory extends SSLSocketFactory { SSLContext sslContext = SSLContext.getInstance("TLS"); public MySSLSocketFactory(KeyStore truststore) throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException { super(truststore); TrustManager tm = new X509TrustManager() { public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { } public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { } public X509Certificate[] getAcceptedIssuers() { return null; } }; sslContext.init(null, new TrustManager[] { tm }, null); } @Override public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException, UnknownHostException { return sslContext.getSocketFactory().createSocket(socket, host, port, autoClose); } @Override public Socket createSocket() throws IOException { return sslContext.getSocketFactory().createSocket(); } }
并在创buildHttpClient的实例时使用这个类。
public HttpClient getNewHttpClient() { try { KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType()); trustStore.load(null, null); MySSLSocketFactory sf = new MySSLSocketFactory(trustStore); sf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER); HttpParams params = new BasicHttpParams(); HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1); HttpProtocolParams.setContentCharset(params, HTTP.UTF_8); SchemeRegistry registry = new SchemeRegistry(); registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80)); registry.register(new Scheme("https", sf, 443)); ClientConnectionManager ccm = new ThreadSafeClientConnManager(params, registry); return new DefaultHttpClient(ccm, params); } catch (Exception e) { return new DefaultHttpClient(); } }
顺便说一句,下面的链接是为谁正在寻找HttpURLConnection解决scheme。 https连接Android
我已经在froyo上testing了上述两种解决scheme,在我的案例中,它们都像魅力一样工作。 最后,使用HttpURLConnection可能会面临redirect问题,但是这超出了主题。
注意:在您决定信任所有证书之前,您可能应该充分了解该网站,并且不会对最终用户造成危害。
事实上,你应该仔细考虑你所承担的风险,包括下面的评论中提到的黑客模拟网站的影响,我深表感谢。 在某些情况下,虽然可能很难照顾所有的证书,但最好还是要明白隐含的缺点,相信所有的证书。
您基本上有四个可能的解决scheme来解决Android上使用httpclient的“Not Trusted”exception:
- 信任所有证书。 不要这样做,除非你真的知道你在做什么。
- 创build只信任您的证书的自定义SSLSocketFactory。 只要您确切知道要连接哪台服务器,只要您需要使用不同的SSL证书连接到新的服务器,就需要更新您的应用。
- 创build一个包含Android的证书“主列表”的密钥库文件,然后添加自己的。 如果这些证书中的任何一条到期,您有责任在您的应用中对其进行更新。 我想不出有这个理由。
- 创build一个使用内置证书KeyStore的自定义SSLSocketFactory,但是对于任何无法使用缺省值进行validation的事件,将返回到备用KeyStore上。
这个答案使用解决scheme#4,这在我看来是最强大的。
解决scheme是使用可以接受多个KeyStore的SSLSocketFactory,从而允许您为自己的KeyStore提供自己的证书。 这允许您加载其他顶级证书,例如某些Android设备上可能缺less的Thawte。 它也允许你加载自己的自签名证书。 它将首先使用内置的默认设备证书,并根据需要回退到其他证书上。
首先,您需要确定您的KeyStore中缺less哪个证书。 运行以下命令:
openssl s_client -connect www.yourserver.com:443
你会看到如下输出:
Certificate chain 0 s:/O=www.yourserver.com/OU=Go to https://www.thawte.com/repository/index.html/OU=Thawte SSL123 certificate/OU=Domain Validated/CN=www.yourserver.com i:/C=US/O=Thawte, Inc./OU=Domain Validated SSL/CN=Thawte DV SSL CA 1 s:/C=US/O=Thawte, Inc./OU=Domain Validated SSL/CN=Thawte DV SSL CA i:/C=US/O=thawte, Inc./OU=Certification Services Division/OU=(c) 2006 thawte, Inc. - For authorized use only/CN=thawte Primary Root CA
正如你所看到的,我们的根证书来自Thawte。 去你的供应商的网站,find相应的证书。 对我们来说,这是在这里 ,你可以看到,我们需要的是2006版权。
如果您使用的是自签名证书,则您已经拥有签名证书,因此无需执行上一步操作。
然后,创build一个包含缺less的签名证书的密钥库文件。 Crazybob 详细说明了如何在Android上做到这一点 ,但想法是做到以下几点:
如果您还没有,请从http://www.bouncycastle.org/latest_releases.html下载弹性城堡提供程序库。; 这将在你的类path下面。
运行命令从服务器提取证书并创build一个pem文件。 在这种情况下,mycert.pem。
echo | openssl s_client -connect ${MY_SERVER}:443 2>&1 | \ sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' > mycert.pem
然后运行以下命令来创build密钥库。
export CLASSPATH=/path/to/bouncycastle/bcprov-jdk15on-155.jar CERTSTORE=res/raw/mystore.bks if [ -a $CERTSTORE ]; then rm $CERTSTORE || exit 1 fi keytool \ -import \ -v \ -trustcacerts \ -alias 0 \ -file <(openssl x509 -in mycert.pem) \ -keystore $CERTSTORE \ -storetype BKS \ -provider org.bouncycastle.jce.provider.BouncyCastleProvider \ -providerpath /path/to/bouncycastle/bcprov-jdk15on-155.jar \ -storepass some-password
您会注意到,上面的脚本将结果放在res/raw/mystore.bks
。 现在你有一个文件,你将加载到你的Android应用程序,提供缺less的证书(S)。
为此,请为SSLscheme注册您的SSLSocketFactory:
final SchemeRegistry schemeRegistry = new SchemeRegistry(); schemeRegistry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80)); schemeRegistry.register(new Scheme("https", createAdditionalCertsSSLSocketFactory(), 443)); // and then however you create your connection manager, I use ThreadSafeClientConnManager final HttpParams params = new BasicHttpParams(); ... final ThreadSafeClientConnManager cm = new ThreadSafeClientConnManager(params,schemeRegistry);
要创build您的SSLSocketFactory:
protected org.apache.http.conn.ssl.SSLSocketFactory createAdditionalCertsSSLSocketFactory() { try { final KeyStore ks = KeyStore.getInstance("BKS"); // the bks file we generated above final InputStream in = context.getResources().openRawResource( R.raw.mystore); try { // don't forget to put the password used above in strings.xml/mystore_password ks.load(in, context.getString( R.string.mystore_password ).toCharArray()); } finally { in.close(); } return new AdditionalKeyStoresSSLSocketFactory(ks); } catch( Exception e ) { throw new RuntimeException(e); } }
最后,AdditionalKeyStoresSSLSocketFactory代码接受您的新KeyStore并检查内置的KeyStore是否无法validationSSL证书:
/** * Allows you to trust certificates from additional KeyStores in addition to * the default KeyStore */ public class AdditionalKeyStoresSSLSocketFactory extends SSLSocketFactory { protected SSLContext sslContext = SSLContext.getInstance("TLS"); public AdditionalKeyStoresSSLSocketFactory(KeyStore keyStore) throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException { super(null, null, null, null, null, null); sslContext.init(null, new TrustManager[]{new AdditionalKeyStoresTrustManager(keyStore)}, null); } @Override public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException { return sslContext.getSocketFactory().createSocket(socket, host, port, autoClose); } @Override public Socket createSocket() throws IOException { return sslContext.getSocketFactory().createSocket(); } /** * Based on http://download.oracle.com/javase/1.5.0/docs/guide/security/jsse/JSSERefGuide.html#X509TrustManager */ public static class AdditionalKeyStoresTrustManager implements X509TrustManager { protected ArrayList<X509TrustManager> x509TrustManagers = new ArrayList<X509TrustManager>(); protected AdditionalKeyStoresTrustManager(KeyStore... additionalkeyStores) { final ArrayList<TrustManagerFactory> factories = new ArrayList<TrustManagerFactory>(); try { // The default Trustmanager with default keystore final TrustManagerFactory original = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); original.init((KeyStore) null); factories.add(original); for( KeyStore keyStore : additionalkeyStores ) { final TrustManagerFactory additionalCerts = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); additionalCerts.init(keyStore); factories.add(additionalCerts); } } catch (Exception e) { throw new RuntimeException(e); } /* * Iterate over the returned trustmanagers, and hold on * to any that are X509TrustManagers */ for (TrustManagerFactory tmf : factories) for( TrustManager tm : tmf.getTrustManagers() ) if (tm instanceof X509TrustManager) x509TrustManagers.add( (X509TrustManager)tm ); if( x509TrustManagers.size()==0 ) throw new RuntimeException("Couldn't find any X509TrustManagers"); } /* * Delegate to the default trust manager. */ public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { final X509TrustManager defaultX509TrustManager = x509TrustManagers.get(0); defaultX509TrustManager.checkClientTrusted(chain, authType); } /* * Loop over the trustmanagers until we find one that accepts our server */ public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { for( X509TrustManager tm : x509TrustManagers ) { try { tm.checkServerTrusted(chain,authType); return; } catch( CertificateException e ) { // ignore } } throw new CertificateException(); } public X509Certificate[] getAcceptedIssuers() { final ArrayList<X509Certificate> list = new ArrayList<X509Certificate>(); for( X509TrustManager tm : x509TrustManagers ) list.addAll(Arrays.asList(tm.getAcceptedIssuers())); return list.toArray(new X509Certificate[list.size()]); } } }
在HttpsURLConnection
之前添加这个代码,它将被完成。 我知道了。
private void trustEveryone() { try { HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier(){ public boolean verify(String hostname, SSLSession session) { return true; }}); SSLContext context = SSLContext.getInstance("TLS"); context.init(null, new X509TrustManager[]{new X509TrustManager(){ public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {} public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {} public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[0]; }}}, new SecureRandom()); HttpsURLConnection.setDefaultSSLSocketFactory( context.getSocketFactory()); } catch (Exception e) { // should never happen e.printStackTrace(); } }
我希望这可以帮助你。
这是一个坏主意。 信任任何证书只是(非常)略好于不使用SSL。 当你说“我希望我的客户接受任何证书(因为我只指向一台服务器)”时,你假设这意味着某种方式指向“一台服务器”是安全的,而不是公共networking。
通过信任任何证书,您完全接受中间人攻击。 任何人都可以通过与您和最终服务器build立单独的SSL连接来代理您的连接。 MITM可以访问你的整个请求和响应。 除非你真的不需要SSL,否则你不应该盲目地信任所有的证书。
您应该考虑使用keytool将公共证书添加到jks中,并使用它来构build您的套接字工厂,例如:
KeyStore ks = KeyStore.getInstance("JKS"); // get user password and file input stream char[] password = ("mykspassword")).toCharArray(); ClassLoader cl = this.getClass().getClassLoader(); InputStream stream = cl.getResourceAsStream("myjks.jks"); ks.load(stream, password); stream.close(); SSLContext sc = SSLContext.getInstance("TLS"); KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509"); TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509"); kmf.init(ks, password); tmf.init(ks); sc.init(kmf.getKeyManagers(), tmf.getTrustManagers(),null); return sc.getSocketFactory();
这有一个警告要小心。 证书最终会过期,代码将在当时停止工作。 您可以通过查看证书来轻松确定何时发生这种情况。
您可以通过这种方式禁用HttpURLConnection SSL检查以用于testing目的,因为API 8:
HttpURLConnection conn = (HttpURLConnection) url.openConnection(); if (conn instanceof HttpsURLConnection) { HttpsURLConnection httpsConn = (HttpsURLConnection) conn; httpsConn.setSSLSocketFactory(SSLCertificateSocketFactory.getInsecure(0, null)); httpsConn.setHostnameVerifier(new AllowAllHostnameVerifier()); }
HttpComponents的API已经改变了。 它适用于下面的代码。
public static HttpClient getTestHttpClient() { try { SSLSocketFactory sf = new SSLSocketFactory(new TrustStrategy(){ @Override public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException { return true; } }, new AllowAllHostnameVerifier()); SchemeRegistry registry = new SchemeRegistry(); registry.register(new Scheme("https",8444, sf)); ClientConnectionManager ccm = new ThreadSafeClientConnManager(registry); return new DefaultHttpClient(ccm); } catch (Exception e) { e.printStackTrace(); return new DefaultHttpClient(); } }
上面的代码在https://stackoverflow.com/a/6378872/1553004是正确的,除了它也必须调用主机名validation者:;
@Override public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException { SSLSocket sslSocket = (SSLSocket)sslContext.getSocketFactory().createSocket(socket, host, port, autoClose); getHostnameVerifier().verify(host, sslSocket); return sslSocket; }
我明确注册到了stackoverflow来添加此修复程序。 注意我的警告!
这里是一个简单的使用4.1.2 httpclient代码的版本。 这可以被修改为您认为合适的任何信任algorithm。
public static HttpClient getTestHttpClient() { try { SSLSocketFactory sf = new SSLSocketFactory(new TrustStrategy(){ @Override public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException { return true; } }); SchemeRegistry registry = new SchemeRegistry(); registry.register(new Scheme("https", 443, sf)); ClientConnectionManager ccm = new ThreadSafeClientConnManager(registry); return new DefaultHttpClient(ccm); } catch (Exception e) { return new DefaultHttpClient(); } }
我看起来从“emmby”(回答6月16日在21:29)响应,项目#4:“创build一个自定义的SSLSocketFactory使用内置的证书KeyStore,但落在一个备用KeyStore的任何失败用默认的validation。“
这是一个简化的实现。 加载系统密钥库并与应用程序密钥库合并。
public HttpClient getNewHttpClient() { try { InputStream in = null; // Load default system keystore KeyStore trusted = KeyStore.getInstance(KeyStore.getDefaultType()); try { in = new BufferedInputStream(new FileInputStream(System.getProperty("javax.net.ssl.trustStore"))); // Normally: "/system/etc/security/cacerts.bks" trusted.load(in, null); // no password is "changeit" } finally { if (in != null) { in.close(); in = null; } } // Load application keystore & merge with system try { KeyStore appTrusted = KeyStore.getInstance("BKS"); in = context.getResources().openRawResource(R.raw.mykeystore); appTrusted.load(in, null); // no password is "changeit" for (Enumeration<String> e = appTrusted.aliases(); e.hasMoreElements();) { final String alias = e.nextElement(); final KeyStore.Entry entry = appTrusted.getEntry(alias, null); trusted.setEntry(System.currentTimeMillis() + ":" + alias, entry, null); } } finally { if (in != null) { in.close(); in = null; } } HttpParams params = new BasicHttpParams(); HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1); HttpProtocolParams.setContentCharset(params, HTTP.UTF_8); SSLSocketFactory sf = new SSLSocketFactory(trusted); sf.setHostnameVerifier(SSLSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER); SchemeRegistry registry = new SchemeRegistry(); registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80)); registry.register(new Scheme("https", sf, 443)); ClientConnectionManager ccm = new ThreadSafeClientConnManager(params, registry); return new DefaultHttpClient(ccm, params); } catch (Exception e) { return new DefaultHttpClient(); } }
从JKS转换到BKS的简单模式:
keytool -importkeystore -destkeystore cacerts.bks -deststoretype BKS -providerclass org.bouncycastle.jce.provider.BouncyCastleProvider -providerpath bcprov-jdk16-141.jar -deststorepass changeit -srcstorepass changeit -srckeystore $JAVA_HOME/jre/lib/security/cacerts -srcstoretype JKS -noprompt
*注意:在Android 4.0(ICS)中,Trust Store已更改,更多信息: http : //nelenkov.blogspot.com.es/2011/12/ics-trust-store-implementation.html
对于希望允许所有证书通过OAuth工作(用于testing目的)的用户,请按以下步骤操作:
1)在这里下载Android OAuth API的源代码: https : //github.com/kaeppler/signpost
2)find文件“CommonsHttpOAuthProvider”类
3)改变它如下:
public class CommonsHttpOAuthProvider extends AbstractOAuthProvider { private static final long serialVersionUID = 1L; private transient HttpClient httpClient; public CommonsHttpOAuthProvider(String requestTokenEndpointUrl, String accessTokenEndpointUrl, String authorizationWebsiteUrl) { super(requestTokenEndpointUrl, accessTokenEndpointUrl, authorizationWebsiteUrl); //this.httpClient = new DefaultHttpClient();//Version implemented and that throws the famous "javax.net.ssl.SSLException: Not trusted server certificate" if the certificate is not signed with a CA this.httpClient = MySSLSocketFactory.getNewHttpClient();//This will work with all certificates (for testing purposes only) }
上面的“MySSLSocketFactory”基于接受的答案。 为了使它更容易,这里是完整的类:
package com.netcomps.oauth_example; import java.io.IOException; import java.net.Socket; import java.net.UnknownHostException; import java.security.KeyManagementException; import java.security.KeyStore; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.UnrecoverableKeyException; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import javax.net.ssl.SSLContext; import javax.net.ssl.TrustManager; import javax.net.ssl.X509TrustManager; import org.apache.http.HttpVersion; import org.apache.http.client.HttpClient; import org.apache.http.conn.ClientConnectionManager; import org.apache.http.conn.scheme.PlainSocketFactory; import org.apache.http.conn.scheme.Scheme; import org.apache.http.conn.scheme.SchemeRegistry; import org.apache.http.conn.ssl.SSLSocketFactory; import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager; import org.apache.http.params.BasicHttpParams; import org.apache.http.params.HttpParams; import org.apache.http.params.HttpProtocolParams; import org.apache.http.protocol.HTTP; //http://stackoverflow.com/questions/2642777/trusting-all-certificates-using-httpclient-over-https public class MySSLSocketFactory extends SSLSocketFactory { SSLContext sslContext = SSLContext.getInstance("TLS"); public MySSLSocketFactory(KeyStore truststore) throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException { super(truststore); TrustManager tm = new X509TrustManager() { @Override public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { } @Override public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { } @Override public X509Certificate[] getAcceptedIssuers() { return null; } }; sslContext.init(null, new TrustManager[] { tm }, null); } @Override public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException, UnknownHostException { return sslContext.getSocketFactory().createSocket(socket, host, port, autoClose); } @Override public Socket createSocket() throws IOException { return sslContext.getSocketFactory().createSocket(); } public static HttpClient getNewHttpClient() { try { KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType()); trustStore.load(null, null); SSLSocketFactory sf = new MySSLSocketFactory(trustStore); sf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER); HttpParams params = new BasicHttpParams(); HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1); HttpProtocolParams.setContentCharset(params, HTTP.UTF_8); SchemeRegistry registry = new SchemeRegistry(); registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80)); registry.register(new Scheme("https", sf, 443)); ClientConnectionManager ccm = new ThreadSafeClientConnManager(params, registry); return new DefaultHttpClient(ccm, params); } catch (Exception e) { return new DefaultHttpClient(); } }
}
希望这有助于某人。
信任所有证书对我来说并不是真正的select,所以我做了以下的操作来让HttpsURLConnection信任一个新的证书(另请参阅http://nelenkov.blogspot.jp/2011/12/using-custom-certificate-trust-store- on.html )。
-
获得证书; 我通过在Firefox中导出证书(单击小锁图标,获取证书详细信息,单击导出)完成此操作,然后使用portecle导出信任库(BKS)。
-
使用以下代码从/res/raw/geotrust_cert.bks加载Truststore:
final KeyStore trustStore = KeyStore.getInstance("BKS"); final InputStream in = context.getResources().openRawResource( R.raw.geotrust_cert); trustStore.load(in, null); final TrustManagerFactory tmf = TrustManagerFactory .getInstance(TrustManagerFactory.getDefaultAlgorithm()); tmf.init(trustStore); final SSLContext sslCtx = SSLContext.getInstance("TLS"); sslCtx.init(null, tmf.getTrustManagers(), new java.security.SecureRandom()); HttpsURLConnection.setDefaultSSLSocketFactory(sslCtx .getSocketFactory());
我为那些使用httpclient-4.5的应用程序添加了一个响应,而且可能也适用于4.4。
import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import org.apache.http.HttpResponse; import org.apache.http.client.HttpClient; import org.apache.http.client.HttpResponseException; import org.apache.http.client.fluent.ContentResponseHandler; import org.apache.http.client.methods.HttpPost; import org.apache.http.conn.ssl.NoopHostnameVerifier; import org.apache.http.conn.ssl.SSLConnectionSocketFactory; import org.apache.http.conn.ssl.TrustStrategy; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.apache.http.ssl.SSLContextBuilder; public class HttpClientUtils{ public static HttpClient getHttpClientWithoutSslValidation_UsingHttpClient_4_5_2() { try { SSLContextBuilder builder = new SSLContextBuilder(); builder.loadTrustMaterial(null, new TrustStrategy() { @Override public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException { return true; } }); SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(builder.build(), new NoopHostnameVerifier()); CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(sslsf).build(); return httpclient; } catch (Exception e) { throw new RuntimeException(e); } } }
Any body still struggling with StartCom SSL Certificates on Android 2.1 visit https://www.startssl.com/certs/ and download the ca.pem, now in the answer provided by @emmby replace
`export CLASSPATH=bcprov-jdk16-145.jar CERTSTORE=res/raw/mystore.bks if [ -a $CERTSTORE ]; then rm $CERTSTORE || exit 1 fi keytool \ -import \ -v \ -trustcacerts \ -alias 0 \ -file <(openssl x509 -in mycert.pem) \ -keystore $CERTSTORE \ -storetype BKS \ -provider org.bouncycastle.jce.provider.BouncyCastleProvider \ -providerpath /usr/share/java/bcprov.jar \ -storepass some-password`
同
`export CLASSPATH=bcprov-jdk16-145.jar CERTSTORE=res/raw/mystore.bks if [ -a $CERTSTORE ]; then rm $CERTSTORE || exit 1 fi keytool \ -import \ -v \ -trustcacerts \ -alias 0 \ -file <(openssl x509 -in ca.pem) \ -keystore $CERTSTORE \ -storetype BKS \ -provider org.bouncycastle.jce.provider.BouncyCastleProvider \ -providerpath /usr/share/java/bcprov.jar \ -storepass some-password`
Should work out of the box. I was struggling it for over a day even after a perfect answer by @emmby .. Hope this helps someone…
I used this and It works for me on all OS.
/** * Disables the SSL certificate checking for new instances of {@link HttpsURLConnection} This has been created to * aid testing on a local box, not for use on production. * /
private static void disableSSLCertificateChecking() { TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() { public X509Certificate[] getAcceptedIssuers() { return null; } @Override public void checkClientTrusted(X509Certificate[] arg0, String arg1) throws CertificateException { // Not implemented } @Override public void checkServerTrusted(X509Certificate[] arg0, String arg1) throws CertificateException { // Not implemented } } }; try { SSLContext sc = SSLContext.getInstance("TLS"); sc.init(null, trustAllCerts, new java.security.SecureRandom()); HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory()); } catch (KeyManagementException e) { e.printStackTrace(); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } }
Just adding -Dtrust_all_cert=true
to VM arguments should do. This argument tells java to ignore certificate checks.
work with all https
httpClient = new DefaultHttpClient(); SSLContext ctx = SSLContext.getInstance("TLS"); X509TrustManager tm = new X509TrustManager() { public void checkClientTrusted(X509Certificate[] xcs, String string) throws CertificateException { } public void checkServerTrusted(X509Certificate[] xcs, String string) throws CertificateException { } public X509Certificate[] getAcceptedIssuers() { return null; } }; ctx.init(null, new TrustManager[]{tm}, null); SSLSocketFactory ssf = new SSLSocketFactory(ctx, SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER); httpClient.getConnectionManager().getSchemeRegistry().register(new Scheme("https", 443, ssf));
There a many answers above but I wasn't able to get any of them working correctly (with my limited time), so for anyone else in the same situation you can try the code below which worked perfectly for my java testing purposes:
public static HttpClient wrapClient(HttpClient base) { try { SSLContext ctx = SSLContext.getInstance("TLS"); X509TrustManager tm = new X509TrustManager() { public void checkClientTrusted(X509Certificate[] xcs, String string) throws CertificateException { } public void checkServerTrusted(X509Certificate[] xcs, String string) throws CertificateException { } public X509Certificate[] getAcceptedIssuers() { return null; } }; ctx.init(null, new TrustManager[]{tm}, null); SSLSocketFactory ssf = new SSLSocketFactory(ctx); ssf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER); ClientConnectionManager ccm = base.getConnectionManager(); SchemeRegistry sr = ccm.getSchemeRegistry(); sr.register(new Scheme("https", ssf, 443)); return new DefaultHttpClient(ccm, base.getParams()); } catch (Exception ex) { return null; } }
and call like:
DefaultHttpClient baseClient = new DefaultHttpClient(); HttpClient httpClient = wrapClient(baseClient );
Simply use this –
public DefaultHttpClient wrapClient(HttpClient base) { try { SSLContext ctx = SSLContext.getInstance("TLS"); X509TrustManager tm = new X509TrustManager() { public void checkClientTrusted(X509Certificate[] xcs, String string) throws CertificateException { } public void checkServerTrusted(X509Certificate[] xcs, String string) throws CertificateException { } public X509Certificate[] getAcceptedIssuers() { return null; } }; ctx.init(null, new TrustManager[]{tm}, null); SSLSocketFactory ssf = new SSLSocketFactory(ctx); ssf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER); ClientConnectionManager ccm = base.getConnectionManager(); SchemeRegistry sr = ccm.getSchemeRegistry(); sr.register(new Scheme("https", ssf, 443)); return new DefaultHttpClient(ccm, base.getParams()); } catch (Exception ex) { return null; } }
Daniel's answer was good except I had to change this code…
SchemeRegistry registry = new SchemeRegistry(); registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80)); registry.register(new Scheme("https", sf, 443)); ClientConnectionManager ccm = new ThreadSafeClientConnManager(params, registry);
to this code…
ClientConnectionManager ccm = new ThreadSafeClientConnManager(params, registry); SchemeRegistry registry = ccm.getShemeRegistry() registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80)); registry.register(new Scheme("https", sf, 443));
to get it to work.