读取 Android 应用程序的 PKCS12/P12 客户端证书文件
Read in PKCS12/P12 Client Cert file for Android App
我正在尝试在我的 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.access0(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.access0(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 文件,将二进制文件转换为字符串,将字符串导出到日志,然后将其用作证书。这消除了将证书文件添加到应用程序的需要。所以,这就是整个事情的发展方向。如果有人有一种快速简便的方法来做到这一点,那就太好了。现在,我只需要弄清楚为什么这部分不起作用。
谢谢!
更新您的 NullPointerException
根据您的logcat信息,GetCert.getThisCert()
中的getResources()
导致了NullPointerException。看起来您还没有将 Context
从 Activity 传递到 GetCert
class。所以,请更新您的代码并检查结果。
更新结束
你可以参考我下面的代码:
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...
、post 更多代码和 logcat 信息(如果有)。
我正在尝试在我的 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.access0(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.access0(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 文件,将二进制文件转换为字符串,将字符串导出到日志,然后将其用作证书。这消除了将证书文件添加到应用程序的需要。所以,这就是整个事情的发展方向。如果有人有一种快速简便的方法来做到这一点,那就太好了。现在,我只需要弄清楚为什么这部分不起作用。
谢谢!
更新您的 NullPointerException
根据您的logcat信息,GetCert.getThisCert()
中的getResources()
导致了NullPointerException。看起来您还没有将 Context
从 Activity 传递到 GetCert
class。所以,请更新您的代码并检查结果。
更新结束
你可以参考我下面的代码:
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...
、post 更多代码和 logcat 信息(如果有)。