web-dev-qa-db-ja.com

OkHttp javax.net.ssl.SSLPeerUnverifiedException:ホスト名domain.comは検証されていません

私はこれを機能させるために何日も試みてきました。自己署名証明書を使用してhttps経由でサーバーに接続しようとしています。今まで読んだことのないページや例はないと思います。

私がやった事:

  1. このチュートリアルに従ってbksキーストアを作成しました: http://blog.crazybob.org/2010/02/Android-trusting-ssl-certificates.html

それは使用しています openssl s_client -connect domain.com:443サーバーから証明書を取得します。次に、弾力がある城を使用してbksキーストアを作成します。

  1. Rawフォルダーから作成されたキーストアを読み取り、sslfactoryに追加してから、OkHttpClientに追加します。このような:

    public ApiService() {
        mClient = new OkHttpClient();
        mClient.setConnectTimeout(TIMEOUT_SECONDS, TimeUnit.SECONDS);
        mClient.setReadTimeout(TIMEOUT_SECONDS, TimeUnit.SECONDS);
        mClient.setCache(getCache());
        mClient.setCertificatePinner(getPinnedCerts());
        mClient.setSslSocketFactory(getSSL());
    }
    
    protected SSLSocketFactory getSSL() {
        try {
            KeyStore trusted = KeyStore.getInstance("BKS");
            InputStream in = Beadict.getAppContext().getResources().openRawResource(R.raw.mytruststore);
            trusted.load(in, "pwd".toCharArray());
            SSLContext sslContext = SSLContext.getInstance("TLS");
            TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
            trustManagerFactory.init(trusted);
            sslContext.init(null, trustManagerFactory.getTrustManagers(), null);
            return sslContext.getSocketFactory();
        } catch(Exception e) {
            e.printStackTrace();
        }
        return null;
    }
    
    public CertificatePinner getPinnedCerts() {
        return new CertificatePinner.Builder()
                .add("domain.com", "sha1/theSha=")
                .build();
    }
    
  2. これは何らかの理由で、常にキーストアの有無にかかわらずSSLPeerUnverifiedExceptionを生成します。 CertificatePinnerの有無にかかわらず。

    javax.net.ssl.SSLPeerUnverifiedException: Hostname domain.com not verified: 0         
     W/System.err﹕ certificate: sha1/theSha=
     W/System.err﹕ DN: 1.2.840.113549.1.9.1=#1610696e666f40626561646963742e636f6d,CN=http://domain.com,OU=development,O=domain,L=Valencia,ST=Valencia,C=ES
     W/System.err﹕ subjectAltNames: []
     W/System.err﹕ at com.squareup.okhttp.internal.http.SocketConnector.connectTls(SocketConnector.Java:124)
     W/System.err﹕ at com.squareup.okhttp.Connection.connect(Connection.Java:143)
     W/System.err﹕ at com.squareup.okhttp.Connection.connectAndSetOwner(Connection.Java:185)
     W/System.err﹕ at com.squareup.okhttp.OkHttpClient$1.connectAndSetOwner(OkHttpClient.Java:128)
     W/System.err﹕ at com.squareup.okhttp.internal.http.HttpEngine.nextConnection(HttpEngine.Java:341)
     W/System.err﹕ at com.squareup.okhttp.internal.http.HttpEngine.connect(HttpEngine.Java:330)
     W/System.err﹕ at com.squareup.okhttp.internal.http.HttpEngine.sendRequest(HttpEngine.Java:248)
     W/System.err﹕ at com.squareup.okhttp.Call.getResponse(Call.Java:273)
     W/System.err﹕ at com.squareup.okhttp.Call$ApplicationInterceptorChain.proceed(Call.Java:230)
     W/System.err﹕ at com.squareup.okhttp.Call.getResponseWithInterceptorChain(Call.Java:201)
     W/System.err﹕ at com.squareup.okhttp.Call.execute(Call.Java:81)
     ...
    

何が間違っていますか?

17
just_user

私は最終的に、複数の回答を組み合わせてこれを機能させました。

まず、証明書の作成方法が間違っていました。しかし、 this answer のスクリプトを使用してそれらを作成することにより、それらを機能させました。必要なのは、サーバー証明書とキーです。その後、クライアントは別の証明書を必要としました。

Androidで証明書を使用するには、次のように.pemファイルを.crtファイルに変換しました。

_openssl x509 -outform der -in client.pem  -out client.crt
_

Android以下のようにOkHttpクライアントに証明書を追加しました。

_public ApiService() {
    mClient = new OkHttpClient();
    mClient.setConnectTimeout(TIMEOUT_SECONDS, TimeUnit.SECONDS);
    mClient.setReadTimeout(TIMEOUT_SECONDS, TimeUnit.SECONDS);
    mClient.setCache(getCache());
    mClient.setSslSocketFactory(getSSL());
}

protected SSLSocketFactory getSSL() {
    try {
        CertificateFactory cf = CertificateFactory.getInstance("X.509");
        InputStream cert = getAppContext().getResources().openRawResource(R.raw.client);
        Certificate ca = cf.generateCertificate(cert);
        cert.close();

        // creating a KeyStore containing our trusted CAs
        String keyStoreType = KeyStore.getDefaultType();
        KeyStore keyStore = KeyStore.getInstance(keyStoreType);
        keyStore.load(null, null);
        keyStore.setCertificateEntry("ca", ca);

        return new AdditionalKeyStore(keyStore);
    } catch(Exception e) {
        e.printStackTrace();
    }
    return null;
}
_

new AdditionalKeyStore()の最後の部分は この非常によく書かれた答え から取られています。フォールバックキーストアが追加されます。

これが他の誰にも役立つことを願っています!これは、見つけた自己署名証明書でHTTPSを機能させる最も簡単な方法です。他の方法としては、BouncyCastleキーストアを使用する方法がありますが、これは私には過度に思えます。

12
just_user

私は同じ問題を抱えていましたが、アプリケーションがいくつかのステージング環境で動作する必要があり、そのすべてに自己署名証明書がありました。さらに悪いことに、それらの証明書をその場で変更できます。

これを修正するために、ステージングのみに接続する場合、すべての証明書を信頼するSSLSocketFactoryを追加しました。これによりJavaエラーが修正されましたが、このチケットに記載されているokhttp例外が残りました。

このエラーを回避するには、okHttpClientにもう1つのカスタマイズを追加する必要がありました。これでエラーが修正されました。

okHttpClient.setHostnameVerifier(new HostnameVerifier() {
            @Override
            public boolean verify(String hostname, SSLSession session) {
                return true;
            }
        });
20
Jake Hall

この問題は、setHostNameVerifierokHttpBuilderに設定することで解決されます。 verifyメソッドがtrueを返すことを確認してください。

サンプル:

okHttpClient.setHostnameVerifier(new HostnameVerifier() {
    @Override
    public boolean verify(String hostname, SSLSession session) {
        return true;
    }
});

OkHttpClient.Builder builder = new OkHttpClient.Builder();
    builder.hostnameVerifier(new HostnameVerifier() {
        @Override
        public boolean verify(String hostname, SSLSession session) {
            return true;
        }
    });
OkHttpClient client = builder.build();
10
KayKay

クライアント証明書のCN名がサブジェクトの別名に追加されているかどうかを確認してください。私は同じ問題を抱えていた

3
Rahul Goel