通过HTTPS使用HttpClient信任所有证书

最近发布了一个关于Https上的HttpClient (在这里找到)的问题。我取得了一些进展,但我遇到了新的问题。就像我的最后一个问题一样,我似乎找不到一个适合我的例子。基本上,我希望我的客户端接受任何证书(因为我只指向一个服务器),但我总是得到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
764109 次浏览

注意:不要在您将在不完全信任的网络上使用的产品代码中实现此功能。尤其是在公共网络上传播的任何东西。

你的问题正是我想知道的。经过我的一些搜索,结论如下。

在HttpClient方式中,您应该从org.apache.http.conn.ssl创建一个自定义类。SSLSocketFactory,而不是org.apache.http.conn.ssl.SSLSocketFactory 本身。一些线索可以在这篇文章自定义SSL处理在android2.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();
}
}

并在创建HttpClient实例时使用这个类。

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解决方案的人准备的。 Https连接Android < / p >

我已经在酸奶上测试了上述两种解决方案,它们在我的案例中都很有效。最后,使用HttpURLConnection可能会面临重定向问题,但这超出了本主题的范围。

注意:在您决定信任所有证书之前,您可能应该完全了解该站点,并且不会对最终用户造成损害。

的确,你所承担的风险应该慎重考虑,包括以下评论中提到的黑客模拟网站的影响,我非常感谢。在某些情况下,尽管可能很难处理所有证书,但您最好了解信任所有证书的潜在缺点。

将此代码添加到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好一点点。当你说“我想让我的客户端接受任何证书(因为我只指向一个服务器)”时,你假设这意味着以某种方式指向“一个服务器”是安全的,它不是在公共网络上。

通过信任任何证书,您完全有可能受到中间人攻击。任何人都可以通过与您和终端服务器建立单独的SSL连接来代理您的连接。然后,MITM可以访问您的整个请求和响应。除非您一开始就不需要SSL(您的消息没有任何敏感内容,并且不进行身份验证),否则您不应该盲目地信任所有证书。

你应该考虑使用keytool将公共证书添加到jks中,并使用它来构建你的套接字工厂,例如:

    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();

这需要注意一个警告。证书最终会过期,代码届时将停止工作。通过查看证书,您可以很容易地确定何时会发生这种情况。

你基本上有四个潜在的解决方案来修复Android上使用httpclient的“不受信任”异常:

  1. 信任所有证书。不要这样做,除非你真的知道自己在做什么。
  2. 创建一个只信任您的证书的自定义SSLSocketFactory。只要你确切地知道你要连接到哪个服务器,这就可以工作,但一旦你需要连接到一个使用不同SSL证书的新服务器,你就需要更新你的应用程序。
  3. 创建一个包含Android证书“主列表”的密钥存储库文件,然后添加自己的证书。如果这些证书中的任何一个过期了,你有责任在你的应用中更新它们。我想不出这样做的理由。
  4. 创建一个使用内置证书KeyStore的自定义SSLSocketFactory,但是对于任何无法使用默认证书KeyStore进行验证的内容,都会使用另一个KeyStore。

这个答案使用了解决方案#4,在我看来这是最健壮的。

解决方案是使用可以接受多个KeyStore的SSLSocketFactory,允许您使用自己的证书提供自己的KeyStore。这允许你加载额外的顶级证书,比如Thawte,这可能在一些Android设备上是缺失的。它还允许您加载自己的自签名证书。它将首先使用内置的默认设备证书,只在必要时使用您的附加证书。

首先,您需要确定KeyStore中缺少哪个证书。执行如下命令:

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。访问您的提供商的网站并找到相应的证书。对我们来说,它是在这里,你可以看到我们需要的是Copyright 2006。

如果您使用的是自签名证书,则不需要执行前面的步骤,因为您已经有了签名证书。

然后,创建一个包含缺少的签名证书的keystore文件。Crazybob有详细说明如何在Android上做到这一点,但其思想是这样做的:

如果你还没有,从http://www.bouncycastle.org/latest_releases.html下载弹性城堡提供程序库。这将在下面的类路径中。

执行命令从服务器端提取证书并生成pem文件。在本例中为mycert.pem。

echo | openssl s_client -connect ${MY_SERVER}:443 2>&1 | \
sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' > 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

