IT_Programming/Android_Java

Trusting all certificates using HttpClient over HTTPS

JJun ™ 2013. 6. 24. 02:14

 


출처: http://stackoverflow.com/questions/2642777/trusting-all-certificates-using-httpclient-over-https


 

You basically have four potential solutions to fix a "Not Trusted" exception on Android using httpclient:

기본적으로 HTTPClient 사용하여 Android에서 "신뢰할 수없는" 예외를 해결하기 위해 4개의 잠정적인

해결책이 있습니다:


1. Trust all certificates. Don't do this, unless you really know what you're doing.

    모든 인증서를 신뢰합니다. 정말 여러분이 무슨 일을 해야하는지 알고 있다면 이렇게 하지 마세요.

더보기

 

 

import java.io.IOException;
import java.net.Socket;
import java.net.UnknownHostException;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;

import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

import org.apache.http.conn.ssl.SSLSocketFactory;


public class MySSLSocketFactory extends SSLSocketFactory

{

    SSLContext sslContext = SSLContext.getInstance("TLS");

 

    public MySSLSocketFactory(KeyStore truststore) throws NoSuchAlgorithmException,

                                                         KeyManagementException, KeyStoreException, UnrecoverableKeyException

    {
        super(truststore);

        TrustManager tm = new X509TrustManager() {
            public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
            }

            public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
            }

            public X509Certificate[] getAcceptedIssuers() {
                return null;
            }
        };

        sslContext.init(null, new TrustManager[] { tm }, null);
    }

 

    @Override
    public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException, UnknownHostException {
        return sslContext.getSocketFactory().createSocket(socket, host, port, autoClose);
    }

    @Override
    public Socket createSocket() throws IOException {
        return sslContext.getSocketFactory().createSocket();
    }
}

 

 

 


 

// and use this class while creating instance of HttpClient.

public HttpClient getNewHttpClient()

{
    try

    {
        KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
        trustStore.load(null, null);

        SSLSocketFactory sf = new MySSLSocketFactory(trustStore);
        sf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);

        HttpParams params = new BasicHttpParams();
        HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
        HttpProtocolParams.setContentCharset(params, HTTP.UTF_8);

        SchemeRegistry registry = new SchemeRegistry();
        registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
        registry.register(new Scheme("https", sf, 443));

        ClientConnectionManager ccm = new ThreadSafeClientConnManager(params, registry);

        return new DefaultHttpClient(ccm, params);
    } catch (Exception e) {
        return new DefaultHttpClient();
    }
}

 

 

 

 

 


2. Create a custom SSLSocketFactory that trusts only your certificate.

   오직 여러분이 만든 인증서만을 신뢰하는 사용자 지정 SSLSocketFactory 만듭니다.

   This works as long as you know exactly which servers you're going to connect to, but as soon as

   you need to connect to a new server with a different SSL certificate, you'll need to update your app.

   이 작업은 당신이 연결을 해야하는 서버가 정확히 어떤 서버를 알고 있는 만큼 최대한 빨리 여러분이

    다른 SSL 인증서를 사용하여 새 서버에 연결함으로써 여러분은 여러분의 앱을 업데이트 해야 합니다.


 

3. Create a keystore file that contains Android's "master list" of certificates, then add your own.

    인증서들이 포함된 Android "마스터 목록" 포함된 KeyStore 파일을 만든 후에 이를 추가하는

     방법입니다.

    If any of those certs expire down the road, you are responsible for updating them in your app.

    I can't think of a reason to do this. 만약 인증서들 중 어떤 것이라도 만료 경우, 여러분은 앱

    업데이트 책임이 있습니다. 이는 당연한거겠죠.

 

4. Create a custom SSLSocketFactory that uses the built-in certificate KeyStore,

    but falls back on an alternate KeyStore for anything that fails to verify with the default.

    KeyStore 기본 인증서를 사용하지만, 기본적으로 확인하지 못한 것에 대해 다른 KeyStore

     대체 지정할 수 있는 SSLSocketFactory 만듭니다.

 

This answer uses solution #4, which seems to me to be the most robust.

나는 가장 강력하다는 것을 생각되는 솔루션 #4 설명할 것입니다.

 

The solution is to use an SSLSocketFactory that can accept multiple KeyStores, allowing you to supply your own KeyStore with your own certificates.

이 해결책은 여러분이 자신 인증서를 사용하여 자신의 키 스토어를 공급할 수 있도록 여러 KeyStore

받아 들일 수있는 SSLSocketFactory 사용하는 것입니다.

 

