安卓WebView如何处理onReceivedClientCertRequest请求

14

我正在使用WebView开发一个Android应用程序,其中涉及到客户端证书认证。该证书(cert.pfx)和密码已被嵌入到应用程序中。

在WebView中使用ajax调用执行客户端证书认证请求时,将调用以下函数:

@Override
public void onReceivedClientCertRequest(WebView view, final ClientCertRequest request) {}

我理解需要打电话:

request.proceed(PrivateKey privateKey, X509Certificate[] chain)
任何想法如何从嵌入式证书创建PrivateKey和X509Certificate对象以便继续请求。顺便问一下,这是否是在Android应用程序上实现客户端证书身份验证的正确方式?如果不是,请建议。
3个回答

20

使用KeyStore获取PrivateKey和X509Certificate对象来解决了它:

    private X509Certificate[] mCertificates;
    private PrivateKey mPrivateKey;

    private void loadCertificateAndPrivateKey() {
          try {
                InputStream certificateFileStream = getClass().getResourceAsStream("/assets/cert.pfx");

                KeyStore keyStore = KeyStore.getInstance("PKCS12");
                String password = "password";
                keyStore.load(certificateFileStream, password != null ? password.toCharArray() : null);

                Enumeration<String> aliases = keyStore.aliases();
                String alias = aliases.nextElement();

                Key key = keyStore.getKey(alias, password.toCharArray());
                if (key instanceof PrivateKey) {
                    mPrivateKey = (PrivateKey)key;
                    Certificate cert = keyStore.getCertificate(alias);
                    mCertificates = new X509Certificate[1];
                    mCertificates[0] = (X509Certificate)cert;
                 }

                 certificateFileStream.close();

            } catch (Exception e) {
                 Log.e(TAG, e.getMessage());
         }
    }


    private WebViewClient mWebViewClient = new WebViewClient() {
        @Override
        public boolean shouldOverrideUrlLoading(WebView view, String url) {
            view.loadUrl(url);
            return false;
        }

        @Override
        public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
            handler.proceed();
        }

        @Override
        public void onReceivedClientCertRequest(WebView view, final ClientCertRequest request) {
            if (mCertificates == null || mPrivateKey == null) {
                loadCertificateAndPrivateKey();
            } 
            request.proceed(mPrivateKey, mCertificates);
        }
    };

我们需要提供 /assets/cert.pfx 文件吗?还是它会默认选择? - Akash kumar

0

这是如何在 Kotlin 中从用户选择并回答 ClientCertRequest 的方法

    override fun onReceivedClientCertRequest(view: WebView?, request: ClientCertRequest?) {
        try {
            KeyChain.choosePrivateKeyAlias(this, {
                if (it == null)
                    return@choosePrivateKeyAlias

                try {
                    val certs = KeyChain.getCertificateChain(this, it)
                    val pKey = KeyChain.getPrivateKey(this, it)
                    request?.proceed(pKey, certs)
                } catch (e: Exception) {
                    request?.cancel()
                    e.printStackTrace()
                }
            }, null, null, null, -1, null)
        } catch (e: Throwable) {
            request?.cancel()
            e.printStackTrace()
        }
    }

0

我遇到了同样的问题,我是这样解决的:

private void initPrivateKeyAndX509Certificate(int clientCertResourceId, String clientCertPassword) throws UnrecoverableKeyException, CertificateException, NoSuchAlgorithmException, KeyStoreException, IOException {
        try {
            InputStream certificateFileStream = null;
            if (clientCertResourceId > 0) {
                certificateFileStream = new ByteArrayInputStream(Utility.readbyteFromResources(clientCertResourceId, MYApplication.getAppContext()));
            }

            if (certificateFileStream != null) {
                KeyStore keyStore = KeyStore.getInstance("PKCS12");
                keyStore.load(certificateFileStream, clientCertPassword != null ? clientCertPassword.toCharArray() : null);

                Enumeration<String> aliases = keyStore.aliases();

                while (aliases.hasMoreElements()) {
                    String alias = aliases.nextElement();
                    Key key = keyStore.getKey(alias, clientCertPassword.toCharArray());
                    if (key instanceof PrivateKey) {
                        mPrivateKey = (PrivateKey) key;
                        Certificate[] arrayOfCertificate = keyStore.getCertificateChain(alias);
                        mCertificates = new X509Certificate[arrayOfCertificate.length];
                        for (int i = 0; i < mCertificates.length; i++) {
                            mCertificates[i] = ((X509Certificate) arrayOfCertificate[i]);
                        }
                        break;
                    }
                }

                certificateFileStream.close();
            }

        } catch (Exception e) {
            Log.e(TAG, e.getMessage());
            throw e;
        }
    }

其中clientCertResourceId是与raw文件夹中p12相关的资源ID。 然后我按照以下方式重写了onReveicedClientCertRequest:

@Override
    public void onReceivedClientCertRequest(WebView view, ClientCertRequest request) {
        if (mPrivateKey != null && mCertificates != null && mCertificates.length != 0) {
            request.proceed(mPrivateKey, mCertificates);
        } else {
            request.cancel();
        }
    }

我在想这种做法是否正确,但目前它能够工作。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接