2010-11-06 9 views
33

Tôi đang phát triển một ứng dụng Android để truy cập một số battle.net (https://eu.battle.net) dữ liệu tài khoản (cho World of Warcraft) và tôi đang sử dụng org.apache.http.client.HttpClient để làm như vậy.Apache HttpClient trên Android sản xuất CertPathValidatorException (IssuerName! = SubjectName)

Đây là mã Tôi đang sử dụng:

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

Các keystore được lấy (bằng OpenSSL) như thế này:

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

Tôi đã so sánh các chứng chỉ lệnh sản xuất (http://vipsaran.webs.com/openssl_output.txt) với những cái tôi đã xuất từ ​​Firefox (http://vipsaran.webs.com/Firefox_output.zip) và chúng giống nhau.

Bằng cách làm theo lời khuyên trên this blog, tôi đã thiết lập mã trên và nhập chứng chỉ (gốc và trung gian) vào kho khóa (battlenetkeystore.bks) được sử dụng cho HttpClient.

Đây là các lệnh tôi sử dụng để nhập khẩu các Certs để keystore:

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" 

Btw. Tôi cũng đã thử keytool -import mà không cần -keyalg "RSA" -sigalg "SHA1withRSA", nhưng không có thay đổi.

Vấn đề là tôi nhận được lỗi này:

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 

và tôi không thể hình dung một cách để giải quyết nó. Đã cố nhập các chứng chỉ vào kho khóa theo thứ tự khác, aso. nhưng không có gì hiệu quả.

Vui lòng giúp (và vui lòng tập trung vào các giải pháp dựa trên Apache HttpClient của Android chỉ).

+0

+1 Chắc chắn là một câu hỏi thú vị nhưng, tôi hơi băn khoăn về tính hợp pháp của nó. – Terrance

Trả lời

56

Tôi hy vọng bạn đã có giải pháp riêng của bạn bằng cách bây giờ, nhưng nếu không nói là:

Bằng cách kết hợp những hiểu biết từ

blog của

Tôi đã quản lý để đạt được kết nối an toàn tới https://eu.battle.net/login/en/login.xml chỉ với các lớp sau. Lưu ý rằng không cần phải xây dựng kho khóa vì CA gốc được tin cậy bởi android - vấn đề đơn giản là các chứng chỉ được trả về theo thứ tự sai.

(Tuyên bố từ chối trách nhiệm: Không bỏ ra bất kỳ lúc nào để dọn sạch mã.)

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 (hoạt động)

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

** Bạn đã hoàn thành nó! :) ** Điều tôi đã bỏ lỡ là sắp xếp chuỗi chứng chỉ. Cảm ơn các giải pháp - đơn giản, nhưng hiệu quả! – Saran

+0

Giả sử tôi muốn làm https auth bên trong webview của riêng tôi, tôi có thể làm theo phương pháp này? –

+7

Tôi gặp lỗi: "Không thể tìm thấy tin cậy cho đường dẫn chứng nhận". –

0

Tôi không có giải pháp để sửa đường dẫn. Nhưng tôi có một giải pháp để bỏ qua certs. Tôi sử dụng phương pháp này để bỏ qua các chứng chỉ tự ký trong quá trình phát triển. Xem nếu nó giúp.

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

Ok ... nhưng, tôi e là một phần quan trọng bị thiếu - nguồn cho lớp EasySSLSocketFactory :) Bạn có thể đính kèm nó không? – Saran

+0

Rất tiếc. Tôi thực sự googled nó trước vì vậy tôi quên. http://code.google.com/p/transdroid/source/browse/trunk/src/org/xmlrpc/android/EasySSLSocketFactory.java?spec=svn103&r=103 –

+0

Cảm ơn. Và lớp EasyX509TrustManager? :) Tôi đoán, một trong những từ cùng một gói (từ thân cây) nên làm công việc;) – Saran

0

điều này có thể giúp: http://blog.antoine.li/index.php/2010/10/android-trusting-ssl-certificates/, Bạn có chứng chỉ đáng tin cậy từ CA (như versign hay Geotrust)? hoặc bạn đang sử dụng chứng chỉ tự ký ... tôi đã gặp phải sự cố tương tự và đã giải quyết nó ngay hôm nay ...

+0

