web-dev-qa-db-ja.com

証明書が特定の自己署名CAから署名されているかどうかをWebViewClientのonReceivedSslError()メソッドで確認します。

WebViewClientonReceivedSslError()をオーバーライドしたいと思います。ここで、error.getCertificate()証明書が自己署名CAから署名されているかどうかを確認し、この場合のみhandler.proceed()を呼び出します。疑似コード:

@Override
public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
    SslCertificate serverCertificate = error.getCertificate();

    if (/* signed from my self-signed CA */) {
        handler.proceed();
    }
    else {
        super.onReceivedSslError(view, handler, error);
    }
}

私のCAの公開鍵は、rootca.bksというBouncyCastleリソースに保存されています。どのようにできるのか?

17
Dev

私はあなたが次のように試すことができると思います:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    try {
        WebView webView = (WebView) findViewById(R.id.webView);
        if (webView != null) {
            // Get cert from raw resource...
            CertificateFactory cf = CertificateFactory.getInstance("X.509");
            InputStream caInput = getResources().openRawResource(R.raw.rootca); // stored at \app\src\main\res\raw
            final Certificate certificate = cf.generateCertificate(caInput);
            caInput.close();

            String url = "https://www.yourserver.com";
            webView.setWebViewClient(new WebViewClient() {                    
                @Override
                public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
                    // Get cert from SslError
                    SslCertificate sslCertificate = error.getCertificate();
                    Certificate cert = getX509Certificate(sslCertificate);
                    if (cert != null && certificate != null){
                        try {
                            // Reference: https://developer.Android.com/reference/Java/security/cert/Certificate.html#verify(Java.security.PublicKey)
                            cert.verify(certificate.getPublicKey()); // Verify here...
                            handler.proceed();
                        } catch (CertificateException | NoSuchAlgorithmException | InvalidKeyException | NoSuchProviderException | SignatureException e) {
                            super.onReceivedSslError(view, handler, error);
                            e.printStackTrace();
                        }
                    } else {
                        super.onReceivedSslError(view, handler, error);
                    }
                }
            });

            webView.loadUrl(url);
        }
    } catch (Exception e){
        e.printStackTrace();
    }
}

// credits to @Heath Borders at http://stackoverflow.com/questions/20228800/how-do-i-validate-an-Android-net-http-sslcertificate-with-an-x509trustmanager
private Certificate getX509Certificate(SslCertificate sslCertificate){
    Bundle bundle = SslCertificate.saveState(sslCertificate);
    byte[] bytes = bundle.getByteArray("x509-certificate");
    if (bytes == null) {
        return null;
    } else {
        try {
            CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
            return certFactory.generateCertificate(new ByteArrayInputStream(bytes));
        } catch (CertificateException e) {
            return null;
        }
    }
}

検証に失敗した場合、logcatにはJava.security.SignatureException: Signature was not verified...などの情報が含まれます

成功した場合のスクリーンショットは次のとおりです。

BNK's screenshot

20
BNK

これはうまくいくと思います(_SSL_IDMISMATCH_は「ホスト名の不一致」を意味します)。

_@Override
public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
    SslCertificate serverCertificate = error.getCertificate();

    if (error.hasError(SSL_UNTRUSTED)) {
        // Check if Cert-Domain equals the Uri-Domain
        String certDomain = serverCertificate.getIssuedTo().getCName();
        if(certDomain.equals(new URL(error.getUrl()).getHost())) {
          handler.proceed();
        }
    }
    else {
        super.onReceivedSslError(view, handler, error);
    }
}
_

「hasError()」が機能しない場合は、error.getPrimaryError() == SSL_IDMISMATCHを試してください

すべてのエラータイプについて、 SslErrorのドキュメント を確認します。

EDIT:自分の自己証明書サーバー(Xampp)で関数をテストしたところ、エラー#3が発生しました。つまり、自己署名証明書のerror.hasError(SslError.SSL_UNTRUSTED)を確認する必要があります。

5
Radon8472

ドキュメントに基づく:

クラスSslCertificateのメソッドgetIssuedBy().getDName()を使用してみましたか?このメソッドは、「この証明書を発行したエンティティ」を表す文字列を返します。

ここを見てください: http://developer.Android.com/reference/Android/net/http/SslCertificate.html#getIssuedBy()

次に、自己署名された文字列が返されることを知る必要があります。

編集:それが自己署名されている場合は空の文字列を返し、そうでない場合はエンティティを返すと思います

よろしく

0
Oldskultxo