Programing

HTTPS를 통한 HttpClient를 사용하여 모든 인증서 신뢰

lottogame 2020. 2. 25. 22:11
반응형

HTTPS를 통한 HttpClient를 사용하여 모든 인증서 신뢰


최근 HttpClientover Https ( here here ) 에 관한 질문을 게시했습니다 . 나는 약간의 진전을 이루었지만 새로운 문제에 부딪쳤다. 마지막 문제와 마찬가지로, 나에게 적합한 예제를 찾을 수없는 것 같습니다. 기본적으로 클라이언트가 모든 인증서를 수락하기를 원하지만 (단 하나의 서버 만 가리 키기 때문에) 계속javax.net.ssl.SSLException: Not trusted server certificate exception.

이것이 내가 가진 것입니다 :


    public void connect() throws A_WHOLE_BUNCH_OF_EXCEPTIONS {

        HttpPost post = new HttpPost(new URI(PROD_URL));
        post.setEntity(new StringEntity(BODY));

        KeyStore trusted = KeyStore.getInstance("BKS");
        trusted.load(null, "".toCharArray());
        SSLSocketFactory sslf = new SSLSocketFactory(trusted);
        sslf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);

        SchemeRegistry schemeRegistry = new SchemeRegistry();
        schemeRegistry.register(new Scheme ("https", sslf, 443));
        SingleClientConnManager cm = new SingleClientConnManager(post.getParams(),
                schemeRegistry);

        HttpClient client = new DefaultHttpClient(cm, post.getParams());
        HttpResponse result = client.execute(post);
    }

그리고 내가받는 오류는 다음과 같습니다.

    W/System.err(  901): javax.net.ssl.SSLException: Not trusted server certificate 
    W/System.err(  901):    at org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:360) 
    W/System.err(  901):    at org.apache.http.conn.ssl.AbstractVerifier.verify(AbstractVerifier.java:92) 
    W/System.err(  901):    at org.apache.http.conn.ssl.SSLSocketFactory.connectSocket(SSLSocketFactory.java:321) 
    W/System.err(  901):    at org.apache.http.impl.conn.DefaultClientConnectionOperator.openConnection(DefaultClientConnectionOperator.java:129) 
    W/System.err(  901):    at org.apache.http.impl.conn.AbstractPoolEntry.open(AbstractPoolEntry.java:164) 
    W/System.err(  901):    at org.apache.http.impl.conn.AbstractPooledConnAdapter.open(AbstractPooledConnAdapter.java:119) 
    W/System.err(  901):    at org.apache.http.impl.client.DefaultRequestDirector.execute(DefaultRequestDirector.java:348) 
    W/System.err(  901):    at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:555) 
    W/System.err(  901):    at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:487) 
    W/System.err(  901):    at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:465) 
    W/System.err(  901):    at me.harrisonlee.test.ssl.MainActivity.connect(MainActivity.java:129) 
    W/System.err(  901):    at me.harrisonlee.test.ssl.MainActivity.access$0(MainActivity.java:77) 
    W/System.err(  901):    at me.harrisonlee.test.ssl.MainActivity$2.run(MainActivity.java:49) 
    W/System.err(  901): Caused by: java.security.cert.CertificateException: java.security.InvalidAlgorithmParameterException: the trust anchors set is empty 
    W/System.err(  901):    at org.apache.harmony.xnet.provider.jsse.TrustManagerImpl.checkServerTrusted(TrustManagerImpl.java:157) 
    W/System.err(  901):    at org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:355) 
    W/System.err(  901):    ... 12 more 
    W/System.err(  901): Caused by: java.security.InvalidAlgorithmParameterException: the trust anchors set is empty 
    W/System.err(  901):    at java.security.cert.PKIXParameters.checkTrustAnchors(PKIXParameters.java:645) 
    W/System.err(  901):    at java.security.cert.PKIXParameters.<init>(PKIXParameters.java:89) 
    W/System.err(  901):    at org.apache.harmony.xnet.provider.jsse.TrustManagerImpl.<init>(TrustManagerImpl.java:89) 
    W/System.err(  901):    at org.apache.harmony.xnet.provider.jsse.TrustManagerFactoryImpl.engineGetTrustManagers(TrustManagerFactoryImpl.java:134) 
    W/System.err(  901):    at javax.net.ssl.TrustManagerFactory.getTrustManagers(TrustManagerFactory.java:226)W/System.err(  901):     at org.apache.http.conn.ssl.SSLSocketFactory.createTrustManagers(SSLSocketFactory.java:263) 
    W/System.err(  901):    at org.apache.http.conn.ssl.SSLSocketFactory.<init>(SSLSocketFactory.java:190) 
    W/System.err(  901):    at org.apache.http.conn.ssl.SSLSocketFactory.<init>(SSLSocketFactory.java:216) 
    W/System.err(  901):    at me.harrisonlee.test.ssl.MainActivity.connect(MainActivity.java:107) 
    W/System.err(  901):    ... 2 more

