【问题标题】:Read in PKCS12/P12 Client Cert file for Android App读取 Android 应用程序的 PKCS12/P12 客户端证书文件
【发布时间】:2015-09-09 15:46:33
【问题描述】:

我正在尝试在我的 Android 应用程序中使用客户端证书,以确保仅与使用该应用程序或拥有该证书的人进行 HTTPS 通信。

我的 res/raw 文件夹中有证书。当我开始输入“R.raw”时,Android Studio 会看到它。

但是,当我使用以下任何代码时,变量返回为具有“null”值:

FileInputStream fis = null;
fis = (FileInputStream) getClass().getResourceAsStream(String.valueOf(R.raw.clientcert2));

InputStream fis = getClass().getResourceAsStream("raw/clientcert2.p12");

InputStream fis = null;
fis= (InputStream) getResources().openRawResource(R.raw.clientcert2);

最后一个实际上使应用程序崩溃,这让我认为它甚至不接近正确。它崩溃并出现以下错误:

09-10 08:13:06.677  10310-10310/com.example.agenice.clientcert E/AndroidRuntime﹕ FATAL EXCEPTION: main
Process: com.example.agenice.clientcert, PID: 10310
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.agenice.clientcert/com.example.agenice.clientcert.MainActivity}: java.lang.NullPointerException
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2184)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2233)
at android.app.ActivityThread.access$800(ActivityThread.java:135)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1196)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:136)
at android.app.ActivityThread.main(ActivityThread.java:5001)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:515)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:785)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:601)
at dalvik.system.NativeStart.main(Native Method)
 Caused by: java.lang.NullPointerException
at android.content.ContextWrapper.getResources(ContextWrapper.java:89)
at android.view.ContextThemeWrapper.getResources(ContextThemeWrapper.java:78)
at com.example.agenice.clientcert.GetCert.getThisCert(MainActivity.java:92)
at com.example.agenice.clientcert.MainActivity.onCreate(MainActivity.java:58)
at android.app.Activity.performCreate(Activity.java:5231)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1087)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2148)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2233)
at android.app.ActivityThread.access$800(ActivityThread.java:135)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1196)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:136)
at android.app.ActivityThread.main(ActivityThread.java:5001)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:515)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:785)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:601)
at dalvik.system.NativeStart.main(Native Method)

附带说明 - 是的,我知道如果有人获得 APK,他们可以简单地解压缩包并获取证书。这就是为什么这不是应用程序的最终形式。这只是过程中的第一步。在 iOS 版本的应用程序中,我能够导入 p12 文件,将二进制文件转换为字符串,将字符串导出到日志中,然后将其用作证书。这消除了将证书文件添加到应用程序的需要。所以,这就是整件事的发展方向。如果有人有一种快速简便的方法来做到这一点,那就太好了。现在,我只需要弄清楚为什么这部分不起作用。

谢谢!