Cảm ơn bạn đã trả lời Có, tôi biết blog đó (và tôi cũng đã đề cập rằng trong bài đăng của mình : "Bằng cách làm theo lời khuyên trên blog này, tôi đã thiết lập ..."). Thật không may tác giả cũng không thành công khi kết nối: (Nhận xét của tôi trên blog đó có tên "Saran". Chỉ cần thử sử dụng mã của anh ấy hoặc bất kỳ mã nào khác để mở trang này bằng Apache HttpClient của Android: https://eu.battle.net/login/en/login.xml?ref=http%3A%2F%2Feu.wowarmory.com%2Fvault%2Fcharacter-select.xml&app=armory&locale = vi_US – Saran

0

Btw, tôi là tác giả từ blog được đề cập ở trên;) Tôi cố gắng trả lời câu hỏi của bạn tại đây.

Tôi đã xem kết quả đầu ra chứng chỉ của bạn từ firefox và openssl và tìm thấy một cái gì đó thú vị.

Tìm chứng chỉ ca gốc (chỉ mục 1) tại đầu ra openssl của bạn. Tên tổ chức phát hành là: Thawte Premium Server CA Tên chủ đề là: thawte Primary Root CA Tên chủ đề và tổ chức phát hành khác nhau. Do đó, chứng chỉ này không được coi là Root CA, bởi vì nó được cấp bởi một cá thể khác. Vì vậy, nhà cung cấp bouncycastle đang xem xét chứng chỉ này là Root CA nhưng nó phàn nàn vì các vấn đề và chủ đề khác nhau.

Tôi không biết làm thế nào bạn có được chứng chỉ CA gốc "sai".Khi tôi nhìn vào chứng chỉ CA gốc trong firefox, chủ đề và nhà phát hành là như nhau, như nó phải được.

Cố gắng lấy đúng CA gốc và thử lại.

Hy vọng điều này sẽ hữu ích. Chúc mừng và chúc may mắn;)

+0

Đã thử. Không thành công :(Cảm ơn anyways :) – Saran

17

Nhìn vào "openssl s_client -connect eu.battle.net:443", tôi thấy chuỗi cert sau:

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/[email protected] 
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 

Lưu ý rằng là ra khỏi trật tự. tổ chức phát hành chứng chỉ "n" trong chuỗi phải khớp với chủ đề của "n + 1". Tổ chức phát hành chứng chỉ cuối cùng phải được tự ký (chủ thể == tổ chức phát hành) và không bao gồm kỹ thuật.

Chuỗi đúng sẽ được đặt hàng như thế này:

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/[email protected] 

Trình duyệt Android đối phó với ra khỏi chuỗi trật tự bởi có mã android.net.http.CertificateChainValidator của nó sắp xếp lại các chuỗi cert trước khi đi qua nó để xác nhận.

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   } 

Để đối phó với điều này trong ứng dụng của riêng bạn, bạn muốn tạo javax.net.ssl.SSLSocketFactory riêng của bạn từ một SSLContext được khởi tạo với một X509TrustManager đó sắp xếp lại chuỗi trước khi gọi các TrustManagerFactory mặc định cung cấp TrustManager.

Gần đây tôi chưa xem mã Apache HTTP Client để xem cách cung cấp javax.net.ssl.SSLSocketFactory tùy chỉnh cho trình bao bọc SSLSocketFactory của họ, nhưng có thể (hoặc không sử dụng Apache HTTP Client và chỉ sử dụng URL mới ("https: // ..") .openConnection() cho phép bạn chỉ định javax.net.ssl.SSLSocketFactory tùy chỉnh trên HttpsURLConnection.

Cuối cùng, lưu ý rằng bạn chỉ cần nhập CA gốc tự ký vào kho khóa của bạn (và chỉ khi nó chưa có trong kho hệ thống, nhưng tôi vừa kiểm tra và CA này không có trong froyo) CA bạn muốn trong trường hợp này có chủ đề:

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

Cảm ơn bạn đã phân tích chi tiết và gợi ý. Ghi đè thứ tự mà tôi sẽ không đi vào (vì tôi nghĩ mình sẽ phải ghi đè lên nhiều lớp để đến đó). Cách với SSLSocketFactory của riêng tôi (như được mô tả trong tài liệu HttpClient: http://bit.ly/9HefD6) không hoạt động trong Android (vì nó đã thực hiện riêng SSLSocketFactory chỉ chấp nhận KeyStore làm tham số cho hàm tạo). – Saran

+0

Không biết nếu nó quá nhiều để hỏi, nhưng xem xét bạn đã đi sâu vào chuỗi cert, bạn có thể thử tạo chuỗi của bạn (từ https://eu.battle.net) và sử dụng nó trong hoạt động thử nghiệm này: http : //vipsaran.webs.com/TestTrusted.java – Saran

+0

Về tuyến đường HttpsURLConnection. Vâng, kết nối làm việc (như tôi sử dụng HostnameVerifier riêng), nhưng tôi đã bị mắc kẹt với yêu cầu chuyển tiếp và muốn làm cho nó làm việc với apache client như tuyến đường đó dường như ít hướng dẫn sử dụng (cộng với nó làm việc trước). – Saran

0

Cuối cùng tôi đã giải quyết cùng một ngoại lệ "IssuerName không khớp với SubjectName" của mình. Tôi đã theo dõi cùng một blog của Antoine và những gì được mô tả ở đây nhiều lần, và đây là cách làm cho nó hoạt động sau cùng:

1) Trang web của chúng tôi sử dụng hai chứng chỉ từ GeoTrust: CA trung gian do GeoTrust SSL CA cấp cho chúng tôi và CA gốc được cấp cho GeoTrust SSL CA của GeoTrust Global CA;

2) Nếu chỉ sử dụng CA gốc hoặc CA trung gian trong 1), tôi nhận được ngoại lệ không khớp, vì Android chỉ hỗ trợ a limited number of trusted root CA và GeoTrust Global CA không có trong danh sách;