참고 : 완전히 신뢰할 수없는 네트워크에서 사용할 프로덕션 코드에는 이것을 구현하지 마십시오. 특히 공공 인터넷을 통한 모든 것.

당신의 질문은 내가 알고 싶은 것입니다. 몇 가지 검색을 한 후 결론은 다음과 같습니다.

HttpClient 방식에서는 org.apache.http.conn.ssl.SSLSocketFactory 자체가 아닌 org.apache.http.conn.ssl.SSLSocketFactory에서 사용자 정의 클래스를 작성해야합니다. 이 게시물에서 몇 가지 단서를 찾을 수 있습니다. 사용자 정의 SSL 처리는 Android 2.2 FroYo에서 작동이 중지되었습니다 .

예를 들면 ...

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

HttpClient의 인스턴스를 만드는 동안이 클래스를 사용하십시오.

public HttpClient getNewHttpClient() {
    try {
        KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
        trustStore.load(null, null);

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

BTW, 아래 링크는 HttpURLConnection 솔루션을 찾는 사람을위한 것입니다. Https 연결 안드로이드

나는 위의 두 가지 솔루션을 froyo에서 테스트했으며 모두 내 경우에는 매력처럼 작동합니다. 마지막으로 HttpURLConnection을 사용하면 리디렉션 문제가 발생할 수 있지만 이는 주제를 벗어납니다.

참고 : 모든 인증서를 신뢰하기로 결정하기 전에 해당 사이트를 완전히 알고 있어야하며 최종 사용자에게 해를 끼치 지 않을 것입니다.

실제로, 다음의 의견에서 언급 한 해커의 모의 사이트의 영향을 포함하여 귀하가 취하는 위험을 신중하게 고려해야합니다. 어떤 상황에서는 모든 인증서를 관리하기가 어려울 수 있지만 모든 인증서를 신뢰하는 암시적인 단점을 더 잘 알고있을 것입니다.


기본적으로 httpclient를 사용하여 Android에서 "신뢰할 수 없음"예외를 해결하는 네 가지 가능한 솔루션이 있습니다.

  1. 모든 인증서를 신뢰하십시오. 당신이하고있는 일을 정말로 알지 못한다면 이것을하지 마십시오.
  2. 인증서 만 신뢰하는 사용자 정의 SSLSocketFactory를 작성하십시오. 연결하려는 서버를 정확히 알고있는 한 작동하지만 다른 SSL 인증서를 사용하여 새 서버에 연결해야하는 즉시 앱을 업데이트해야합니다.
  3. Android의 "마스터 목록"인증서가 포함 된 키 저장소 파일을 작성한 후 직접 저장하십시오. 해당 인증서 중 어느 것이라도 만료되면 앱에서 업데이트해야합니다. 나는 이것을 할 이유를 생각할 수 없다.
  4. 내장 인증서 KeyStore를 사용하는 사용자 정의 SSLSocketFactory를 작성하십시오. 그러나 기본값으로 확인하지 못하는 경우 대체 KeyStore로 대체됩니다.

이 답변은 솔루션 # 4를 사용합니다.

해결책은 여러 키 저장소를 승인 할 수있는 SSLSocketFactory를 사용하여 고유 한 인증서를 고유 한 키 저장소에 제공 할 수 있습니다. 이를 통해 일부 Android 기기에서 누락 될 수있는 Thawte와 같은 추가 최상위 인증서를로드 할 수 있습니다. 또한 자체 서명 된 인증서도로드 할 수 있습니다. 내장 된 기본 장치 인증서를 먼저 사용하고 필요한 경우에만 추가 인증서로 대체합니다.

먼저, KeyStore에서 누락 된 인증서를 결정해야합니다. 다음 명령을 실행하십시오.

openssl s_client -connect www.yourserver.com:443

그리고 당신은 다음과 같은 출력을 볼 수 있습니다 :

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

보시다시피 루트 인증서는 Thawte의 것입니다. 공급자의 웹 사이트로 이동하여 해당 인증서를 찾으십시오. 우리에게는 여기 에 있으며, 필요한 것은 Copyright 2006이라는 것을 알 수 있습니다.

자체 서명 인증서를 사용하는 경우 서명 인증서가 이미 있으므로 이전 단계를 수행하지 않아도됩니다.

그런 다음 누락 된 서명 인증서가 포함 된 키 저장소 파일을 작성하십시오. Crazybob에는 Android에서이 작업을 수행하는 방법자세히 설명되어 있지만 아이디어는 다음을 수행하는 것입니다.

Bouncy Castle 제공자 라이브러리가없는 경우 http://www.bouncycastle.org/latest_releases.html 에서 다운로드 하십시오 . 이것은 아래 클래스 경로로 진행됩니다.

서버에서 인증서를 추출하고 pem 파일을 작성하는 명령을 실행하십시오. 이 경우 mycert.pem입니다.

echo | openssl s_client -connect ${MY_SERVER}:443 2>&1 | \
 sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' > mycert.pem

그런 다음 다음 명령을 실행하여 키 저장소를 작성하십시오.

export CLASSPATH=/path/to/bouncycastle/bcprov-jdk15on-155.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 /path/to/bouncycastle/bcprov-jdk15on-155.jar \
      -storepass some-password

위 스크립트는 결과를에 배치한다는 것을 알 수 있습니다 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);

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

마지막으로 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 on
             * to 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()]);
        }
    }

}

