Java 通过 HTTPS 使用 HttpClient 信任所有证书
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2642777/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Trusting all certificates using HttpClient over HTTPS
提问by harrisonlee
Recently posted a question regarding the HttpClient
over Https (found here). I've made some headway, but I've run into new issues. As with my last problem, I can't seem to find an example anywhere that works for me. Basically, I want my client to accept any certificate (because I'm only ever pointing to one server) but I keep getting a javax.net.ssl.SSLException: Not trusted server certificate exception.
最近发布了一个关于HttpClient
Https的问题(在这里找到)。我已经取得了一些进展,但我遇到了新的问题。与我的上一个问题一样,我似乎无法在任何地方找到适合我的示例。基本上,我希望我的客户接受任何证书(因为我只指向一个服务器)但我不断收到javax.net.ssl.SSLException: Not trusted server certificate exception.
So this is what I have:
所以这就是我所拥有的:
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);
}
And here's the error I'm getting:
这是我得到的错误:
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.accessimport 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();
}
}
(MainActivity.java:77)
W/System.err( 901): at me.harrisonlee.test.ssl.MainActivity.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
采纳答案by Daniel
Note: Do not implement this in production code you are ever going to use on a network you do not entirely trust. Especially anything going over the public internet.
注意:不要在您不完全信任的网络上使用的生产代码中实现这一点。尤其是通过公共互联网进行的任何事情。
Your question is just what I want to know. After I did some searches, the conclusion is as follows.
你的问题正是我想知道的。经过一番搜索,得出的结论如下。
In HttpClient way, you should create a custom class from org.apache.http.conn.ssl.SSLSocketFactory, not the one org.apache.http.conn.ssl.SSLSocketFactory itself. Some clues can be found in this post Custom SSL handling stopped working on Android 2.2 FroYo.
在 HttpClient 方式中,您应该从 org.apache.http.conn.ssl.SSLSocketFactory 创建一个自定义类,而不是 org.apache.http.conn.ssl.SSLSocketFactory 本身。在这篇文章中可以找到一些线索自定义 SSL 处理在 Android 2.2 FroYo 上停止工作。
An example is like ...
一个例子就像...
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();
}
}
and use this class while creating instance of HttpClient.
并在创建 HttpClient 实例时使用此类。
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();
}
}
BTW, the link below is for someone who is looking for HttpURLConnection solution. Https Connection Android
顺便说一句,下面的链接适用于正在寻找 HttpURLConnection 解决方案的人。 HTTPS连接安卓
I have tested the above two kinds of solutions on froyo, and they all work like a charm in my cases. Finally, using HttpURLConnection may face the redirect problems, but this is beyond the topic.
我已经在 froyo 上测试了上述两种解决方案,在我的情况下,它们都非常有用。最后,使用 HttpURLConnection 可能会面临重定向问题,但这超出了主题。
Note: Before you decide to trust all certificates, you probably should know the site full well and won't be harmful of it to end-user.
注意:在您决定信任所有证书之前,您可能应该充分了解该站点并且不会对最终用户造成危害。
Indeed, the risk you take should be considered carefully, including the effect of hacker's mock site mentioned in the following comments that I deeply appreciated. In some situation, although it might be hard to take care of all certificates, you'd better know the implicit drawbacks to trust all of them.
确实,你所承担的风险应该慎重考虑,包括以下评论中提到的黑客模拟站点的影响,我深表感谢。在某些情况下,虽然可能很难处理所有证书,但您最好了解信任所有证书的隐含缺点。
回答by Alok Gupta
Add this code before the HttpsURLConnection
and it will be done. I got it.
在 之前添加此代码HttpsURLConnection
,它将完成。我知道了。
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();
I hope this helps you.
我希望这可以帮助你。
回答by Dan
This is a bad idea. Trusting any certificate is only (very) slightly better than using no SSL at all. When you say "I want my client to accept any certificate (because I'm only ever pointing to one server)" you are assuming this means that somehow pointing to "one server" is safe, which it's not on a public network.
这是一个坏主意。信任任何证书只是(非常)比完全不使用 SSL 好一点。当您说“我希望我的客户端接受任何证书(因为我只指向一台服务器)”时,您假设这意味着以某种方式指向“一台服务器”是安全的,它不在公共网络上。
You are completely open to a man-in-the-middle attack by trusting any certificate. Anyone can proxy your connection by establishing a separate SSL connection with you and with the end server. The MITM then has access to your entire request and response. Unless you didn't really need SSL in the first place (your message has nothing sensitive, and doesn't do authentication) you shouldn't trust all certificates blindly.
通过信任任何证书,您完全可以接受中间人攻击。任何人都可以通过与您和终端服务器建立单独的 SSL 连接来代理您的连接。MITM 然后可以访问您的整个请求和响应。除非您一开始并不真正需要 SSL(您的消息没有任何敏感内容,并且不进行身份验证),否则您不应该盲目信任所有证书。
You should consider adding the public cert to a jks using keytool, and using that to build your socket factory, such as this:
您应该考虑使用 keytool 将公共证书添加到 jks,并使用它来构建您的套接字工厂,例如:
openssl s_client -connect www.yourserver.com:443
This has one caveat to watch out for. The certificate will expire eventually, and the code will stop working at that time. You can easily determine when this will happen by looking at the cert.
这有一个需要注意的警告。证书最终会过期,届时代码将停止工作。您可以通过查看证书轻松确定何时会发生这种情况。
回答by emmby
You basically have four potential solutions to fix a "Not Trusted" exception on Android using httpclient:
您基本上有四种可能的解决方案来使用 httpclient 修复 Android 上的“Not Trusted”异常:
- Trust all certificates. Don't do this, unless you really know what you're doing.
- Create a custom SSLSocketFactory that trusts only your certificate. This works as long as you know exactly which servers you're going to connect to, but as soon as you need to connect to a new server with a different SSL certificate, you'll need to update your app.
- Create a keystore file that contains Android's "master list" of certificates, then add your own. If any of those certs expire down the road, you are responsible for updating them in your app. I can't think of a reason to do this.
- Create a custom SSLSocketFactory that uses the built-in certificate KeyStore, but falls back on an alternate KeyStore for anything that fails to verify with the default.
- 信任所有证书。不要这样做,除非你真的知道你在做什么。
- 创建一个仅信任您的证书的自定义 SSLSocketFactory。只要您确切地知道要连接到哪些服务器,这就会起作用,但是一旦您需要连接到具有不同 SSL 证书的新服务器,您就需要更新您的应用程序。
- 创建一个包含 Android 证书“主列表”的密钥库文件,然后添加您自己的。如果这些证书中的任何一个在将来过期,您有责任在您的应用程序中更新它们。我想不出这样做的理由。
- 创建一个使用内置证书 KeyStore 的自定义 SSLSocketFactory,但对于无法使用默认值进行验证的任何内容,都使用备用 KeyStore。
This answer uses solution #4, which seems to me to be the most robust.
这个答案使用解决方案#4,在我看来它是最强大的。
The solution is to use an SSLSocketFactory that can accept multiple KeyStores, allowing you to supply your own KeyStore with your own certificates. This allows you to load additional top-level certificates such as Thawte that might be missing on some Android devices. It also allows you to load your own self-signed certificates as well. It will use the built-in default device certificates first, and fall back on your additional certificates only as necessary.
解决方案是使用可以接受多个 KeyStore 的 SSLSocketFactory,允许您提供自己的 KeyStore 和您自己的证书。这允许您加载其他顶级证书,例如某些 Android 设备上可能缺少的 Thawte。它还允许您加载自己的自签名证书。它将首先使用内置的默认设备证书,并仅在必要时使用您的附加证书。
First, you'll want to determine which cert you are missing in your KeyStore. Run the following command:
首先,您需要确定您的 KeyStore 中缺少哪个证书。运行以下命令:
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
And you'll see output like the following:
你会看到如下输出:
echo | openssl s_client -connect ${MY_SERVER}:443 2>&1 | \
sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' > mycert.pem
As you can see, our root certificate is from Thawte. Go to your provider's website and find the corresponding certificate. For us, it was here, and you can see that the one we needed was the one Copyright 2006.
如您所见,我们的根证书来自 Thawte。转到您的提供商的网站并找到相应的证书。对我们来说,它就在这里,你可以看到我们需要的是一个 Copyright 2006。
If you're using a self-signed certificate, you didn't need to do the previous step since you already have your signing certificate.
如果您使用的是自签名证书,则无需执行上一步,因为您已经拥有签名证书。
Then, create a keystore file containing the missing signing certificate. Crazybob has details how to do this on Android, but the idea is to do the following:
然后,创建一个包含缺少的签名证书的密钥库文件。Crazybob详细介绍了如何在 Android 上执行此操作,但其想法是执行以下操作:
If you don't have it already, download the bouncy castle provider library from: http://www.bouncycastle.org/latest_releases.html. This will go on your classpath below.
如果您还没有,请从http://www.bouncycastle.org/latest_releases.html下载充气城堡提供程序库。这将在下面的类路径中进行。
Run a command to extract the certificate from the server and create a pem file. In this case, mycert.pem.
运行命令从服务器提取证书并创建一个 pem 文件。在这种情况下,mycert.pem。
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
Then run the following commands to create the keystore.
然后运行以下命令来创建密钥库。
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);
You'll notice that the above script places the result in res/raw/mystore.bks
. Now you have a file that you'll load into your Android app that provides the missing certificate(s).
您会注意到上面的脚本将结果放在res/raw/mystore.bks
. 现在您有一个文件,您可以将其加载到您的 Android 应用程序中,该文件提供缺少的证书。
To do this, register your SSLSocketFactory for the SSL scheme:
为此,请为 SSL 方案注册您的 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);
}
}
To create your SSLSocketFactory:
创建您的 SSLSocketFactory:
/**
* 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()]);
}
}
}
And finally, the AdditionalKeyStoresSSLSocketFactory code, which accepts your new KeyStore and checks if the built-in KeyStore fails to validate an SSL certificate:
最后是 AdditionalKeyStoresSSLSocketFactory 代码,它接受您的新 KeyStore 并检查内置 KeyStore 是否无法验证 SSL 证书:
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);
回答by Dean Hiller
Daniel's answerwas good except I had to change 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 this code...
到这个代码...
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();
}
}
to get it to work.
让它工作。
回答by Joseph Valerio
Here is a much simple version using 4.1.2 httpclient code. This can then be modified to any trust algorithm you see fit.
这是一个使用 4.1.2 httpclient 代码的非常简单的版本。然后可以将其修改为您认为合适的任何信任算法。
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();
}
}
回答by ggrandes
I'm looked response from "emmby" (answered Jun 16 '11 at 21:29), item #4: "Create a custom SSLSocketFactory that uses the built-in certificate KeyStore, but falls back on an alternate KeyStore for anything that fails to verify with the default."
我看到了来自“emmby”的回复(11 年 6 月 16 日 21:29 回答),第 4 项:“创建一个使用内置证书 KeyStore 的自定义 SSLSocketFactory,但对于任何失败的事情,都依赖备用 KeyStore以验证默认值。”
This is a simplified implementation. Load the system keystore & merge with application keystore.
这是一个简化的实现。加载系统密钥库并与应用程序密钥库合并。
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
A simple mode to convert from JKS to BKS:
从 JKS 转换为 BKS 的简单模式:
`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`
*Note: In Android 4.0 (ICS) the Trust Store has changed, more info: http://nelenkov.blogspot.com.es/2011/12/ics-trust-store-implementation.html
*注意:在 Android 4.0 (ICS) 中,Trust Store 已更改,更多信息:http: //nelenkov.blogspot.com.es/2011/12/ics-trust-store-implementation.html
回答by 13hsoj
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 answerprovided by @emmbyreplace
任何机构仍然StartCom SSL证书上Android 2.1的访问挣扎https://www.startssl.com/certs/并下载ca.pem,在现在的答案提供@emmby更换
`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`
with
和
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();
}
}
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...
回答by Prabhu Periasamy
The API of HttpComponents has got changed. It works with the code below.
HttpComponents 的 API 已更改。它适用于下面的代码。
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)
}
回答by Tiago
For those who would like to allow all certificates to work (for testing purposes) over OAuth, follow these steps:
对于那些希望允许所有证书通过 OAuth 工作(用于测试目的)的人,请按照以下步骤操作:
1) Download the source code of the Android OAuth API here: https://github.com/kaeppler/signpost
1) 在此处下载 Android OAuth API 的源代码:https: //github.com/kaeppler/signpost
2) Find the file "CommonsHttpOAuthProvider" class
2)找到文件“CommonsHttpOAuthProvider”类
3) Change it as below:
3)改成如下:
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();
}
}
The "MySSLSocketFactory" above is based on the accepted answer. To make it even easier, here goes the complete class:
上面的“MySSLSocketFactory”基于已接受的答案。为了使它更容易,这里有完整的课程:
##代码##}
}
Hope this helps someone.
希望这可以帮助某人。