Apache HttpClient на Android создает исключение CertPathValidatorException (IssuerName! = SubjectName) - PullRequest
33 голосов
/ 06 ноября 2010

Я разрабатываю приложение для Android для доступа к некоторым Battle.net (https://eu.battle.net) данным учетной записи (для World of Warcraft) и использую 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)), и они совпадают.

Следуя советам по этому блогу , я настроил вышеуказанный код и импортировал (корневые и промежуточные) сертификаты в хранилище ключей (battlenetkeystore.bks), которое используется для HttpClient.

Это команды, которые я использовал для импорта сертификатов в хранилище ключей:

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. Я также пробовал keytool -import без -keyalg "RSA" -sigalg "SHA1withRSA", но без изменений.

Проблема в том, что я получаю эту ошибку:

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

и я не могу найти способ решить это. Попробовал импортировать сертификаты в хранилище ключей в другом порядке, так же. но ничего не получалось.

Пожалуйста, помогите (и, пожалуйста, обратите внимание на решения на базе Apache HttpClient для Android только ).

Ответы [ 7 ]

56 голосов
/ 09 июля 2011

Полагаю, у вас уже есть собственное решение, но если нет:

Сочетая идеи от

  • Блог Антуана Хаука
  • http://blog.synyx.de/2010/06/android-and-self-signed-ssl-certificates/
  • отличный ответ от BDC выше
  • Исходный код, который легко найти, для EasySSLSocketFactory и EasyX509TrustManager - предоставит ссылку, если мне не помешают (при первом ответе!)

Мне удалось установить безопасное соединение с https://eu.battle.net/login/en/login.xml только следующими классами. Обратите внимание, что создавать хранилище ключей не нужно, поскольку корневому ЦС доверяет android - проблема в том, что сертификаты возвращаются в неправильном порядке.

(Отказ от ответственности: не тратить время на очистку кода.)

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();
          }
        }                       
        }
    }      
}
17 голосов
/ 17 ноября 2010

Глядя на "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".Эмитент последнего сертификата должен быть самозаверяющим (субъект == эмитент) и технически не включен.

Правильная цепочка будет упорядочена следующим образом:

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 переупорядочить цепочку сертификатов перед передачей для проверки.

 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         }

Чтобы справиться с этим в вашем собственном приложенииВы хотите создать свой собственный javax.net.ssl.SSLSocketFactory из SSLContext, который был инициализирован с помощью X509TrustManager, который переупорядочивает цепочку перед вызовом TrustManagerFactory по умолчанию, предоставленного TrustManager.

Недавно я не просматривал код HTTP-клиента Apache, чтобы узнать, как предоставить свой пользовательский javax.net.ssl.SSLSocketFactory в их оболочку SSLSocketFactory, но это должно быть возможно (или просто не использовать ApacheHTTP Client и просто используйте новый URL ("https: // ..") .openConnection (), который позволяет вам указать пользовательский javax.net.ssl.SSLSocketFactory в HttpsURLConnection.

Наконец, обратите внимание, что вынужно только импортировать самозаверяющий корневой 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
4 голосов
/ 29 апреля 2011

Полагаю, ваша проблема решена прямо сейчас, но у меня была такая же проблема, и я тоже некоторое время пытался найти правильное решение. Может быть, это кому-то поможет.

Я также использовал код из Блога Антуана , но я изменил конструктор, используемый для SSLSocketFactory.

Поэтому я использую

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

для этого я создал два хранилища ключей

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

Я создал магазины BKS с Portecle . В signature_truststore.bks я импортировал корневой сертификат, а в signature_certstore.bks вы должны импортировать один или несколько промежуточных сертификатов.

Остальная часть кода точно такая же, как и в блоге.

0 голосов
/ 15 апреля 2011

Я наконец-то решил то же самое исключение «IssuerName не совпадает с SubjectName». Я следил за тем же блогом Антуана и тем, что описано здесь много раз, и вот как он наконец заработал:

1) Наш веб-сайт использует два сертификата от GeoTrust: промежуточный CA выдается нам GeoTrust SSL CA, а корневой CA выдается GeoTrust SSL CA от GeoTrust Global CA;

2) Если используется только корневой ЦС или оба корневых и промежуточных ЦС в 1), я получаю исключение несоответствия, поскольку Android поддерживает только ограниченное количество доверенных корневых ЦС и GeoTrust. Глобального ЦС нет в списке;

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" -старотип BKS -storepass mysecret

На втором этапе блога Антони есть ссылка для загрузки BouncyCastleProvider;

4) Добавьте файл хранилища ключей в проект Android, и он работает - это имеет смысл, потому что теперь Android находит доверенный корневой центр сертификации Equifax Secure (см. Список выше 1 ), у которого SubjectName GeoTrust Global CA соответствует корню нашего сайта IssuerName.

5) Код в шаге 3 блога работает нормально, и чтобы сделать его более полным, я скопировал свой тестовый код ниже:

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

Сложная часть этой проблемы заключается в том, что если эмитент вашего корневого ЦС не включен в список доверенных для Android, вам придется получить его от компании, которая выдает вам сертификаты, - попросите предоставить вам ЦС, который будет имеет корневой издатель в качестве одного из доверенных корневых центров сертификации Android.

0 голосов
/ 09 ноября 2010

Кстати, я автор упомянутого выше блога;) Я пытаюсь ответить на ваш вопрос здесь.

Я посмотрел на результаты вашего сертификата из firefox и openssl и нашел что-то интересное.

Посмотрите корневой сертификат CA (индекс 1) на ваш вывод openssl.Имя эмитента: Thawte Premium Server CA Имя субъекта: thawte Primary Root CA Имена субъекта и эмитента различаются.Следовательно, этот сертификат не считается корневым центром сертификации, поскольку он был выпущен другим экземпляром.Поэтому провайдер bouncycastle рассматривает этот сертификат в качестве корневого ЦС, но жалуется, потому что проблемы и предмет различаются.

Я понятия не имею, как вы получили «неправильный» сертификат корневого ЦС.Когда я смотрю на сертификат Root CA в Firefox, субъект и издатель совпадают, как и должно быть.

Попробуйте получить правильный Root CA и повторите попытку.

Надеюсь, это поможет,Привет и удачи;)

0 голосов
/ 08 ноября 2010

это может помочь: http://blog.antoine.li/index.php/2010/10/android-trusting-ssl-certificates/, у вас есть доверенные сертификаты от CA (например, versign или Geotrust)? или вы используете самозаверяющий сертификат ... я столкнулся с подобной проблемой и решил ее сегодня ...

0 голосов
/ 07 ноября 2010

У меня нет решения, чтобы исправить путь.Но у меня есть решение игнорировать сертификаты.Я использую этот метод, чтобы игнорировать самоподписанные сертификаты в разработке.Посмотрите, поможет ли это.

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