이 코드를 앞에 추가하면 HttpsURLConnection완료됩니다. 알았어

private void trustEveryone() { 
    try { 
            HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier(){ 
                    public boolean verify(String hostname, SSLSession session) { 
                            return true; 
                    }}); 
            SSLContext context = SSLContext.getInstance("TLS"); 
            context.init(null, new X509TrustManager[]{new X509TrustManager(){ 
                    public void checkClientTrusted(X509Certificate[] chain, 
                                    String authType) throws CertificateException {} 
                    public void checkServerTrusted(X509Certificate[] chain, 
                                    String authType) throws CertificateException {} 
                    public X509Certificate[] getAcceptedIssuers() { 
                            return new X509Certificate[0]; 
                    }}}, new SecureRandom()); 
            HttpsURLConnection.setDefaultSSLSocketFactory( 
                            context.getSocketFactory()); 
    } catch (Exception e) { // should never happen 
            e.printStackTrace(); 
    } 
} 

이것이 도움이되기를 바랍니다.


이것은 나쁜 생각입니다. 모든 인증서를 신뢰하는 것은 SSL을 전혀 사용하지 않는 것보다 약간 좋습니다. "내 서버가 하나의 서버 만 가리 키기 때문에 클라이언트가 모든 인증서를 수락하기를 원합니다"라고 말하면 이것은 "하나의 서버"를 가리키는 것이 안전하며 공용 네트워크에 있지 않다는 것을 의미합니다.

