문제

Android의 Java에서 자체 서명 된 인증서를 어떻게 수락합니까?

코드 샘플은 완벽합니다.

나는 인터넷의 모든 곳을 보았고 어떤 사람들은 해결책을 찾았다 고 주장하지만, 작동하지 않거나 샘플 코드가 없다고 주장합니다.

도움이 되었습니까?

해결책

Exchangeit 에이 기능이 있으며 Webdav를 통해 Microsoft Exchange에 연결됩니다. 다음은 SSL을 통해 자체 서명 인증서에 연결하는 httpclient를 만들기위한 코드입니다.

SchemeRegistry schemeRegistry = new SchemeRegistry();
// http scheme
schemeRegistry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
// https scheme
schemeRegistry.register(new Scheme("https", new EasySSLSocketFactory(), 443));

HttpParams params = new BasicHttpParams();
params.setParameter(ConnManagerPNames.MAX_TOTAL_CONNECTIONS, 30);
params.setParameter(ConnManagerPNames.MAX_CONNECTIONS_PER_ROUTE, new ConnPerRouteBean(30));
params.setParameter(HttpProtocolParams.USE_EXPECT_CONTINUE, false);
HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);

ClientConnectionManager cm = new ThreadSafeClientConnManager(params, schemeRegistry);

easysslsocketfactory입니다 여기, easyx509trustmanager입니다 여기.

Exchange의 코드는 오픈 소스이며 GoogleCode에서 호스팅됩니다. 여기, 문제가있는 경우. 더 이상 적극적으로 작업하지는 않지만 코드는 작동해야합니다.

Android 2.2 이후 프로세스가 약간 변경되었으므로 확인하십시오. 이것 위의 코드를 만들기 위해.

다른 팁

EJP가 올바르게 댓글을 달았 듯이 "독자들은이 기술이 근본적으로 안전하지 않다는 점에 주목해야합니다. SSL은 적어도 하나의 피어가 인증되지 않으면 안전하지 않습니다. RFC 2246 참조."

추가 수업이없는 또 다른 방법이 있습니다.

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

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.X509TrustManager;

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

어제 회사의 편안한 API를 HTTP로 마이그레이션하면서 자체 서명 된 SSL 인증서를 사용 하면서이 문제에 직면했습니다.

나는 어디에서나보고 있지만 내가 찾은 모든 "올바른"표시 답변은 인증서 검증을 비활성화하는 것으로 구성되어 SSL의 모든 감각을 명확하게 무시했습니다.

나는 마침내 해결책에왔다 :

  1. 로컬 키 스토어를 만듭니다

    앱이 자체 서명 된 인증서를 검증 할 수 있도록하려면 Android가 엔드 포인트를 신뢰할 수있는 방식으로 인증서를 사용자 정의 키 스토어에 제공해야합니다.

이러한 사용자 정의 Keystores의 형식은 "BKS"입니다. 바운시 캐슬, 따라서 다운로드 할 수있는 1.46 버전의 BouncycastleProvider가 필요합니다. 여기.

자체 서명 된 인증서도 필요합니다. self_cert.pem.

이제 KeyStore를 만드는 명령은 다음과 같습니다.

<!-- language: lang-sh -->

    $ keytool -import -v -trustcacerts -alias 0 \
    -file *PATH_TO_SELF_CERT.PEM* \
    -keystore *PATH_TO_KEYSTORE* \
    -storetype BKS \
    -provider org.bouncycastle.jce.provider.BouncyCastleProvider \
    -providerpath *PATH_TO_bcprov-jdk15on-146.jar* \
    -storepass *STOREPASS*

PATH_TO_KEYSTORE KeyStore가 생성되는 파일을 가리 킵니다. 그것 존재하지 않아야합니다.

PATH_TO_bcprov-jdk15on-146.jar.JAR 다운로드 된 .jar libary로가는 길입니다.

STOREPASS 새로 만들어진 Keystore 비밀번호입니다.

  1. 응용 프로그램에 Keystore를 포함시킵니다

새로 생성 된 키 스토어를 복사하십시오 PATH_TO_KEYSTORE 에게 res/raw/certs.bks (certs.bks 파일 이름 일뿐입니다. 원하는 이름을 사용할 수 있습니다)

키를 만듭니다 res/values/strings.xml ~와 함께

<!-- language: lang-xml -->

    <resources>
    ...
        <string name="store_pass">*STOREPASS*</string>
    ...
    </resources>
  1. 상속하는이 클래스를 만듭니다 DefaultHttpClient

    import android.content.Context;
    import android.util.Log;
    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.params.HttpParams;
    
    import java.io.IOException;
    import java.io.InputStream;
    import java.security.*;
    
    public class MyHttpClient extends DefaultHttpClient {
    
        private static Context appContext = null;
        private static HttpParams params = null;
        private static SchemeRegistry schmReg = null;
        private static Scheme httpsScheme = null;
        private static Scheme httpScheme = null;
        private static String TAG = "MyHttpClient";
    
        public MyHttpClient(Context myContext) {
    
            appContext = myContext;
    
            if (httpScheme == null || httpsScheme == null) {
                httpScheme = new Scheme("http", PlainSocketFactory.getSocketFactory(), 80);
                httpsScheme = new Scheme("https", mySSLSocketFactory(), 443);
            }
    
            getConnectionManager().getSchemeRegistry().register(httpScheme);
            getConnectionManager().getSchemeRegistry().register(httpsScheme);
    
        }
    
        private SSLSocketFactory mySSLSocketFactory() {
            SSLSocketFactory ret = null;
            try {
                final KeyStore ks = KeyStore.getInstance("BKS");
    
                final InputStream inputStream = appContext.getResources().openRawResource(R.raw.certs);
    
                ks.load(inputStream, appContext.getString(R.string.store_pass).toCharArray());
                inputStream.close();
    
                ret = new SSLSocketFactory(ks);
            } catch (UnrecoverableKeyException ex) {
                Log.d(TAG, ex.getMessage());
            } catch (KeyStoreException ex) {
                Log.d(TAG, ex.getMessage());
            } catch (KeyManagementException ex) {
                Log.d(TAG, ex.getMessage());
            } catch (NoSuchAlgorithmException ex) {
                Log.d(TAG, ex.getMessage());
            } catch (IOException ex) {
                Log.d(TAG, ex.getMessage());
            } catch (Exception ex) {
                Log.d(TAG, ex.getMessage());
            } finally {
                return ret;
            }
        }
    }
    