【问题讨论】:

    标签: android ssl fileinputstream client-certificates self-signed


    【解决方案1】:

    更新您的NullPointerException

    根据您的 logcat 信息,GetCert.getThisCert() 中的 getResources() 导致 NullPointerException。看起来您尚未将 Activity 中的 Context 传递给 GetCert 类。所以,请更新您的代码并检查结果。

    更新结束

    你可以参考我下面的代码:

    private TrustManager[] getWrappedTrustManagers(TrustManager[] trustManagers) {
            final X509TrustManager originalTrustManager = (X509TrustManager) trustManagers[0];
            return new TrustManager[]{
                    new X509TrustManager() {
                        public X509Certificate[] getAcceptedIssuers() {
                            return originalTrustManager.getAcceptedIssuers();
                        }
    
                        public void checkClientTrusted(X509Certificate[] certs, String authType) {
                            try {
                                originalTrustManager.checkClientTrusted(certs, authType);
                            } catch (CertificateException e) {
                                e.printStackTrace();
                            }
                        }
    
                        public void checkServerTrusted(X509Certificate[] certs, String authType) {
                            try {
                                originalTrustManager.checkServerTrusted(certs, authType);
                            } catch (CertificateException e) {
                                e.printStackTrace();
                            }
                        }
                    }
            };
        }
    
    private SSLSocketFactory getSSLSocketFactory_Certificate(String keyStoreType, int keystoreResId)
            throws CertificateException, KeyStoreException, IOException, NoSuchAlgorithmException, KeyManagementException {
    
        CertificateFactory cf = CertificateFactory.getInstance("X.509");
        InputStream caInput = getResources().openRawResource(keystoreResId);
    
        Certificate ca = cf.generateCertificate(caInput);
        caInput.close();
    
        if (keyStoreType == null || keyStoreType.length() == 0) {
            keyStoreType = KeyStore.getDefaultType();
        }
        KeyStore keyStore = KeyStore.getInstance(keyStoreType);
        keyStore.load(null, null);
        keyStore.setCertificateEntry("ca", ca);
    
        String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
        TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
        tmf.init(keyStore);
    
        TrustManager[] wrappedTrustManagers = getWrappedTrustManagers(tmf.getTrustManagers());
    
        SSLContext sslContext = SSLContext.getInstance("TLS");
        sslContext.init(null, wrappedTrustManagers, null);
    
        return sslContext.getSocketFactory();
    }
    
    private SSLSocketFactory getSSLSocketFactory_KeyStore(String keyStoreType, int keystoreResId, String keyPassword)
                throws CertificateException, KeyStoreException, IOException, NoSuchAlgorithmException, KeyManagementException {
    
            InputStream caInput = getResources().openRawResource(keystoreResId);
    
            // creating a KeyStore containing trusted CAs
    
            if (keyStoreType == null || keyStoreType.length() == 0) {
                keyStoreType = KeyStore.getDefaultType();
            }
            KeyStore keyStore = KeyStore.getInstance(keyStoreType);
    
            keyStore.load(caInput, keyPassword.toCharArray());
    
            // creating a TrustManager that trusts the CAs in the KeyStore
    
            String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
            TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
            tmf.init(keyStore);
    
            TrustManager[] wrappedTrustManagers = getWrappedTrustManagers(tmf.getTrustManagers());
    
            SSLContext sslContext = SSLContext.getInstance("TLS");
            sslContext.init(null, wrappedTrustManagers, null);
    
            return sslContext.getSocketFactory();
        }
    

    然后调用以下两个之一(一个用于密钥库文件,另一个用于证书文件):

    SSLSocketFactory sslSocketFactory = getSSLSocketFactory_KeyStore("BKS", R.raw.androidbksv1, "123456789");
    SSLSocketFactory sslSocketFactory = getSSLSocketFactory_Certificate("BKS", R.raw.androidbksv1_cert);
    

    因为您有 PKCS12 文件,您可以使用以下两种之一:

    SSLSocketFactory sslSocketFactory = getSSLSocketFactory_KeyStore("PKCS12", R.raw.androidpkcs12, "123456789");        
    SSLSocketFactory sslSocketFactory = getSSLSocketFactory_Certificate("PKCS12", R.raw.androidpkcs12_cert);
    

    以上代码已在我的 HTTPS 项目中测试成功。希望这会有所帮助!

    此外,请澄清The last one actually crashes the app...,如果可用,请发布更多代码和logcat信息。

    【讨论】:

    • BNK,这太棒了!谢谢。不过,当我运行任一行时,我都会收到以下错误。 9885-9885/com.example.agenice.clientcert E/AndroidRuntime﹕ FATAL EXCEPTION: main Process: com.example.agenice.clientcert, PID: 9885 java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.agenice.clientcert/com.example.agenice.clientcert.Main‌​Activity}: java.lang.NullPointerException 这与我运行另一行时遇到的错误相同(见上面的编辑)
    • 我确实让它工作了。我没有在课堂上创建 Context 变量。在我的电话中添加getThisCert(MainActivity.this)。然后这个到 getThisCert 方法this.context = context;。然后将上下文放在getResources() 调用前面解决了我的问题。感谢您的帮助!
    • 天哪,我刚刚更新了关于Context 的答案。很高兴你修好了:)
    • 不用担心。你帮了大忙。 2 天的搜索,您在几分钟内就让我走上了正确的道路。感谢您的帮助!
    猜你喜欢
    • 2012-01-08
    • 2021-09-24
    • 2011-04-02
    • 2013-01-14
    • 1970-01-01
    • 2013-06-02
    • 2019-06-01
    • 2020-01-23
    • 1970-01-01
    相关资源
    最近更新 更多