인증서를 신뢰하면 중간자 공격에 완전히 개방됩니다. 사용자와 최종 서버와 별도의 SSL 연결을 설정하여 누구나 연결을 프록시 할 수 있습니다. 그런 다음 MITM은 전체 요청 및 응답에 액세스 할 수 있습니다. 처음에 실제로 SSL이 필요하지 않은 경우 (메시지에 민감한 것은 없으며 인증을 수행하지 않음) 모든 인증서를 맹목적으로 신뢰해서는 안됩니다.

keytool을 사용하여 공개 인증서를 jks에 추가하고 소켓 팩토리를 빌드하는 데 다음과 같이 사용하십시오.

    KeyStore ks = KeyStore.getInstance("JKS");

    // get user password and file input stream
    char[] password = ("mykspassword")).toCharArray();
    ClassLoader cl = this.getClass().getClassLoader();
    InputStream stream = cl.getResourceAsStream("myjks.jks");
    ks.load(stream, password);
    stream.close();

    SSLContext sc = SSLContext.getInstance("TLS");
    KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
    TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");

    kmf.init(ks, password);
    tmf.init(ks);

    sc.init(kmf.getKeyManagers(), tmf.getTrustManagers(),null);

    return sc.getSocketFactory();

여기에는주의해야 할 사항이 하나 있습니다. 인증서는 결국 만료되고 코드는 그때 작동을 멈 춥니 다. 인증서를 보면 언제 이런 일이 일어날 지 쉽게 결정할 수 있습니다.


API 8부터이 방법으로 테스트 목적으로 HttpURLConnection SSL 검사를 비활성화 할 수 있습니다.

    HttpURLConnection conn = (HttpURLConnection) url.openConnection();
    if (conn instanceof HttpsURLConnection) {
        HttpsURLConnection httpsConn = (HttpsURLConnection) conn;
        httpsConn.setSSLSocketFactory(SSLCertificateSocketFactory.getInsecure(0, null));
        httpsConn.setHostnameVerifier(new AllowAllHostnameVerifier());
    }

HttpComponents의 API가 변경되었습니다. 아래 코드와 함께 작동합니다.

public static HttpClient getTestHttpClient() {
    try {
        SSLSocketFactory sf = new SSLSocketFactory(new TrustStrategy(){
            @Override
            public boolean isTrusted(X509Certificate[] chain,
                    String authType) throws CertificateException {
                return true;
            }
        }, new AllowAllHostnameVerifier());

        SchemeRegistry registry = new SchemeRegistry();
        registry.register(new Scheme("https",8444, sf));
        ClientConnectionManager ccm = new ThreadSafeClientConnManager(registry);
        return new DefaultHttpClient(ccm);
    } catch (Exception e) {
        e.printStackTrace();
        return new DefaultHttpClient();
    }
}

https://stackoverflow.com/a/6378872/1553004의 위 코드 는 호스트 이름 검증자를 호출해야한다는 점을 제외하고는 정확합니다.

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

이 수정 프로그램을 추가하기 위해 명시 적으로 stackoverflow에 가입했습니다. 내 경고에 유의하십시오!


httpclient-4.5를 사용하는 사람들에 대한 응답을 추가하고 있으며 4.4에서도 작동합니다.

import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;

import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.HttpResponseException;
import org.apache.http.client.fluent.ContentResponseHandler;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.TrustStrategy;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.ssl.SSLContextBuilder;



public class HttpClientUtils{

public static HttpClient getHttpClientWithoutSslValidation_UsingHttpClient_4_5_2() {
    try {
        SSLContextBuilder builder = new SSLContextBuilder();
        builder.loadTrustMaterial(null, new TrustStrategy() {
            @Override
            public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException {
                return true;
            }
        });
        SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(builder.build(), new NoopHostnameVerifier());
        CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(sslsf).build(); 
        return httpclient;
    } catch (Exception e) {
        throw new RuntimeException(e);
    }
}
}