이제 단순히 인스턴스를 사용하십시오 **MyHttpClient** 당신과 마찬가지로 **DefaultHttpClient** HTTPS 쿼리를 만들려면 자체 서명 된 SSL 인증서를 올바르게 사용하고 검증합니다.

HttpResponse httpResponse;

HttpPost httpQuery = new HttpPost("https://yourserver.com");
... set up your query ...

MyHttpClient myClient = new MyHttpClient(myContext);

try{

    httpResponse = myClient.(peticionHttp);

    // Check for 200 OK code
    if (httpResponse.getStatusLine().getStatusCode() == HttpURLConnection.HTTP_OK) {
        ... do whatever you want with your response ...
    }

}catch (Exception ex){
    Log.d("httpError", ex.getMessage());
}

내가 무언가를 놓치지 않는 한,이 페이지의 다른 답변은 위험하며 SSL을 전혀 사용하지 않는 것과 동일합니다. 인증서가 예상되는 인증서인지 확인하기 위해 추가 점검을하지 않고 자체 서명 된 인증서를 신뢰하는 경우 누구나 자체 서명 된 인증서를 만들고 서버 인 척 할 수 있습니다. 그 시점에서, 당신은 실제 보안이 없습니다.

그만큼 이를 수행하는 합법적 인 방법 (전체 SSL 스택을 작성하지 않고)은 인증서 확인 프로세스 중에 신뢰할 수있는 추가 신뢰할 수있는 앵커를 추가하는 것입니다. 둘 다 신뢰할 수있는 앵커 인증서를 앱에 하드 코딩하고 OS가 제공하는 신뢰할 수있는 앵커에 추가하는 것과 관련이 있습니다 (그렇지 않으면 실제 인증서를 받으면 사이트에 연결할 수 없습니다).

이 작업을 수행하는 두 가지 방법을 알고 있습니다.

  1. 설명대로 사용자 정의 신뢰 상점을 만듭니다 http://www.ibm.com/developerworks/java/library/j-customssl/#8

  2. x509trustmanager의 사용자 정의 인스턴스를 작성하고 getAcceptedissuers 메소드를 재정의하여 인증서가 포함 된 배열을 반환합니다.

    public X509Certificate[] getAcceptedIssuers()
    {
        X509Certificate[] trustedAnchors =
            super.getAcceptedIssuers();
    
        /* Create a new array with room for an additional trusted certificate. */
        X509Certificate[] myTrustedAnchors = new X509Certificate[trustedAnchors.length + 1];
        System.arraycopy(trustedAnchors, 0, myTrustedAnchors, 0, trustedAnchors.length);  
    
        /* Load your certificate.
    
           Thanks to http://stackoverflow.com/questions/11857417/x509trustmanager-override-without-allowing-all-certs
           for this bit.
         */
        InputStream inStream = new FileInputStream("fileName-of-cert");
        CertificateFactory cf = CertificateFactory.getInstance("X.509");
        X509Certificate cert = (X509Certificate)cf.generateCertificate(inStream);
        inStream.close();
    
        /* Add your anchor cert as the last item in the array. */
        myTrustedAnchors[trustedAnchors.length] = cert;
    
        return myTrustedAnchors;
    }
    

이 코드는 완전히 테스트되지 않았으며 컴파일되지 않을 수도 있지만 적어도 올바른 방향으로 조종해야합니다.

Brian Yarger의 답변은 다음과 같이 더 큰 CreateSocket 메소드 오버로드를 수정하면 Android 2.2에서도 작동합니다. 자체 서명 된 SSL을 작동시키는 데 시간이 걸렸습니다.

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

안드로이드에서 HttpProtocolParams 수락합니다 ProtocolVersion 보다는 HttpVersion.

ProtocolVersion pv = new ProtocolVersion("HTTP", 1, 1);
HttpProtocolParams.setVersion(params, pv);

@chris- 댓글을 추가 할 수 없기 때문에 이것을 답으로 게시합니다 (아직). 웹 뷰를 사용할 때 귀하의 접근 방식이 작동하는지 궁금합니다. Android 2.3에서는 그렇게 할 수 없습니다. 대신 흰색 화면을 얻습니다.

좀 더 검색 한 후, 나는 이것을 발견했다 웹 뷰에서 SSL 오류를 처리하기위한 간단한 수정 그것은 나를위한 매력처럼 작동했습니다.

핸들러에서 특별한 개발 모드에 있는지 확인하고 handler.proceed ()를 호출합니다. 그렇지 않으면 handler.cancel ()을 호출합니다. 이를 통해 지역 웹 사이트에서 자체 서명 된 인증서에 대한 개발을 수행 할 수 있습니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top