你会注意到上面的脚本将结果放在res/raw/mystore.bks中。现在你有一个文件,你将加载到你的Android应用程序,提供缺少的证书。

为此,为SSL方案注册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);

创建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是否无法验证SSL证书:

/**
* 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()]);
}
}


}

丹尼尔的回答很好,除了我必须改变这段代码…

    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);

对于这个代码…

    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));

让它工作。

下面是一个使用4.1.2 httpclient代码的简单版本。然后可以将其修改为您认为合适的任何信任算法。

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 '11 21:29),项目#4:“创建一个自定义SSLSocketFactory,使用内置的证书KeyStore,但对于任何无法用默认值验证的东西,都依赖于另一个KeyStore。”

这是一个简化的实现。加载系统keystore &与应用程序密钥库合并。

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)的信任存储已经改变,更多信息:http://nelenkov.blogspot.com.es/2011/12/ics-trust-store-implementation.html

任何仍然在Android 2.1上与StartCom SSL证书作斗争的人,请访问https://www.startssl.com/certs/并下载ca.pem,现在在回答提供的@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`

应该开箱即用。即使@emmby给出了一个完美的答案,我还是挣扎了一天多。希望这能帮助到某人…

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();
}
}

对于那些希望允许所有证书在OAuth上工作(用于测试目的)的人,请遵循以下步骤:

1)下载Android OAuth API的源代码:https://github.com/kaeppler/signpost

2)找到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();
}
}

希望这能帮助到一些人。

信任所有证书对我来说并不是真正的替代方法,所以我做了以下事情来让HttpsURLConnection信任一个新的证书(另见http://nelenkov.blogspot.jp/2011/12/using-custom-certificate-trust-store-on.html)。

  1. 拿到证书;我通过在Firefox中导出证书来完成这一点(单击小锁图标,获得证书详细信息,单击导出),然后使用portecle导出信任库(BKS)。

  2. 从/res/raw/geotrust_cert加载信任库。BKS,代码如下:

        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());
    

使用所有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));

简单地使用这个-

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;
}
}

上面有很多答案,但我不能让他们中的任何一个正确工作(与我有限的时间),所以对于其他人在相同的情况下,你可以尝试下面的代码,它非常适合我的java测试目的:

    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;
}
}

像这样叫:

DefaultHttpClient baseClient = new DefaultHttpClient();
HttpClient httpClient = wrapClient(baseClient );

参考:http://tech.chitgoks.com/2011/04/24/how-to-avoid-javax-net-ssl-sslpeerunverifiedexception-peer-not-authenticated-problem-using-apache-httpclient/

上面https://stackoverflow.com/a/6378872/1553004中的代码是正确的,除了它还必须调用主机名验证器:

    @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来添加这个修复。注意我的警告!

自API 8以来,您可以通过这种方式禁用HttpURLConnection SSL检查:

    HttpURLConnection conn = (HttpURLConnection) url.openConnection();
if (conn instanceof HttpsURLConnection) {
HttpsURLConnection httpsConn = (HttpsURLConnection) conn;
httpsConn.setSSLSocketFactory(SSLCertificateSocketFactory.getInsecure(0, null));
httpsConn.setHostnameVerifier(new AllowAllHostnameVerifier());
}

我正在为那些使用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);
}
}
}

我用了这个,它适用于我所有的操作系统。

/**
* 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();
}
}

只要将-Dtrust_all_cert=true添加到VM参数就可以了。这个参数告诉java忽略证书检查。

使用这个类

public class WCFs
{
//  https://192.168.30.8/myservice.svc?wsdl
private static final String NAMESPACE = "http://tempuri.org/";
private static final String URL = "192.168.30.8";
private static final String SERVICE = "/myservice.svc?wsdl";
private static String SOAP_ACTION = "http://tempuri.org/iWCFserviceMe/";




public static Thread myMethod(Runnable rp)
{
String METHOD_NAME = "myMethod";


SoapObject request = new SoapObject(NAMESPACE, METHOD_NAME);


request.addProperty("Message", "Https WCF Running...");
return _call(rp,METHOD_NAME, request);
}


protected static HandlerThread _call(final RunProcess rp,final String METHOD_NAME, SoapObject soapReq)
{
final SoapSerializationEnvelope envelope = new SoapSerializationEnvelope(SoapEnvelope.VER11);
int TimeOut = 5*1000;


envelope.dotNet = true;
envelope.bodyOut = soapReq;
envelope.setOutputSoapObject(soapReq);


final HttpsTransportSE httpTransport_net = new HttpsTransportSE(URL, 443, SERVICE, TimeOut);


try
{
HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() // use this section if crt file is handmake
{
@Override
public boolean verify(String hostname, SSLSession session)
{
return true;
}
});


KeyStore k = getFromRaw(R.raw.key, "PKCS12", "password");
((HttpsServiceConnectionSE) httpTransport_net.getServiceConnection()).setSSLSocketFactory(getSSLSocketFactory(k, "SSL"));




}
catch(Exception e){}


HandlerThread thread = new HandlerThread("wcfTd"+ Generator.getRandomNumber())
{
@Override
public void run()
{
Handler h = new Handler(Looper.getMainLooper());
Object response = null;


for(int i=0; i<4; i++)
{
response = send(envelope, httpTransport_net , METHOD_NAME, null);


try
{if(Thread.currentThread().isInterrupted()) return;}catch(Exception e){}


if(response != null)
break;


ThreadHelper.threadSleep(250);
}


if(response != null)
{
if(rp != null)
{
rp.setArguments(response.toString());
h.post(rp);
}
}
else
{
if(Thread.currentThread().isInterrupted())
return;


if(rp != null)
{
rp.setExceptionState(true);
h.post(rp);
}
}


ThreadHelper.stopThread(this);
}
};


thread.start();


return thread;
}




private static Object send(SoapSerializationEnvelope envelope, HttpTransportSE androidHttpTransport, String METHOD_NAME, List<HeaderProperty> headerList)
{
try
{
if(headerList != null)
androidHttpTransport.call(SOAP_ACTION + METHOD_NAME, envelope, headerList);
else
androidHttpTransport.call(SOAP_ACTION + METHOD_NAME, envelope);


Object res = envelope.getResponse();


if(res instanceof SoapPrimitive)
return (SoapPrimitive) envelope.getResponse();
else if(res instanceof SoapObject)
return ((SoapObject) envelope.getResponse());
}
catch(Exception e)
{}


return null;
}


public static KeyStore getFromRaw(@RawRes int id, String algorithm, String filePassword)
{
try
{
InputStream inputStream = ResourceMaster.openRaw(id);
KeyStore keystore = KeyStore.getInstance(algorithm);
keystore.load(inputStream, filePassword.toCharArray());
inputStream.close();


return keystore;
}
catch(Exception e)
{}


return null;
}


public static SSLSocketFactory getSSLSocketFactory(KeyStore trustKey, String SSLAlgorithm)
{
try
{
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(trustKey);


SSLContext context = SSLContext.getInstance(SSLAlgorithm);//"SSL" "TLS"
context.init(null, tmf.getTrustManagers(), null);


return context.getSocketFactory();
}
catch(Exception e){}


return null;
}

enter image description here

在xamarin android中sspi失败。

我找到了这个解;在你点击HTTPS链接之前,先把这个代码放上去

const SslProtocols _Tls12 = (SslProtocols)0x00000C00;
const SecurityProtocolType Tls12 = (SecurityProtocolType)_Tls12;
ServicePointManager.SecurityProtocol = Tls12;

对于这个用例有很多替代方案。如果你不想在你的代码库中有任何自定义代码,如自定义TrustManager或自定义SSLSocketFactory,我建议尝试GitHub - SSLContext Kickstart和下面的代码片段:

<dependency>
<groupId>io.github.hakky54</groupId>
<artifactId>sslcontext-kickstart</artifactId>
<version>7.0.2</version>
</dependency>

SSL配置

SSLFactory sslFactory = SSLFactory.builder()
.withUnsafeTrustMaterial()
.withUnsafeHostnameVerifier()
.build();


SSLSocketFactory sslSocketFactory = sslFactory.getSslSocketFactory();

HttpClient配置

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("https", sslSocketFactory, 443));


ClientConnectionManager ccm = new ThreadSafeClientConnManager(params, registry);


HttpClient httpClient = new DefaultHttpClient(ccm, params);

HttpsUrlConnection

HttpsURLConnection.setDefaultSSLSocketFactory(sslSocketFactory);

我还需要声明一下,我是这个库的维护者。