모든 인증서를 신뢰하는 것은 나에게 실질적인 대안이 아니므로 HttpsURLConnection이 새 인증서를 신뢰하도록하기 위해 다음을 수행했습니다 ( http://nelenkov.blogspot.jp/2011/12/using-custom-certificate-trust-store- 참조). on.html ).

  1. 인증서를 받으십시오. Firefox에서 인증서를 내 보내면 (작은 자물쇠 아이콘 클릭, 인증서 세부 정보 가져 오기, 내보내기 클릭)이 작업을 완료 한 다음 portecle 을 사용하여 신뢰 저장소 (BKS)를 내보냈 습니다.

  2. 다음 코드를 사용하여 /res/raw/geotrust_cert.bks에서 Truststore를로드하십시오.

        final KeyStore trustStore = KeyStore.getInstance("BKS");
        final InputStream in = context.getResources().openRawResource(
                R.raw.geotrust_cert);
        trustStore.load(in, null);
    
        final TrustManagerFactory tmf = TrustManagerFactory
                .getInstance(TrustManagerFactory.getDefaultAlgorithm());
        tmf.init(trustStore);
    
        final SSLContext sslCtx = SSLContext.getInstance("TLS");
        sslCtx.init(null, tmf.getTrustManagers(),
                new java.security.SecureRandom());
    
        HttpsURLConnection.setDefaultSSLSocketFactory(sslCtx
                .getSocketFactory());
    

다음은 4.1.2 httpclient 코드를 사용하는 훨씬 간단한 버전입니다. 그런 다음 적합한 트러스트 알고리즘으로 수정할 수 있습니다.

public static HttpClient getTestHttpClient() {
    try {
        SSLSocketFactory sf = new SSLSocketFactory(new TrustStrategy(){
            @Override
            public boolean isTrusted(X509Certificate[] chain,
                    String authType) throws CertificateException {
                return true;
            }
        });
        SchemeRegistry registry = new SchemeRegistry();
        registry.register(new Scheme("https", 443, sf));
        ClientConnectionManager ccm = new ThreadSafeClientConnManager(registry);
        return new DefaultHttpClient(ccm);
    } catch (Exception e) {
        return new DefaultHttpClient();
    }
}

"emmby"(6 월 16 일 11시 21:29에 답변 됨), 항목 # 4 : "내장 인증서 KeyStore를 사용하는 사용자 정의 SSLSocketFactory를 작성하지만 실패한 항목에 대해서는 대체 KeyStore로 대체됩니다. "기본값으로 확인하십시오."

이것은 단순화 된 구현입니다. 시스템 키 저장소를로드하고 응용 프로그램 키 저장소와 병합하십시오.

public HttpClient getNewHttpClient() {
    try {
        InputStream in = null;
        // Load default system keystore
        KeyStore trusted = KeyStore.getInstance(KeyStore.getDefaultType()); 
        try {
            in = new BufferedInputStream(new FileInputStream(System.getProperty("javax.net.ssl.trustStore"))); // Normally: "/system/etc/security/cacerts.bks"
            trusted.load(in, null); // no password is "changeit"
        } finally {
            if (in != null) {
                in.close();
                in = null;
            }
        }

        // Load application keystore & merge with system
        try {
            KeyStore appTrusted = KeyStore.getInstance("BKS"); 
            in = context.getResources().openRawResource(R.raw.mykeystore);
            appTrusted.load(in, null); // no password is "changeit"
            for (Enumeration<String> e = appTrusted.aliases(); e.hasMoreElements();) {
                final String alias = e.nextElement();
                final KeyStore.Entry entry = appTrusted.getEntry(alias, null);
                trusted.setEntry(System.currentTimeMillis() + ":" + alias, entry, null);
            }
        } finally {
            if (in != null) {
                in.close();
                in = null;
            }
        }

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

        SSLSocketFactory sf = new SSLSocketFactory(trusted);
        sf.setHostnameVerifier(SSLSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);

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

JKS에서 BKS로 변환하는 간단한 모드 :

keytool -importkeystore -destkeystore cacerts.bks -deststoretype BKS -providerclass org.bouncycastle.jce.provider.BouncyCastleProvider -providerpath bcprov-jdk16-141.jar -deststorepass changeit -srcstorepass changeit -srckeystore $JAVA_HOME/jre/lib/security/cacerts -srcstoretype JKS -noprompt

* 참고 : Android 4.0 (ICS)에서 신뢰 저장소가 변경되었습니다. 추가 정보 : http://nelenkov.blogspot.com.es/2011/12/ics-trust-store-implementation.html


OAuth를 통해 모든 인증서가 (테스트 목적으로) 작동하도록하려면 다음 단계를 수행하십시오.

1) https://github.com/kaeppler/signpost 에서 Android OAuth API의 소스 코드를 다운로드하십시오.

2) "CommonsHttpOAuthProvider"클래스를 찾으십시오

3) 아래와 같이 변경하십시오.

