Android上的Apache HttpClient产生CertPathValidatorException(IssuerName!= SubjectName)

我正在开发一个Android应用程序访问一些battle.net( https://eu.battle.net )帐户数据(魔兽世界),我使用org.apache.http.client.HttpClient来这样做。

这是我正在使用的代码:

  public static final String USER_AGENT = "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.2.8) Gecko/20100722 Firefox/3.6.8 (.NET CLR 3.5.30729)"; public static class MyHttpClient extends DefaultHttpClient { final Context context; public MyHttpClient(Context context) { super(); this.context = context; } @Override protected ClientConnectionManager createClientConnectionManager() { SchemeRegistry registry = new SchemeRegistry(); registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80)); // Register for port 443 our SSLSocketFactory with our keystore // to the ConnectionManager registry.register(new Scheme("https", newSslSocketFactory(), 443)); return new SingleClientConnManager(getParams(), registry); } private SSLSocketFactory newSslSocketFactory() { try { // Get an instance of the Bouncy Castle KeyStore format KeyStore trusted = KeyStore.getInstance("BKS"); // Get the raw resource, which contains the keystore with // your trusted certificates (root and any intermediate certs) InputStream in = context.getResources().openRawResource(R.raw.battlenetkeystore); try { // Initialize the keystore with the provided trusted certificates // Also provide the password of the keystore trusted.load(in, "mysecret".toCharArray()); } finally { in.close(); } // Pass the keystore to the SSLSocketFactory. The factory is responsible // for the verification of the server certificate. SSLSocketFactory sf = new SSLSocketFactory(trusted); // Hostname verification from certificate // http://hc.apache.org/httpcomponents-client-ga/tutorial/html/connmgmt.html#d4e506 sf.setHostnameVerifier(SSLSocketFactory.STRICT_HOSTNAME_VERIFIER); return sf; } catch (Exception e) { throw new AssertionError(e); } } } private static void maybeCreateHttpClient(Context context) { if (mHttpClient == null) { mHttpClient = new MyHttpClient(context); final HttpParams params = mHttpClient.getParams(); HttpConnectionParams.setConnectionTimeout(params, REGISTRATION_TIMEOUT); HttpConnectionParams.setSoTimeout(params, REGISTRATION_TIMEOUT); ConnManagerParams.setTimeout(params, REGISTRATION_TIMEOUT); Log.d(TAG, LEAVE + "maybeCreateHttpClient()"); } } public static boolean authenticate(String username, String password, Handler handler, final Context context) { final HttpResponse resp; final ArrayList<NameValuePair> params = new ArrayList<NameValuePair>(); params.add(new BasicNameValuePair(PARAM_USERNAME, username)); params.add(new BasicNameValuePair(PARAM_PASSWORD, password)); HttpEntity entity = null; try { entity = new UrlEncodedFormEntity(params); } catch (final UnsupportedEncodingException e) { // this should never happen. throw new AssertionError(e); } final HttpPost post = new HttpPost(THE_URL); post.addHeader(entity.getContentType()); post.addHeader("User-Agent", USER_AGENT); post.setEntity(entity); maybeCreateHttpClient(context); if (mHttpClient == null) { return false; } try { resp = mHttpClient.execute(post); } catch (final IOException e) { Log.e(TAG, "IOException while authenticating", e); return false; } finally { } } 

密钥库被这样检索(通过OpenSSL):

 openssl s_client -connect eu.battle.net:443 -showcerts 

我已经将生成的证书( http://vipsaran.webs.com/openssl_output.txt )与从Firefox( http://vipsaran.webs.com/Firefox_output.zip )导出的证书进行了比较,它们是相同的。

通过遵循这个博客的build议,我设置了上面的代码,并将(根和中间)证书导入到用于HttpClient的密钥库(battlenetkeystore.bks)。

这是我用来将证书导入密钥库的命令:

 keytool -importcert -v -file ~/lib/ThawteSSLCA.crt -alias thawtesslca -keystore ~/lib/battlenetkeystore.bks -provider org.bouncycastle.jce.provider.BouncyCastleProvider -providerpath ~/lib/bcprov-jdk16-145.jar -storetype BKS -storepass mysecret -keypass mysecret -keyalg "RSA" -sigalg "SHA1withRSA" keytool -importcert -v -file ~/lib/thawtePrimaryRootCA.crt -alias thawteprimaryrootca -keystore ~/lib/battlenetkeystore.bks -provider org.bouncycastle.jce.provider.BouncyCastleProvider -providerpath ~/lib/bcprov-jdk16-145.jar -storetype BKS -storepass mysecret -keypass mysecret -keyalg "RSA" -sigalg "SHA1withRSA" 

顺便说一句。 我也尝试没有-keyalg "RSA" -sigalg "SHA1withRSA" keytool -import ,但没有任何改变。

问题是,我得到这个错误:

 javax.net.ssl.SSLException: Not trusted server certificate at org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:371) at org.apache.http.conn.ssl.AbstractVerifier.verify(AbstractVerifier.java:92) at org.apache.http.conn.ssl.SSLSocketFactory.createSocket(SSLSocketFactory.java:381) at org.apache.http.impl.conn.DefaultClientConnectionOperator.openConnection(DefaultClientConnectionOperator.java:164) at org.apache.http.impl.conn.AbstractPoolEntry.open(AbstractPoolEntry.java:164) at org.apache.http.impl.conn.AbstractPooledConnAdapter.open(AbstractPooledConnAdapter.java:119) at org.apache.http.impl.client.DefaultRequestDirector.execute(DefaultRequestDirector.java:348) at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:555) at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:487) at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:465) at org.homedns.saran.android.wowcalendarsync.network.NetworkUtilities.authenticateWithPass(NetworkUtilities.java:346) at org.homedns.saran.android.wowcalendarsync.network.NetworkUtilities$1.run(NetworkUtilities.java:166) at org.homedns.saran.android.wowcalendarsync.network.NetworkUtilities$5.run(NetworkUtilities.java:278) Caused by: java.security.cert.CertificateException: java.security.cert.CertPathValidatorException: IssuerName(CN=thawte Primary Root CA, OU="(c) 2006 thawte, Inc. - For authorized use only", OU=Certification Services Division, O="thawte, Inc.", C=US) does not match SubjectName(CN=Thawte SSL CA, O="Thawte, Inc.", C=US) of signing certificate at org.apache.harmony.xnet.provider.jsse.TrustManagerImpl.checkServerTrusted(TrustManagerImpl.java:168) at org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:366) ... 12 more Caused by: java.security.cert.CertPathValidatorException: IssuerName(CN=thawte Primary Root CA, OU="(c) 2006 thawte, Inc. - For authorized use only", OU=Certification Services Division, O="thawte, Inc.", C=US) does not match SubjectName(CN=Thawte SSL CA, O="Thawte, Inc.", C=US) of signing certificate at org.bouncycastle.jce.provider.PKIXCertPathValidatorSpi.engineValidate(PKIXCertPathValidatorSpi.java:373) at java.security.cert.CertPathValidator.validate(CertPathValidator.java:202) at org.apache.harmony.xnet.provider.jsse.TrustManagerImpl.checkServerTrusted(TrustManagerImpl.java:164) ... 13 more 

