web-dev-qa-db-ja.com

AndroidでApache HttpClientがCertPathValidatorException(IssuerName!= SubjectName)を生成する)

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 )そしてそれらは同じです。

このブログ に関するアドバイスに従って、上記のコードを設定し、(ルートおよび中間)証明書を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"

ところで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

そして私はそれを解決する方法を理解することができません。証明書を別の順序でキーストアにインポートしようとしました。しかし、何もうまくいきませんでした。

助けてください(そしてAndroidのApache HttpClientのみに基づくソリューションに集中してください)。

33
Saran

私はあなたが今あなた自身の解決策を持っていると期待していますが、もしそうでなければ:

からの洞察を組み合わせることにより

以下のクラスだけで https://eu.battle.net/login/en/login.xml への安全な接続を確立することができました。ルートCAは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();
          }
        }                       
        }
    }      
}
56
Andy G

「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/[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

故障していることに注意してください。チェーン内の証明書「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/[email protected]

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         }

独自のアプリでこれに対処するには、デフォルトのTrustManagerFactoryが提供するTrustManagerを呼び出す前にチェーンを並べ替えるX509TrustManagerで初期化されたSSLContextから独自のjavax.net.ssl.SSLSocketFactoryを作成します。

私は最近、Apache HTTPクライアントのコードを見て、カスタムjavax.net.ssl.SSLSocketFactoryをSSLSocketFactoryラッパーに提供する方法を確認していませんが、それは可能です(またはApache HTTPクライアントを使用せず、新しい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
17
bdc

あなたの問題は現在解決されていると思いますが、私も同じ問題を抱えていて、正しい解決策を見つけるのに苦労しました。多分それは誰かを助ける。

Antoine's Blog のコードも使用しましたが、SSLSocketFactoryに使用するコンストラクターを変更しました。

だから私は

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

そのため、2つのキーストアを作成しました

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に1つ以上の中間証明書をインポートする必要があります。

残りのコードは、ブログのコードとまったく同じです。

4
Sandra

ところで、私は上記のブログの著者です;)私はここであなたの質問に答えようとします。

Firefoxとopensslからの証明書の出力を確認したところ、興味深いものが見つかりました。

Openssl出力でルートCA証明書(インデックス1)を確認します。発行者名は次のとおりです。ThawtePremium Server CAサブジェクト名は次のとおりです。thawteプライマリルートCAサブジェクト名と発行者名は異なります。したがって、この証明書は別のインスタンスによって発行されたため、ルートCAとは見なされません。したがって、バウンスキャッスルプロバイダーはこの証明書をルートCAと見なしていますが、問題と件名が異なるため、文句を言われます。

どのようにして「間違った」ルートCA証明書を取得したのかわかりません。 FirefoxでルートCA証明書を見ると、サブジェクトと発行者は同じであるはずです。

適切なルートCAを取得して、再試行してください。

お役に立てれば。挨拶と幸運;)

0
saxos

最後に、同じ「IssuerNameがSubjectNameと一致しない」という例外を解決しました。私はAntoineと同じブログを何度もフォローしてきましたが、ここで何度も説明されていますが、最終的に機能させる方法は次のとおりです。

1)私たちのウェブサイトはGeoTrustからの2つの証明書を使用しています。中間CAはGeoTrust SSL CAによって発行され、ルートCAはGeoTrust Global CAによってGeoTrust SSL CAに発行されます。

2)ルートCAのみ、または1)のルート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

Antonieによるブログのステップ2には、BouncyCastleProviderをダウンロードするためのリンクがあります。

4)キーストアファイルをAndroidプロジェクトに追加すると機能します。これは理にかなっています=Androidが信頼されたルートEquifax Secure Certificate Authorityを見つけるためです(上記のリスト 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();

この問題の難しい部分は、ルートCAの発行者がAndroidの信頼済みリストにない場合、証明書を発行する会社から入手する必要があります-ルートを持つクロスルートCAを提供するよう依頼してくださいAndroidの信頼されたルートCAの1つとしての発行者。

0
Jeff Tang

パスを修正する解決策がありません。しかし、私は証明書を無視する解決策を持っています。このメソッドを使用して、開発中の自己署名証明書を無視します。それが役立つかどうかを確認します。

 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
Amir Raminfar

これは役立つかもしれません: http://blog.antoine.li/index.php/2010/10/Android-trusting-ssl-certificates/ 、CAからの信頼できる証明書(versignまたはGeotrust)?または、自己署名証明書を使用しています...同様の問題に直面していて、今日解決しました...

0
vinay