This allows you to load additional top-level certificates such as Thawte that might be missing on some Android devices. It also allows you to load your own self-signed certificates as well.

이것은 여러분 어떤 Android 장치에서 누락되더라도 Thawte와 같은 추가 최상위 인증서를 로드 할 수

있도록 해줍니다. 뿐만 아니라 (자신이 만든)자체 서명 인증서를 로드할 수 있도록 해줍니다.

 

It will use the built-in default device certificates first, and fall back on your additional certificates only as necessary. First, you'll want to determine which cert you are missing in your KeyStore. Run the following command:

이것은 기본 제공 기본 장치 인증서를 먼저 사용하고, 오직 필요한 경우 여러분의 추가 인증서

대체합니다. 첫째, 여러분이 여러분 키 저장소가 누락 인증서를 만들고 싶다면 다음 명령을 실행합니다.

 

  openssl s_client -connect www.yourserver.com:443


And you'll see output like the following:

그러면 다음과 같이 화면에 출력 됩니다

 Certificate chain
 0 s:/O=www.yourserver.com/OU=Go to
   https://www.thawte.com/repository/index.html/OU=Thawte SSL123
   certificate/OU=Domain Validated/CN=www.yourserver.com
   i:/C=US/O=Thawte, Inc./OU=Domain Validated SSL/CN=Thawte DV SSL CA
 1 s:/C=US/O=Thawte, Inc./OU=Domain Validated SSL/CN=Thawte DV SSL CA
   i:/C=US/O=thawte, Inc./OU=Certification Services Division/OU=(c)
   2006 thawte, Inc. - For authorized use only/CN=thawte Primary Root CA

 

As you can see, our root certificate is from Thawte. Go to your provider's website and find the corresponding certificate. For us, it was here, and you can see that the one we needed was the one Copyright 2006.

Thawte 로부터 발급된 루트 인증서를 볼 수 있습니다. 공급자 Web 사이트로 이동하여 해당 인증서를 찾습니다. 우리를 위해, 그것은 여기서 우리가 필요로 하는 하나다는 것을 알 수 있습니다.

 

If you're using a self-signed certificate, you didn't need to do the previous step since you already have your signing certificate. Then, create a keystore file containing the missing signing certificate. Crazybob has details how to do this on Android, but the idea is to do the following:

여러분이 자체 서명 인증서를 사용하는 경우 이미 여러분의 서명 인증서를 가지고 있기 때문에 이전 단계를 수행할 필요가 없습니다. 그런 다음 누락된 서명 인증서가 포함된 KeyStore 파일을 만듭니다.

 

 

export CLASSPATH=bcprov-jdk16-145.jar
CERTSTORE=res/raw/mystore.bks
if [ -a $CERTSTORE ]; then
    rm $CERTSTORE || exit 1
fi
keytool \
      -import \
      -v \
      -trustcacerts \
      -alias 0 \
      -file <(openssl x509 -in mycert.pem) \
      -keystore $CERTSTORE \
      -storetype BKS \
      -provider org.bouncycastle.jce.provider.BouncyCastleProvider \
      -providerpath /usr/share/java/bcprov.jar \
      -storepass some-password

 


 

You'll notice that the above script places the result in res/raw/mystore.bks.

Now you have a file that you'll load into your Android app that provides the missing certificate(s).

To do this, register your SSLSocketFactory for the SSL scheme:

여러분은 위의 스크립트에서 res/raw/mystore.bks 결과를 배치했다는 것을 알 수 있습니다.
지금 여러분은 (인증서가 누락된) Android 앱 안에서 로드할 파일을 가져와야 합니다.
이렇게 하려면 SSL 체계를 위해 당신 SSLSocketFactory등록해야 합니다. :

 

 

 final SchemeRegistry schemeRegistry = new SchemeRegistry();
 schemeRegistry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
 schemeRegistry.register(new Scheme("https", createAdditionalCertsSSLSocketFactory(), 443));

 // and then however you create your connection manager, I use ThreadSafeClientConnManager
 final HttpParams params = new BasicHttpParams();
   ...
 final ThreadSafeClientConnManager cm = new ThreadSafeClientConnManager(params,schemeRegistry);

 

 

To create your SSLSocketFactory:

여러분 SSLSocketFactory 만들려면

 

 protected org.apache.http.conn.ssl.SSLSocketFactory createAdditionalCertsSSLSocketFactory()

 {
    try

    {
        final KeyStore ks = KeyStore.getInstance("BKS");

        // the bks file we generated above
        final InputStream in = context.getResources().openRawResource( R.raw.mystore); 
        try {
            // don't forget to put the password used above in strings.xml/mystore_password
            ks.load(in, context.getString( R.string.mystore_password ).toCharArray());
        } finally {
            in.close();
        }

        return new AdditionalKeyStoresSSLSocketFactory(ks);

    } catch( Exception e ) {
        throw new RuntimeException(e);
    }
 }

 

 

And finally, the AdditionalKeyStoresSSLSocketFactory code, which accepts your new KeyStore

and checks if the built-in KeyStore fails to validate an SSL certificate:

그리고 마지막으로, AdditionalKeyStoresSSLSocketFactory 코드는 여러분 새로운 KeyStore

받아들이고 내장 KeyStore SSL 인증서가 유효한지를 확인합니다.

 

 

 

 /**
  * Allows you to trust certificates from additional KeyStores in addition to
  * the default KeyStore
  */

 public class AdditionalKeyStoresSSLSocketFactory extends SSLSocketFactory

 {
       protected SSLContext sslContext = SSLContext.getInstance("TLS");

       public AdditionalKeyStoresSSLSocketFactory(KeyStore keyStore) throws

                        NoSuchAlgorithmException, KeyManagementException, KeyStoreException,

                        UnrecoverableKeyException

       {
           super(null, null, null, null, null, null);
           sslContext.init(null, new TrustManager[]{new AdditionalKeyStoresTrustManager(keyStore)}

                                , null);
       }

 

       @Override
       public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws

               IOException

       {
           return sslContext.getSocketFactory().createSocket(socket, host, port, autoClose);
       }

 

       @Override
       public Socket createSocket() throws IOException {
           return sslContext.getSocketFactory().createSocket();
       }

 

       /**
         * Based on

             http://download.oracle.com/javase/1.5.0/docs/guide/security/jsse/JSSERefGuide.html#X509TrustManager
         */
       public static class AdditionalKeyStoresTrustManager implements X509TrustManager

       {

           protected ArrayList<X509TrustManager> x509TrustManagers = new

                                                                                   ArrayList<X509TrustManager>();


           protected AdditionalKeyStoresTrustManager(KeyStore... additionalkeyStores)

           {
               final ArrayList<TrustManagerFactory> factories = new ArrayList<TrustManagerFactory>();

               try

               {
                   // The default Trustmanager with default keystore
                   final TrustManagerFactory original = TrustManagerFactory.getInstance

                                                                        (TrustManagerFactory.getDefaultAlgorithm());
                   original.init((KeyStore) null);
                   factories.add(original);

                   for( KeyStore keyStore : additionalkeyStores ) {
                       final TrustManagerFactory additionalCerts = TrustManagerFactory.getInstance

                                                                           (TrustManagerFactory.getDefaultAlgorithm());
                       additionalCerts.init(keyStore);
                       factories.add(additionalCerts);
                   }

               }

               catch (Exception e)

               {
                   throw new RuntimeException(e);
               }

 

               /*
                * Iterate over the returned trustmanagers, and hold onto any that are X509TrustManagers
                */

               for (TrustManagerFactory tmf : factories)
                   for ( TrustManager tm : tmf.getTrustManagers() )
                       if (tm instanceof X509TrustManager)
                           x509TrustManagers.add( (X509TrustManager)tm );


               if ( x509TrustManagers.size()==0 )
                   throw new RuntimeException("Couldn't find any X509TrustManagers");

           }

       

            /*
             * Delegate to the default trust manager.
             */

           public void checkClientTrusted(X509Certificate[] chain, String authType) throws

                                      CertificateException

           {
               final X509TrustManager defaultX509TrustManager = x509TrustManagers.get(0);
               defaultX509TrustManager.checkClientTrusted(chain, authType);
           }

       

            /*
             * Loop over the trustmanagers until we find one that accepts our server
             */

           public void checkServerTrusted(X509Certificate[] chain, String authType) throws

                                      CertificateException

           {
               for ( X509TrustManager tm : x509TrustManagers ) {
                   try {
                       tm.checkServerTrusted(chain,authType);
                       return;
                   } catch( CertificateException e ) {
                       // ignore
                   }
               }
               throw new CertificateException();
           }

 

           public X509Certificate[] getAcceptedIssuers()

           {
               final ArrayList<X509Certificate> list = new ArrayList<X509Certificate>();
               for ( X509TrustManager tm : x509TrustManagers )
                   list.addAll(Arrays.asList(tm.getAcceptedIssuers()));
           

               return list.toArray(new X509Certificate[list.size()]);
           }
      }

 }