3) Trong trang hỗ trợ của www.geotrust.com, có một trang có tên GeoTrust Cross Root CA, chỉ cần tải xuống nó , lưu nó vào tên như crossroot.pem và sử dụng lệnh này để tạo kho khóa:

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

Bước 2 của blog của Antonie có một liên kết để tải BouncyCastleProvider;

4) Thêm file keystore cho dự án Android, và nó hoạt động - điều này có ý nghĩa vì bây giờ Android tìm thấy một gốc đáng tin cậy Equifax Secure Certificate Authority (xem danh sách ở trên 1) mà SubjectName GeoTrust Global CA khớp với tên gốc của chúng tôi IssuerName của trang web của chúng tôi. hoàn chỉnh hơn, tôi đã sao chép mã thử nghiệm của mình bên dưới:

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

Phần khó khăn của vấn đề này là nếu nhà phát hành CA gốc không có trong danh sách đáng tin cậy của Android, bạn sẽ phải lấy nó từ công ty phát hành chứng chỉ - yêu cầu họ cung cấp cho bạn một CA gốc có tổ chức phát hành gốc là một trong những CA gốc đáng tin cậy của Android.

+0

Cảm ơn bạn đã đề xuất, nhưng nếu tôi chỉ nhập Cross Root CA, Tôi nhận được ngoại lệ "Chữ ký chưa được xác minh". Bằng cách theo dõi khách hàng tiềm năng của bạn, tôi phát hiện ra rằng tôi đã có CrossRootCA - đó là tệp thawtePrimaryRootCA.crt trong đính kèm/liên kết ban đầu của tôi - khi tôi so sánh nó với Cross Root CA từ trang web của Thawte [link] (https://search.thawte.com/support/ssl-digital-certificates/index?page=content&actp=CROSSLINK&id=SO13881). Cảm ơn bạn. – Saran

+0

Có thể bạn có thể giúp tôi với vấn đề ssl của tôi https://stackoverflow.com/questions/47554129/https-volley-invalid-header-issue cảm ơn trước –

4

Tôi đoán vấn đề của bạn đã được giải quyết ngay bây giờ, nhưng tôi đã có cùng một vấn đề và tôi cũng phải vật lộn một chút thời gian để tìm ra giải pháp đúng. Có lẽ nó giúp ai đó.

Tôi cũng đã sử dụng mã từ Antoine's Blog nhưng tôi đã thay đổi hàm tạo được sử dụng cho SSLSocketFactory.

Vì vậy, tôi sử dụng

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

do tôi tạo ra hai KeyStores

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

tôi tạo ra các Cửa hàng BKS với Portecle. Trong signature_truststore.bks, tôi đã nhập chứng chỉ gốc và trong tệp signature_certstore.bks, bạn phải nhập một hoặc nhiều chứng chỉ trung gian.

Phần còn lại của mã chính xác giống với mã từ blog.

+0

Cảm ơn bạn đã nỗ lực nhưng quy trình này cũng tạo ra "Chữ ký chưa được xác minh. " ngoại trừ tôi. Nếu bạn cảm thấy tò mò, bạn có thể thử kết nối với [url này] (https://eu.battle.net/login/en/login.xml) (chỉ cần sử dụng certs i đính kèm hoặc truy xuất chúng bằng 'openssl s_client -connect eu .battle.net: 443 -showcerts'). – Saran

+0

Có thể bạn có thể giúp tôi với vấn đề ssl của tôi https://stackoverflow.com/questions/47554129/https-volley-invalid-header-issue cảm ơn trước –