7

私はしばらくの間機能していた次の証明書固定コードを使用しています (エラー処理は簡潔にするために編集されています)。

private static SSLContext _ssl_context = null;

public static SSLSocketFactory get_ssl_socket_factory(Context context)
{
    if (_ssl_context != null) {
        return _ssl_context.getSocketFactory();
    }

    KeyStore keystore = get_keystore(context);
    try
    {
        TrustManagerFactory tmf = TrustManagerFactory.getInstance("X509");
        tmf.init(keystore);
        _ssl_context = SSLContext.getInstance("TLS");
        _ssl_context.init(null, tmf.getTrustManagers(), null);
        return _ssl_context.getSocketFactory();
    }
    catch (GeneralSecurityException e) {
        // ...
    }
}

これは多かれ少なかれ公式ドキュメントで提供されているコードです。次に、SocketFactory は次のように使用されます。

if ("https".equals(target.getProtocol()) &&
    "example.com".equals(target.getHost()) &&
    huc instanceof HttpsURLConnection)
{
    ((HttpsURLConnection) huc).setSSLSocketFactory(
            SSLHelper.get_ssl_socket_factory(this));
}

このコードを Android 8 デバイスで実行すると、期待どおりに動作します。ただし、私の Android 9 エミュレーターでは、例外がスローされます。

E/App: https://example.com/page.html could not be retrieved! (Hostname example.com not verified:
            certificate: sha1/VYMjxowFaRuZpycEoz+srAuXzlU=
            subjectAltNames: [])
        javax.net.ssl.SSLPeerUnverifiedException: Hostname example.com not verified:
            certificate: sha1/VYMjxowFaRuZpycEoz+srAuXzlU=
            subjectAltNames: []
            at com.android.okhttp.internal.io.RealConnection.connectTls(RealConnection.java:201)
            at com.android.okhttp.internal.io.RealConnection.connectSocket(RealConnection.java:149)
            at com.android.okhttp.internal.io.RealConnection.connect(RealConnection.java:112)
            at com.android.okhttp.internal.http.StreamAllocation.findConnection(StreamAllocation.java:184)
            at com.android.okhttp.internal.http.StreamAllocation.findHealthyConnection(StreamAllocation.java:126)
            at com.android.okhttp.internal.http.StreamAllocation.newStream(StreamAllocation.java:95)
            at com.android.okhttp.internal.http.HttpEngine.connect(HttpEngine.java:281)
            at com.android.okhttp.internal.http.HttpEngine.sendRequest(HttpEngine.java:224)
            at com.android.okhttp.internal.huc.HttpURLConnectionImpl.execute(HttpURLConnectionImpl.java:461)
            at com.android.okhttp.internal.huc.HttpURLConnectionImpl.connect(HttpURLConnectionImpl.java:127)
            at com.android.okhttp.internal.huc.DelegatingHttpsURLConnection.connect(DelegatingHttpsURLConnection.java:89)
            at com.android.okhttp.internal.huc.HttpsURLConnectionImpl.connect(HttpsURLConnectionImpl.java:26)
            at ...

Android 9 で何かが変更されたようですが、これまでのところ、この動作に関する情報は見つかりませんでした。私の考えは次のとおりです。

  • 証明書のピン留めを行うこの方法は廃止された可能性があります
  • Android 9 では SHA1 証明書でドメインを検証しなくなる可能性があります

他のアイデアはありますか?

4

2 に答える 2

5

私はちょうど同じ問題を抱えていました。Android 9 Change-Log によると、これは SAN を使用しない証明書で予想されます。

RFC 2818 では、ドメイン名を証明書と照合する 2 つの方法について説明しています。subjectAltName (SAN) 拡張内で使用可能な名前を使用する方法と、SAN 拡張がない場合は commonName (CN) にフォールバックする方法です。

ただし、CN へのフォールバックは RFC 2818 で廃止されました。このため、Android は CN の使用にフォールバックしなくなりました。ホスト名を確認するには、サーバーは一致する SAN を含む証明書を提示する必要があります。ホスト名と一致する SAN を含まない証明書は信頼されなくなりました。

出典: 証明書を使用したホスト名の検証

于 2018-09-27T08:24:14.240 に答える