我无法想出办法解决这个问题。 尝试以不同的顺序将证书导入到密钥存储区,aso。 但没有任何工作。

请帮忙 (请专注于基于Android的Apache HttpClient的解决scheme)。

我希望你现在已经有了自己的解决scheme,但是如果没有的话:

通过结合的见解

我设法通过以下类实现与https://eu.battle.net/login/en/login.xml的安全连接。; 请注意,由于根CA受到android的信任,因此不需要构build密钥库 – 问题在于证书是以错误的顺序返回的。

(免责声明:虽然没有花时间清理代码。)

EasyX509TrustManager:

 package com.trustit.trustme; import java.security.KeyStore; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import java.util.Date; import javax.net.ssl.TrustManager; import javax.net.ssl.TrustManagerFactory; import javax.net.ssl.X509TrustManager; public class EasyX509TrustManager implements X509TrustManager { private X509TrustManager standardTrustManager = null; /** * Constructor for EasyX509TrustManager. */ public EasyX509TrustManager(KeyStore keystore) throws NoSuchAlgorithmException, KeyStoreException { super(); TrustManagerFactory factory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); factory.init(keystore); TrustManager[] trustmanagers = factory.getTrustManagers(); if (trustmanagers.length == 0) { throw new NoSuchAlgorithmException("no trust manager found"); } this.standardTrustManager = (X509TrustManager) trustmanagers[0]; } /** * @see javax.net.ssl.X509TrustManager#checkClientTrusted(X509Certificate[],String authType) */ public void checkClientTrusted(X509Certificate[] certificates, String authType) throws CertificateException { standardTrustManager.checkClientTrusted(certificates, authType); } /** * @see javax.net.ssl.X509TrustManager#checkServerTrusted(X509Certificate[],String authType) */ public void checkServerTrusted(X509Certificate[] certificates, String authType) throws CertificateException { // Clean up the certificates chain and build a new one. // Theoretically, we shouldn't have to do this, but various web servers // in practice are mis-configured to have out-of-order certificates or // expired self-issued root certificate. int chainLength = certificates.length; if (certificates.length > 1) { // 1. we clean the received certificates chain. // We start from the end-entity certificate, tracing down by matching // the "issuer" field and "subject" field until we can't continue. // This helps when the certificates are out of order or // some certificates are not related to the site. int currIndex; for (currIndex = 0; currIndex < certificates.length; ++currIndex) { boolean foundNext = false; for (int nextIndex = currIndex + 1; nextIndex < certificates.length; ++nextIndex) { if (certificates[currIndex].getIssuerDN().equals( certificates[nextIndex].getSubjectDN())) { foundNext = true; // Exchange certificates so that 0 through currIndex + 1 are in proper order if (nextIndex != currIndex + 1) { X509Certificate tempCertificate = certificates[nextIndex]; certificates[nextIndex] = certificates[currIndex + 1]; certificates[currIndex + 1] = tempCertificate; } break; } } if (!foundNext) break; } // 2. we exam if the last traced certificate is self issued and it is expired. // If so, we drop it and pass the rest to checkServerTrusted(), hoping we might // have a similar but unexpired trusted root. chainLength = currIndex + 1; X509Certificate lastCertificate = certificates[chainLength - 1]; Date now = new Date(); if (lastCertificate.getSubjectDN().equals(lastCertificate.getIssuerDN()) && now.after(lastCertificate.getNotAfter())) { --chainLength; } } standardTrustManager.checkServerTrusted(certificates, authType); } /** * @see javax.net.ssl.X509TrustManager#getAcceptedIssuers() */ public X509Certificate[] getAcceptedIssuers() { return this.standardTrustManager.getAcceptedIssuers(); } } 

EasySSLSocketFactory

 package com.trustit.trustme; import java.io.IOException; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.Socket; import java.net.UnknownHostException; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLSocket; import javax.net.ssl.TrustManager; import org.apache.http.conn.ConnectTimeoutException; import org.apache.http.conn.scheme.LayeredSocketFactory; import org.apache.http.conn.scheme.SocketFactory; import org.apache.http.params.HttpConnectionParams; import org.apache.http.params.HttpParams; public class EasySSLSocketFactory implements SocketFactory, LayeredSocketFactory { private SSLContext sslcontext = null; private static SSLContext createEasySSLContext() throws IOException { try { SSLContext context = SSLContext.getInstance("TLS"); context.init(null, new TrustManager[] { new EasyX509TrustManager(null) }, null); return context; } catch (Exception e) { throw new IOException(e.getMessage()); } } private SSLContext getSSLContext() throws IOException { if (this.sslcontext == null) { this.sslcontext = createEasySSLContext(); } return this.sslcontext; } /** * @see org.apache.http.conn.scheme.SocketFactory#connectSocket(java.net.Socket, java.lang.String, int, * java.net.InetAddress, int, org.apache.http.params.HttpParams) */ public Socket connectSocket(Socket sock, String host, int port, InetAddress localAddress, int localPort, HttpParams params) throws IOException, UnknownHostException, ConnectTimeoutException { int connTimeout = HttpConnectionParams.getConnectionTimeout(params); int soTimeout = HttpConnectionParams.getSoTimeout(params); InetSocketAddress remoteAddress = new InetSocketAddress(host, port); SSLSocket sslsock = (SSLSocket) ((sock != null) ? sock : createSocket()); if ((localAddress != null) || (localPort > 0)) { // we need to bind explicitly if (localPort < 0) { localPort = 0; // indicates "any" } InetSocketAddress isa = new InetSocketAddress(localAddress, localPort); sslsock.bind(isa); } sslsock.connect(remoteAddress, connTimeout); sslsock.setSoTimeout(soTimeout); return sslsock; } /** * @see org.apache.http.conn.scheme.SocketFactory#createSocket() */ public Socket createSocket() throws IOException { return getSSLContext().getSocketFactory().createSocket(); } /** * @see org.apache.http.conn.scheme.SocketFactory#isSecure(java.net.Socket) */ public boolean isSecure(Socket socket) throws IllegalArgumentException { return true; } /** * @see org.apache.http.conn.scheme.LayeredSocketFactory#createSocket(java.net.Socket, java.lang.String, int, * boolean) */ public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException, UnknownHostException { return getSSLContext().getSocketFactory().createSocket(socket, host, port, autoClose); } // ------------------------------------------------------------------- // javadoc in org.apache.http.conn.scheme.SocketFactory says : // Both Object.equals() and Object.hashCode() must be overridden // for the correct operation of some connection managers // ------------------------------------------------------------------- public boolean equals(Object obj) { return ((obj != null) && obj.getClass().equals(EasySSLSocketFactory.class)); } public int hashCode() { return EasySSLSocketFactory.class.hashCode(); } } 

MyHttpClient

 package com.trustit.trustme; 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.impl.client.DefaultHttpClient; import org.apache.http.impl.conn.SingleClientConnManager; import org.apache.http.params.HttpParams; import android.content.Context; public class MyHttpClient extends DefaultHttpClient { final Context context; public MyHttpClient(HttpParams hparms, Context context) { super(hparms); this.context = context; } @Override protected ClientConnectionManager createClientConnectionManager() { SchemeRegistry registry = new SchemeRegistry(); registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80)); // Register for port 443 our SSLSocketFactory with our keystore // to the ConnectionManager registry.register(new Scheme("https", new EasySSLSocketFactory(), 443)); //http://blog.synyx.de/2010/06/android-and-self-signed-ssl-certificates/ return new SingleClientConnManager(getParams(), registry); } } 

TrustMe(活动)

 package com.trustit.trustme; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import org.apache.http.HttpResponse; import org.apache.http.client.ClientProtocolException; import org.apache.http.client.HttpClient; import org.apache.http.client.methods.HttpGet; import org.apache.http.params.BasicHttpParams; import org.apache.http.params.HttpConnectionParams; import org.apache.http.params.HttpParams; import android.app.Activity; import android.os.Bundle; import android.widget.TextView; public class TrustMe extends Activity { /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); TextView tv = (TextView)findViewById(R.id.tv1); HttpParams httpParameters = new BasicHttpParams(); // Set the timeout in milliseconds until a connection is established. int timeoutConnection = 10000; HttpConnectionParams.setConnectionTimeout(httpParameters, timeoutConnection); // Set the default socket timeout (SO_TIMEOUT) // in milliseconds which is the timeout for waiting for data. int timeoutSocket = 10000; HttpConnectionParams.setSoTimeout(httpParameters, timeoutSocket); // Instantiate the custom HttpClient HttpClient client = new MyHttpClient(httpParameters, getApplicationContext()); HttpGet request = new HttpGet("https://eu.battle.net/login/en/login.xml"); BufferedReader in = null; try { HttpResponse response = client.execute(request); in = new BufferedReader(new InputStreamReader(response.getEntity().getContent())); StringBuffer sb = new StringBuffer(""); String line = ""; String NL = System.getProperty("line.separator"); while ((line = in.readLine()) != null) { sb.append(line + NL); } in.close(); String page = sb.toString(); //System.out.println(page); tv.setText(page); } catch (ClientProtocolException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { if (in != null) { try { in.close(); } catch (IOException e) { e.printStackTrace(); } } } } } 

看着“openssl s_client -connect eu.battle.net:443”,我看到下面的证书链:

 Certificate chain 0 s:/C=US/ST=California/L=Irvine/O=Blizzard Entertainment, Inc./CN=*.battle.net i:/C=US/O=Thawte, Inc./CN=Thawte SSL CA 1 s:/C=US/O=thawte, Inc./OU=Certification Services Division/OU=(c) 2006 thawte, Inc. - For authorized use only/CN=thawte Primary Root CA i:/C=ZA/ST=Western Cape/L=Cape Town/O=Thawte Consulting cc/OU=Certification Services Division/CN=Thawte Premium Server CA/emailAddress=premium-server@thawte.com 2 s:/C=US/O=Thawte, Inc./CN=Thawte 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 

请注意,这是不正确的。 链中的证书“n”的签发者应该与证书“n + 1”的主题相匹配。 最后一个证书的签发者应该是自签名的(subject == issuer),并且在技术上不包括在内。

正确的链会按这样的顺序排列:

 Certificate chain 0 s:/C=US/ST=California/L=Irvine/O=Blizzard Entertainment, Inc./CN=*.battle.net i:/C=US/O=Thawte, Inc./CN=Thawte SSL CA 1 s:/C=US/O=Thawte, Inc./CN=Thawte 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 2 s:/C=US/O=thawte, Inc./OU=Certification Services Division/OU=(c) 2006 thawte, Inc. - For authorized use only/CN=thawte Primary Root CA i:/C=ZA/ST=Western Cape/L=Cape Town/O=Thawte Consulting cc/OU=Certification Services Division/CN=Thawte Premium Server CA/emailAddress=premium-server@thawte.com 

Android浏览器通过在android.net.http.CertificateChainValidator代码重新sorting证书链之前处理失序链,然后再通过validation。

  136 // Clean up the certificates chain and build a new one. 137 // Theoretically, we shouldn't have to do this, but various web servers 138 // in practice are mis-configured to have out-of-order certificates or 139 // expired self-issued root certificate. 140 int chainLength = serverCertificates.length; 141 if (serverCertificates.length > 1) { 142 // 1. we clean the received certificates chain. 143 // We start from the end-entity certificate, tracing down by matching 144 // the "issuer" field and "subject" field until we can't continue. 145 // This helps when the certificates are out of order or 146 // some certificates are not related to the site. 147 int currIndex; 148 for (currIndex = 0; currIndex < serverCertificates.length; ++currIndex) { 149 boolean foundNext = false; 150 for (int nextIndex = currIndex + 1; 151 nextIndex < serverCertificates.length; 152 ++nextIndex) { 153 if (serverCertificates[currIndex].getIssuerDN().equals( 154 serverCertificates[nextIndex].getSubjectDN())) { 155 foundNext = true; 156 // Exchange certificates so that 0 through currIndex + 1 are in proper order 157 if (nextIndex != currIndex + 1) { 158 X509Certificate tempCertificate = serverCertificates[nextIndex]; 159 serverCertificates[nextIndex] = serverCertificates[currIndex + 1]; 160 serverCertificates[currIndex + 1] = tempCertificate; 161 } 162 break; 163 } 164 } 165 if (!foundNext) break; 166 } 167 168 // 2. we exam if the last traced certificate is self issued and it is expired. 169 // If so, we drop it and pass the rest to checkServerTrusted(), hoping we might 170 // have a similar but unexpired trusted root. 171 chainLength = currIndex + 1; 172 X509Certificate lastCertificate = serverCertificates[chainLength - 1]; 173 Date now = new Date(); 174 if (lastCertificate.getSubjectDN().equals(lastCertificate.getIssuerDN()) 175 && now.after(lastCertificate.getNotAfter())) { 176 --chainLength; 177 } 178 } 

要在您自己的应用程序中处理这个问题,您希望从SSLContext创build自己的javax.net.ssl.SSLSocketFactory,并使用X509TrustManager进行初始化,在调用默认的TrustManagerFactory TrustManager之前对链进行重新sorting。

我最近没有看过Apache HTTP Client代码,看看如何将自定义的javax.net.ssl.SSLSocketFactory提供给它们的SSLSocketFactory包装器,但应该是可能的(或者不要使用Apache HTTP Client,只是使用new URL(“https:// ..”).openConnection()允许您在HttpsURLConnection上指定自定义的javax.net.ssl.SSLSocketFactory。

最后,请注意,您只需要将自签名的根CA导入到您的密钥库中(并且只有当它不在系统存储中,但是我只是检查了此CA并不存在于froyo中)。 你想在这种情况下的CA有主题:

 /C=US/O=thawte, Inc./OU=Certification Services Division/OU=(c) 2006 thawte, Inc. - For authorized use only/CN=thawte Primary Root CA 

我想现在你的问题已经解决了,但是我也遇到了同样的问题,而且我也在努力寻找正确的解决scheme。 也许它有助于某人。

我也使用Antoine的博客的代码,但我改变了用于SSLSocketFactory的构造函数。

所以我用

 SSLSocketFactory sf = new SSLSocketFactory(certStore, "some_password", trustStore); 

为此我创build了两个KeyStore

 KeyStore trustStore = KeyStore.getInstance("BKS"); KeyStore certStore = KeyStore.getInstance("BKS"); InputStream in = context.getResources().openRawResource(R.raw.signature_certstore); try { certStore.load(in, "some_password".toCharArray()); } finally { in.close(); } in = context.getResources().openRawResource(R.raw.signature_truststore); try { trustStore.load(in, "some_password".toCharArray()); } finally { in.close(); } 

我与Portecle创造了BKS商店。 在signature_truststore.bks中,我导入了根证书,而在signature_certstore.bks中,您必须导入一个或多个中间证书。

其余的代码与博客中的完全一样。

我没有解决scheme来修复path。 但我有一个解决scheme来忽略证书。 我使用这种方法忽略开发中的自签名证书。 看看是否有帮助。

  protected final static ClientConnectionManager clientConnectionManager; protected final static HttpParams params; // ...... SchemeRegistry schemeRegistry = new SchemeRegistry(); schemeRegistry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80)); schemeRegistry.register(new Scheme("https", new EasySSLSocketFactory(), 443)); params = new BasicHttpParams(); params.setParameter(ConnManagerPNames.MAX_TOTAL_CONNECTIONS, 1); params.setParameter(ConnManagerPNames.MAX_CONNECTIONS_PER_ROUTE, new ConnPerRouteBean(1)); params.setParameter(HttpProtocolParams.USE_EXPECT_CONTINUE, false); HttpProtocolParams.setUserAgent(params, "android-client-v1.0"); HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1); HttpProtocolParams.setContentCharset(params, "utf8"); clientConnectionManager = new ThreadSafeClientConnManager(params, schemeRegistry); // and later do this HttpClient client = new DefaultHttpClient(clientConnectionManager, params); HttpGet request = new HttpGet(uri); HttpResponse response = client.execute(request); 

这可能有助于: http ://blog.antoine.li/index.php/2010/10/android-trusting-ssl-certificates/,你有一个CA的可信证书(如versign或Geotrust)? 或者你正在使用自签名证书…我面临类似的问题,并解决了今天…

顺便说一句,我是上面提到的博客的作者;)我尝试在这里回答你的问题。

我已经从firefox和openssl看了你的证书输出,发现了一些有趣的东西。

在openssl输出中查看根CA证书(索引1)。 发行者名称为:Thawte Premium Server CA主题名称为:thawte主要根CA主题和发行者名称不同。 因此,此证书不被视为根CA,因为它是由另一个实例发布的。 因此,bouncycastle提供商正在考虑将此证书作为根CA,但由于问题和主题不同,所以会提出投诉。

我不知道你是如何获得“错误的”根CA证书。 当我查看firefox中的根CA证书时,主题和发行者是相同的,因为它应该是。

尝试find正确的根CA并再试一次。

希望这可以帮助。 问候和祝你好运;)

我终于解决了我的同样的“IssuerName不符合SubjectName”exception。 我遵循Antoine的相同的博客,这里描述了很多次,下面是如何使它最终工作:

1)我们的网站使用来自GeoTrust的两个证书:GeoTrust SSL CA向我们颁发中间CA,GeoTrust Global CA向GeoTrust SSL CA颁发根CA;

2)如果只使用1)中的根CA和根CA以及中间CA,则会得到不匹配例外,因为Android仅支持有限数量的受信任根CA ,并且GeoTrust Global CA不在列表中;

3)在www.geotrust.com的支持页面,有一个名为GeoTrust Cross Root CA的页面,只需下载它,将其保存为crossroot.pem这样的名称,然后使用此命令生成密钥库:

