Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c9c119e7 authored by Chung-yih Wang's avatar Chung-yih Wang
Browse files

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.
parent f32f746b
Loading
Loading
Loading
Loading
+44 −6
Original line number Diff line number Diff line
@@ -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 class CertTool {
    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 @@ public class CertTool {
    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 @@ public class CertTool {
        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);
+105 −9
Original line number Diff line number Diff line
@@ -136,29 +136,125 @@ err:
    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) {
    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);
        ret = 1;
        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 (bp) BIO_free(bp);
    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)
{
+14 −1
Original line number Diff line number Diff line
@@ -41,6 +41,13 @@ typedef struct {
    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,6 +56,12 @@ typedef struct {
#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);
int get_cert_name(X509 *cert, char *buf, int size);
+93 −4
Original line number Diff line number Diff line
@@ -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 @@ android_security_CertTool_isPkcs12Keystore(JNIEnv* env,
                                           jobject thiz,
                                           jbyteArray data)
{
    char buf[REPLY_MAX];
    int len = (*env)->GetArrayLength(env, data);

    if (len > REPLY_MAX) return 0;
    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 @@ static JNINativeMethod gCertToolMethods[] = {
        (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",