Support addPkcs12Keystore function in CertTool library.

The function will be called from the credential storage for decoding
the pkcs12 file and saving the certs/keys into mini-keystore.
diff --git a/keystore/java/android/security/CertTool.java b/keystore/java/android/security/CertTool.java
index 26d22ae..c96cd4f 100644
--- a/keystore/java/android/security/CertTool.java
+++ b/keystore/java/android/security/CertTool.java
@@ -16,11 +16,19 @@
 
 package android.security;
 
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateException;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.UnrecoverableKeyException;
+
 import android.content.Context;
 import android.content.Intent;
 import android.security.Keystore;
 import android.text.TextUtils;
-
+import android.util.Log;
 
 /**
  * The CertTool class provides the functions to list the certs/keys,
@@ -41,12 +49,12 @@
     public static final String KEY_NAMESPACE = "namespace";
     public static final String KEY_DESCRIPTION = "description";
 
-    private static final String TAG = "CertTool";
+    public static final String TITLE_CA_CERT = "CA Certificate";
+    public static final String TITLE_USER_CERT = "User Certificate";
+    public static final String TITLE_PKCS12_KEYSTORE = "PKCS12 Keystore";
+    public static final String TITLE_PRIVATE_KEY = "Private Key";
 
-    private static final String TITLE_CA_CERT = "CA Certificate";
-    private static final String TITLE_USER_CERT = "User Certificate";
-    private static final String TITLE_PKCS12_KEYSTORE = "PKCS12 Keystore";
-    private static final String TITLE_PRIVATE_KEY = "Private Key";
+    private static final String TAG = "CertTool";
     private static final String UNKNOWN = "Unknown";
     private static final String ISSUER_NAME = "Issuer Name:";
     private static final String DISTINCT_NAME = "Distinct Name:";
@@ -58,6 +66,11 @@
     private static final String KEYNAME_DELIMITER = "_";
     private static final Keystore sKeystore = Keystore.getInstance();
 
+    private native int getPkcs12Handle(byte[] data, String password);
+    private native String getPkcs12Certificate(int handle);
+    private native String getPkcs12PrivateKey(int handle);
+    private native String popPkcs12CertificateStack(int handle);
+    private native void freePkcs12Handle(int handle);
     private native String generateCertificateRequest(int bits, String subject);
     private native boolean isPkcs12Keystore(byte[] data);
     private native int generateX509Certificate(byte[] data);
@@ -130,10 +143,35 @@
         intent.putExtra(KEY_NAMESPACE + "1", namespace);
     }
 
+    public int addPkcs12Keystore(byte[] p12Data, String password,
+            String keyname) {
+        int handle, i = 0;
+        String pemData;
+        Log.i("CertTool", "addPkcs12Keystore()");
+
+        if ((handle = getPkcs12Handle(p12Data, password)) == 0) return -1;
+        if ((pemData = getPkcs12Certificate(handle)) != null) {
+            sKeystore.put(USER_CERTIFICATE, keyname, pemData);
+        }
+        if ((pemData = getPkcs12PrivateKey(handle)) != null) {
+            sKeystore.put(USER_KEY, keyname, pemData);
+        }
+        while ((pemData = this.popPkcs12CertificateStack(handle)) != null) {
+            if (i++ > 0) {
+                sKeystore.put(CA_CERTIFICATE, keyname + i, pemData);
+            } else {
+                sKeystore.put(CA_CERTIFICATE, keyname, pemData);
+            }
+        }
+        freePkcs12Handle(handle);
+        return 0;
+    }
+
     public synchronized void addCertificate(byte[] data, Context context) {
         int handle;
         Intent intent = null;
 
+        Log.i("CertTool", "addCertificate()");
         if (isPkcs12Keystore(data)) {
             intent = prepareIntent(TITLE_PKCS12_KEYSTORE, data, USER_KEY,
                     UNKNOWN, UNKNOWN);
diff --git a/keystore/jni/cert.c b/keystore/jni/cert.c
index cc36b84..0db28fd 100644
--- a/keystore/jni/cert.c
+++ b/keystore/jni/cert.c
@@ -136,30 +136,126 @@
     return ret_code;
 }
 
-int is_pkcs12(const char *buf, int bufLen)
+PKCS12 *get_p12_handle(const char *buf, int bufLen)
 {
-    int ret = 0;
     BIO *bp = NULL;
     PKCS12  *p12 = NULL;
 
-    if (!buf || bufLen < 1) goto err;
+    if (!buf || (bufLen < 1) || (buf[0] != 48)) goto err;
 
     bp = BIO_new(BIO_s_mem());
     if (!bp) goto err;
 
-    if (buf[0] != 48) goto err; // it is not DER.
-
     if (!BIO_write(bp, buf, bufLen)) goto err;
 
-    if ((p12 = d2i_PKCS12_bio(bp, NULL)) != NULL) {
-        PKCS12_free(p12);
-        ret = 1;
-    }
+    p12 = d2i_PKCS12_bio(bp, NULL);
+
 err:
     if (bp) BIO_free(bp);
+    return p12;
+}
+
+PKCS12_KEYSTORE *get_pkcs12_keystore_handle(const char *buf, int bufLen,
+                                            const char *passwd)
+{
+    PKCS12_KEYSTORE *p12store = NULL;
+    EVP_PKEY *pkey = NULL;
+    X509 *cert = NULL;
+    STACK_OF(X509) *certs = NULL;
+    PKCS12  *p12 = get_p12_handle(buf, bufLen);
+
+    if (p12 == NULL) return NULL;
+    if (!PKCS12_parse(p12, passwd, &pkey, &cert, &certs)) {
+        LOGE("Can not parse PKCS12 content");
+        PKCS12_free(p12);
+        return NULL;
+    }
+    if ((p12store = malloc(sizeof(PKCS12_KEYSTORE))) == NULL) {
+        if (cert) X509_free(cert);
+        if (pkey) EVP_PKEY_free(pkey);
+        if (certs) sk_X509_free(certs);
+    }
+    p12store->p12 = p12;
+    p12store->pkey = pkey;
+    p12store->cert = cert;
+    p12store->certs = certs;
+    return p12store;
+}
+
+void free_pkcs12_keystore(PKCS12_KEYSTORE *p12store)
+{
+    if (p12store != NULL) {
+        if (p12store->cert) X509_free(p12store->cert);
+        if (p12store->pkey) EVP_PKEY_free(p12store->pkey);
+        if (p12store->certs) sk_X509_free(p12store->certs);
+        free(p12store);
+    }
+}
+
+int is_pkcs12(const char *buf, int bufLen)
+{
+    int ret = 0;
+    PKCS12  *p12 = get_p12_handle(buf, bufLen);
+    if (p12 != NULL) ret = 1;
+    PKCS12_free(p12);
     return ret;
 }
 
+static int convert_to_pem(void *data, int is_cert, char *buf, int size)
+{
+    int len = 0;
+    BIO *bio = NULL;
+
+    if (data == NULL) return -1;
+
+    if ((bio = BIO_new(BIO_s_mem())) == NULL) goto err;
+    if (is_cert) {
+        if ((len = PEM_write_bio_X509(bio, (X509*)data)) == 0) {
+            goto err;
+        }
+    } else {
+        if ((len = PEM_write_bio_PrivateKey(bio, (EVP_PKEY *)data, NULL,
+                                            NULL, 0, NULL, NULL)) == 0) {
+            goto err;
+        }
+    }
+    if (len < size && (len = BIO_read(bio, buf, size - 1)) > 0) {
+        buf[len] = 0;
+    }
+err:
+    if (bio) BIO_free(bio);
+    return (len == 0) ? -1 : 0;
+}
+
+int get_pkcs12_certificate(PKCS12_KEYSTORE *p12store, char *buf, int size)
+{
+    if ((p12store != NULL) && (p12store->cert != NULL)) {
+        return convert_to_pem((void*)p12store->cert, 1, buf, size);
+    }
+    return -1;
+}
+
+int get_pkcs12_private_key(PKCS12_KEYSTORE *p12store, char *buf, int size)
+{
+    if ((p12store != NULL) && (p12store->pkey != NULL)) {
+        return convert_to_pem((void*)p12store->pkey, 0, buf, size);
+    }
+    return -1;
+}
+
+int pop_pkcs12_certs_stack(PKCS12_KEYSTORE *p12store, char *buf, int size)
+{
+    X509 *cert = NULL;
+
+    if ((p12store != NULL) && (p12store->certs != NULL) &&
+        ((cert = sk_X509_pop(p12store->certs)) != NULL)) {
+        int ret = convert_to_pem((void*)cert, 1, buf, size);
+        X509_free(cert);
+        return ret;
+    }
+    return -1;
+}
+
 X509* parse_cert(const char *buf, int bufLen)
 {
     X509 *cert = NULL;
diff --git a/keystore/jni/cert.h b/keystore/jni/cert.h
index a9807b1..aaa7602 100644
--- a/keystore/jni/cert.h
+++ b/keystore/jni/cert.h
@@ -41,6 +41,13 @@
     int key_len;
 } PKEY_STORE;
 
+typedef struct {
+    PKCS12  *p12;
+    EVP_PKEY *pkey;
+    X509 *cert;
+    STACK_OF(X509) *certs;
+} PKCS12_KEYSTORE;
+
 #define PKEY_STORE_free(x) { \
     if(x.pkey) EVP_PKEY_free(x.pkey); \
     if(x.public_key) free(x.public_key); \
@@ -49,8 +56,14 @@
 #define nelem(x) (sizeof (x) / sizeof *(x))
 
 int gen_csr(int bits, const char *organizations, char reply[REPLY_MAX]);
+PKCS12_KEYSTORE *get_pkcs12_keystore_handle(const char *buf, int bufLen,
+                                            const char *passwd);
+int get_pkcs12_certificate(PKCS12_KEYSTORE *p12store, char *buf, int size);
+int get_pkcs12_private_key(PKCS12_KEYSTORE *p12store, char *buf, int size);
+int pop_pkcs12_certs_stack(PKCS12_KEYSTORE *p12store, char *buf, int size);
+void free_pkcs12_keystore(PKCS12_KEYSTORE *p12store);
 int is_pkcs12(const char *buf, int bufLen);
-X509*    parse_cert(const char *buf, int bufLen);
+X509 *parse_cert(const char *buf, int bufLen);
 int get_cert_name(X509 *cert, char *buf, int size);
 int get_issuer_name(X509 *cert, char *buf, int size);
 int is_ca_cert(X509 *cert);
diff --git a/keystore/jni/certtool.c b/keystore/jni/certtool.c
index fabf5cd..1ae8dab 100644
--- a/keystore/jni/certtool.c
+++ b/keystore/jni/certtool.c
@@ -19,10 +19,13 @@
 #include <string.h>
 #include <jni.h>
 #include <cutils/log.h>
+#include <openssl/pkcs12.h>
 #include <openssl/x509v3.h>
 
 #include "cert.h"
 
+typedef int PKCS12_KEYSTORE_FUNC(PKCS12_KEYSTORE *store, char *buf, int size);
+
 jstring
 android_security_CertTool_generateCertificateRequest(JNIEnv* env,
                                                      jobject thiz,
@@ -42,12 +45,88 @@
                                            jobject thiz,
                                            jbyteArray data)
 {
-    char buf[REPLY_MAX];
     int len = (*env)->GetArrayLength(env, data);
 
-    if (len > REPLY_MAX) return 0;
-    (*env)->GetByteArrayRegion(env, data, 0, len, (jbyte*)buf);
-    return (jboolean) is_pkcs12(buf, len);
+    if (len > 0) {
+        PKCS12 *handle = NULL;
+        char buf[len];
+
+        (*env)->GetByteArrayRegion(env, data, 0, len, (jbyte*)buf);
+        return (jboolean)is_pkcs12(buf, len);
+    } else {
+        return 0;
+    }
+}
+
+jint
+android_security_CertTool_getPkcs12Handle(JNIEnv* env,
+                                          jobject thiz,
+                                          jbyteArray data,
+                                          jstring jPassword)
+{
+    jboolean bIsCopy;
+    int len = (*env)->GetArrayLength(env, data);
+    const char* passwd = (*env)->GetStringUTFChars(env, jPassword , &bIsCopy);
+
+    if (len > 0) {
+        PKCS12_KEYSTORE *handle = NULL;
+        char buf[len];
+
+        (*env)->GetByteArrayRegion(env, data, 0, len, (jbyte*)buf);
+        handle = get_pkcs12_keystore_handle(buf, len, passwd);
+        (*env)->ReleaseStringUTFChars(env, jPassword, passwd);
+        return (jint)handle;
+    } else {
+        return 0;
+    }
+}
+
+jstring call_pkcs12_ks_func(PKCS12_KEYSTORE_FUNC *func,
+                            JNIEnv* env,
+                            jobject thiz,
+                            jint phandle)
+{
+    char buf[REPLY_MAX];
+
+    if (phandle == 0) return NULL;
+    if (func((PKCS12_KEYSTORE*)phandle, buf, sizeof(buf)) == 0) {
+        return (*env)->NewStringUTF(env, buf);
+    }
+    return NULL;
+}
+
+jstring
+android_security_CertTool_getPkcs12Certificate(JNIEnv* env,
+                                               jobject thiz,
+                                               jint phandle)
+{
+    return call_pkcs12_ks_func((PKCS12_KEYSTORE_FUNC *)get_pkcs12_certificate,
+                               env, thiz, phandle);
+}
+
+jstring
+android_security_CertTool_getPkcs12PrivateKey(JNIEnv* env,
+                                              jobject thiz,
+                                              jint phandle)
+{
+    return call_pkcs12_ks_func((PKCS12_KEYSTORE_FUNC *)get_pkcs12_private_key,
+                               env, thiz, phandle);
+}
+
+jstring
+android_security_CertTool_popPkcs12CertificateStack(JNIEnv* env,
+                                                    jobject thiz,
+                                                    jint phandle)
+{
+    return call_pkcs12_ks_func((PKCS12_KEYSTORE_FUNC *)pop_pkcs12_certs_stack,
+                               env, thiz, phandle);
+}
+
+void android_security_CertTool_freePkcs12Handle(JNIEnv* env,
+                                                jobject thiz,
+                                                jint handle)
+{
+    if (handle != 0) free_pkcs12_keystore((PKCS12_KEYSTORE*)handle);
 }
 
 jint
@@ -117,6 +196,16 @@
         (void*)android_security_CertTool_generateCertificateRequest},
     {"isPkcs12Keystore", "([B)Z",
         (void*)android_security_CertTool_isPkcs12Keystore},
+    {"getPkcs12Handle", "([BLjava/lang/String;)I",
+        (void*)android_security_CertTool_getPkcs12Handle},
+    {"getPkcs12Certificate", "(I)Ljava/lang/String;",
+        (void*)android_security_CertTool_getPkcs12Certificate},
+    {"getPkcs12PrivateKey", "(I)Ljava/lang/String;",
+        (void*)android_security_CertTool_getPkcs12PrivateKey},
+    {"popPkcs12CertificateStack", "(I)Ljava/lang/String;",
+        (void*)android_security_CertTool_popPkcs12CertificateStack},
+    {"freePkcs12Handle", "(I)V",
+        (void*)android_security_CertTool_freePkcs12Handle},
     {"generateX509Certificate", "([B)I",
         (void*)android_security_CertTool_generateX509Certificate},
     {"isCaCertificate", "(I)Z",