public class CommonsHttpOAuthProvider extends AbstractOAuthProvider {

private static final long serialVersionUID = 1L;

private transient HttpClient httpClient;

public CommonsHttpOAuthProvider(String requestTokenEndpointUrl, String accessTokenEndpointUrl,
        String authorizationWebsiteUrl) {
    super(requestTokenEndpointUrl, accessTokenEndpointUrl, authorizationWebsiteUrl);


    //this.httpClient = new DefaultHttpClient();//Version implemented and that throws the famous "javax.net.ssl.SSLException: Not trusted server certificate" if the certificate is not signed with a CA
    this.httpClient = MySSLSocketFactory.getNewHttpClient();//This will work with all certificates (for testing purposes only)
}

위의 "MySSLSocketFactory"는 허용 된 답변을 기반으로합니다. 더 쉽게하기 위해 여기에 전체 수업이 있습니다.

package com.netcomps.oauth_example;

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.HttpVersion;
import org.apache.http.client.HttpClient;
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.conn.ssl.SSLSocketFactory;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpParams;
import org.apache.http.params.HttpProtocolParams;
import org.apache.http.protocol.HTTP;

//http://stackoverflow.com/questions/2642777/trusting-all-certificates-using-httpclient-over-https
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() {

        @Override
        public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
        }

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

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



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

}

이것이 누군가를 돕기를 바랍니다.


나는 이것을 사용했고 그것은 모든 OS에서 나를 위해 일한다.

/**
 * Disables the SSL certificate checking for new instances of {@link HttpsURLConnection} This has been created to
 * aid testing on a local box, not for use on production.
 */


private static void disableSSLCertificateChecking() {
    TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() {
        public X509Certificate[] getAcceptedIssuers() {
            return null;
        }

        @Override
        public void checkClientTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {
            // Not implemented
        }

        @Override
        public void checkServerTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {
            // Not implemented
        }
    } };

    try {
        SSLContext sc = SSLContext.getInstance("TLS");

        sc.init(null, trustAllCerts, new java.security.SecureRandom());

        HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
    } catch (KeyManagementException e) {
        e.printStackTrace();
    } catch (NoSuchAlgorithmException e) {
        e.printStackTrace();
    }
}

-Dtrust_all_cert=trueVM 인수에 추가하면 됩니다. 이 인수는 java에게 인증서 검사를 무시하도록 지시합니다.


모든 몸은 여전히 안드로이드 2.1 방문 StartCom SSL 인증서로 어려움을 겪고 https://www.startssl.com/certs/ 과 지금의 ca.pem를 다운로드 대답 에 의해 제공 @emmby 교체

`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`

 `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 ca.pem) \
  -keystore $CERTSTORE \
  -storetype BKS \
  -provider org.bouncycastle.jce.provider.BouncyCastleProvider \
  -providerpath /usr/share/java/bcprov.jar \
  -storepass some-password`

상자 밖으로 작동해야합니다. @emmby 의 완벽한 답변 후에도 하루 이상 어려움을 겪고 있었습니다 .


이 수업을 사용하십시오