C:\ Program Files \ Java \ jdk1.6.0_24 \ bin> keytool -importcert -v -trustcacerts -file c:\ ssl \ crossroot.pem -alias newroot -keystore c:\ ssl \ crossroot.bks -provider org.bouncycastle .jce.provider.BouncyCastleProvider -providerpath“c:\ downloads \ bcprov-jdk16-145.jar”-storetype BKS -storepass mysecret

安东尼博客的第2步有一个链接,下载BouncyCastleProvider;

4)将密钥库文件添加到Android项目,它的工作原理 – 这是有道理的,因为现在Androidfind一个受信任的根Equifax安全authentication机构(见上面列表1 )其SubjectName GeoTrust全球CA匹配我们的网站的根IssuerName。

5)博客步骤3中的代码正常工作,为了使其更加完整,我复制了以下testing代码:

  HttpResponse response = client.execute(get); HttpEntity entity = response.getEntity(); BufferedReader in = new BufferedReader(new InputStreamReader(entity.getContent())); String line; while ((line = in.readLine()) != null) System.out.println(line); in.close(); 

这个问题的难点在于,如果你的根CA的发行者不在Android的信任列表中,你必须从颁发你证书的公司那里得到它 – 要求他们为你提供一个具有根发行者作为Android的可信根CA之一。