public class WCFs
{
    //  https://192.168.30.8/myservice.svc?wsdl
private static final String NAMESPACE = "http://tempuri.org/";
private static final String URL = "192.168.30.8";
private static final String SERVICE = "/myservice.svc?wsdl";
private static String SOAP_ACTION = "http://tempuri.org/iWCFserviceMe/";


public static Thread myMethod(Runnable rp)
{
    String METHOD_NAME = "myMethod";

    SoapObject request = new SoapObject(NAMESPACE, METHOD_NAME);

    request.addProperty("Message", "Https WCF Running...");
    return _call(rp,METHOD_NAME, request);
}

protected static HandlerThread _call(final RunProcess rp,final String METHOD_NAME, SoapObject soapReq)
{
    final SoapSerializationEnvelope envelope = new SoapSerializationEnvelope(SoapEnvelope.VER11);
    int TimeOut = 5*1000;

    envelope.dotNet = true;
    envelope.bodyOut = soapReq;
    envelope.setOutputSoapObject(soapReq);

    final HttpsTransportSE httpTransport_net = new HttpsTransportSE(URL, 443, SERVICE, TimeOut);

    try
    {
        HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() // use this section if crt file is handmake
        {
            @Override
            public boolean verify(String hostname, SSLSession session)
            {
                return true;
            }
        });

        KeyStore k = getFromRaw(R.raw.key, "PKCS12", "password");
        ((HttpsServiceConnectionSE) httpTransport_net.getServiceConnection()).setSSLSocketFactory(getSSLSocketFactory(k, "SSL"));


    }
    catch(Exception e){}

    HandlerThread thread = new HandlerThread("wcfTd"+ Generator.getRandomNumber())
    {
        @Override
        public void run()
        {
            Handler h = new Handler(Looper.getMainLooper());
            Object response = null;

            for(int i=0; i<4; i++)
            {
                response = send(envelope, httpTransport_net , METHOD_NAME, null);

                try
                {if(Thread.currentThread().isInterrupted()) return;}catch(Exception e){}

                if(response != null)
                    break;

                ThreadHelper.threadSleep(250);
            }

            if(response != null)
            {
                if(rp != null)
                {
                    rp.setArguments(response.toString());
                    h.post(rp);
                }
            }
            else
            {
                if(Thread.currentThread().isInterrupted())
                    return;

                if(rp != null)
                {
                    rp.setExceptionState(true);
                    h.post(rp);
                }
            }

            ThreadHelper.stopThread(this);
        }
    };

    thread.start();

    return thread;
}


private static Object send(SoapSerializationEnvelope envelope, HttpTransportSE androidHttpTransport, String METHOD_NAME, List<HeaderProperty> headerList)
{
    try
    {
        if(headerList != null)
            androidHttpTransport.call(SOAP_ACTION + METHOD_NAME, envelope, headerList);
        else
            androidHttpTransport.call(SOAP_ACTION + METHOD_NAME, envelope);

        Object res = envelope.getResponse();

        if(res instanceof SoapPrimitive)
            return (SoapPrimitive) envelope.getResponse();
        else if(res instanceof SoapObject)
            return ((SoapObject) envelope.getResponse());
    }
    catch(Exception e)
    {}

    return null;
}

public static KeyStore getFromRaw(@RawRes int id, String algorithm, String filePassword)
{
    try
    {
        InputStream inputStream = ResourceMaster.openRaw(id);
        KeyStore keystore = KeyStore.getInstance(algorithm);
        keystore.load(inputStream, filePassword.toCharArray());
        inputStream.close();

        return keystore;
    }
    catch(Exception e)
    {}

    return null;
}

public static SSLSocketFactory getSSLSocketFactory(KeyStore trustKey, String SSLAlgorithm)
{
    try
    {
        TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
        tmf.init(trustKey);

        SSLContext context = SSLContext.getInstance(SSLAlgorithm);//"SSL" "TLS"
        context.init(null, tmf.getTrustManagers(), null);

        return context.getSocketFactory();
    }
    catch(Exception e){}

    return null;
}

}


여기에 이미지 설명을 입력하십시오

xamarin android에서 sspi가 실패했습니다.

이 솔루션을 찾았습니다. HTTPS 링크를 누르기 전에이 코드를 넣으십시오

const SslProtocols _Tls12 = (SslProtocols)0x00000C00;
const SecurityProtocolType Tls12 = (SecurityProtocolType)_Tls12;
ServicePointManager.SecurityProtocol = Tls12;

모든 https와 함께 작동

httpClient = new DefaultHttpClient();

SSLContext ctx = SSLContext.getInstance("TLS");
X509TrustManager tm = new X509TrustManager() {
    public void checkClientTrusted(X509Certificate[] xcs, String string) throws CertificateException { }

    public void checkServerTrusted(X509Certificate[] xcs, String string) throws CertificateException { }

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

ctx.init(null, new TrustManager[]{tm}, null);
SSLSocketFactory ssf = new SSLSocketFactory(ctx, SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);

httpClient.getConnectionManager().getSchemeRegistry().register(new Scheme("https", 443, ssf));

위의 많은 답변이 있지만 제한된 시간 동안 올바르게 작동하지 못했기 때문에 같은 상황에있는 다른 사람을 위해 내 Java 테스트 목적으로 완벽하게 작동하는 아래 코드를 시도 할 수 있습니다.

    public static HttpClient wrapClient(HttpClient base) {
    try {
        SSLContext ctx = SSLContext.getInstance("TLS");
        X509TrustManager tm = new X509TrustManager() {
            public void checkClientTrusted(X509Certificate[] xcs, String string) throws CertificateException { }

            public void checkServerTrusted(X509Certificate[] xcs, String string) throws CertificateException { }

            public X509Certificate[] getAcceptedIssuers() {
                return null;
            }
        };
        ctx.init(null, new TrustManager[]{tm}, null);
        SSLSocketFactory ssf = new SSLSocketFactory(ctx);
        ssf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
        ClientConnectionManager ccm = base.getConnectionManager();
        SchemeRegistry sr = ccm.getSchemeRegistry();
        sr.register(new Scheme("https", ssf, 443));
        return new DefaultHttpClient(ccm, base.getParams());
    } catch (Exception ex) {
        return null;
    }
}

다음과 같이 전화하십시오.

DefaultHttpClient baseClient = new DefaultHttpClient();
HttpClient httpClient = wrapClient(baseClient );

참조 : http://tech.chitgoks.com/2011/04/24/how-to-avoid-javax-net-ssl-sslpeerunverifiedexception-peer-not-authenticated-problem-using-apache-httpclient/


간단히 이것을 사용하십시오-

public DefaultHttpClient wrapClient(HttpClient base) {
    try {
        SSLContext ctx = SSLContext.getInstance("TLS");
        X509TrustManager tm = new X509TrustManager() {
        public void checkClientTrusted(X509Certificate[] xcs, String string) throws CertificateException { }

        public void checkServerTrusted(X509Certificate[] xcs, String string) throws CertificateException { }

        public X509Certificate[] getAcceptedIssuers() {
            return null;
        }
    };
    ctx.init(null, new TrustManager[]{tm}, null);
    SSLSocketFactory ssf = new SSLSocketFactory(ctx);
    ssf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
    ClientConnectionManager ccm = base.getConnectionManager();
    SchemeRegistry sr = ccm.getSchemeRegistry();
    sr.register(new Scheme("https", ssf, 443));
    return new DefaultHttpClient(ccm, base.getParams());
} catch (Exception ex) {
    return null;
}
}

Daniel의 대답 은이 코드를 변경해야한다는 것을 제외하고는 좋았습니다 ...

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

이 코드에 ...

    ClientConnectionManager ccm = new ThreadSafeClientConnManager(params, registry);
    SchemeRegistry registry = ccm.getShemeRegistry()
    registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
    registry.register(new Scheme("https", sf, 443));

작동하도록

참고 URL : https://stackoverflow.com/questions/2642777/trusting-all-certificates-using-httpclient-over-https



반응형