Add CertTool for handling the keygen and certificate download.

1. Have the new Keystore for mini-keystore impelemntation.
2. Add CertTool library and jni dll for handling keygen and certificates.
3. Make Reply hidden.
4. Revert some 'incorrect' change and correct the description.
diff --git a/keystore/java/android/security/CertTool.java b/keystore/java/android/security/CertTool.java
new file mode 100644
index 0000000..285def2
--- /dev/null
+++ b/keystore/java/android/security/CertTool.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security;
+
+import android.content.Context;
+import android.content.Intent;
+import android.security.Keystore;
+import android.text.TextUtils;
+
+
+/**
+ * The CertTool class provides the functions to list the certs/keys,
+ * generate the certificate request(csr) and store the certificate into
+ * keystore.
+ *
+ * {@hide}
+ */
+public class CertTool {
+    public static final String ACTION_ADD_CREDENTIAL =
+            "android.security.ADD_CREDENTIAL";
+    public static final String KEY_TYPE_NAME = "typeName";
+    public static final String KEY_ITEM = "item";
+    public static final String KEY_NAMESPACE = "namespace";
+    public static final String KEY_DESCRIPTION = "description";
+
+    private static final String TAG = "CertTool";
+
+    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 UNKNOWN = "Unknown";
+    private static final String ISSUER_NAME = "Issuer Name:";
+    private static final String DISTINCT_NAME = "Distinct Name:";
+
+    private static final String CA_CERTIFICATE = "CACERT";
+    private static final String USER_CERTIFICATE = "USRCERT";
+    private static final String USER_KEY = "USRKEY";
+
+    private static final String KEYNAME_DELIMITER = " ";
+    private static final Keystore keystore = Keystore.getInstance();
+
+    private native String generateCertificateRequest(int bits, String subject);
+    private native boolean isPkcs12Keystore(byte[] data);
+    private native int generateX509Certificate(byte[] data);
+    private native boolean isCaCertificate(int handle);
+    private native String getIssuerDN(int handle);
+    private native String getCertificateDN(int handle);
+    private native String getPrivateKeyPEM(int handle);
+    private native void freeX509Certificate(int handle);
+
+    public String getUserPrivateKey(String key) {
+        return USER_KEY + KEYNAME_DELIMITER + key;
+    }
+
+    public String getUserCertificate(String key) {
+        return USER_CERTIFICATE + KEYNAME_DELIMITER + key;
+    }
+
+    public String getCaCertificate(String key) {
+        return CA_CERTIFICATE + KEYNAME_DELIMITER + key;
+    }
+
+    public String[] getAllUserCertificateKeys() {
+        return keystore.listKeys(USER_KEY);
+    }
+
+    public String[] getAllCaCertificateKeys() {
+        return keystore.listKeys(CA_CERTIFICATE);
+    }
+
+    public String[] getSupportedKeyStrenghs() {
+        return new String[] {"High Grade", "Medium Grade"};
+    }
+
+    private int getKeyLength(int index) {
+        if (index == 0) return 2048;
+        return 1024;
+    }
+
+    public String generateKeyPair(int keyStrengthIndex, String challenge,
+            String dirName) {
+        return generateCertificateRequest(getKeyLength(keyStrengthIndex),
+                dirName);
+    }
+
+    private Intent prepareIntent(String title, byte[] data, String namespace,
+            String issuer, String distinctName) {
+        Intent intent = new Intent(ACTION_ADD_CREDENTIAL);
+        intent.putExtra(KEY_TYPE_NAME, title);
+        intent.putExtra(KEY_ITEM + "0", data);
+        intent.putExtra(KEY_NAMESPACE + "0", namespace);
+        intent.putExtra(KEY_DESCRIPTION + "0", ISSUER_NAME + issuer);
+        intent.putExtra(KEY_DESCRIPTION + "1", DISTINCT_NAME + distinctName);
+        return intent;
+    }
+
+    private void addExtraIntentInfo(Intent intent, String namespace,
+            String data) {
+        intent.putExtra(KEY_ITEM + "1", data);
+        intent.putExtra(KEY_NAMESPACE + "1", namespace);
+    }
+
+    public synchronized void addCertificate(byte[] data, Context context) {
+        int handle;
+        Intent intent = null;
+
+        if (isPkcs12Keystore(data)) {
+            intent = prepareIntent(TITLE_PKCS12_KEYSTORE, data, USER_KEY,
+                    UNKNOWN, UNKNOWN);
+        } else if ((handle = generateX509Certificate(data)) != 0) {
+            String issuer = getIssuerDN(handle);
+            String distinctName = getCertificateDN(handle);
+            String privateKeyPEM = getPrivateKeyPEM(handle);
+            if (isCaCertificate(handle)) {
+                intent = prepareIntent(TITLE_CA_CERT, data, CA_CERTIFICATE,
+                        issuer, distinctName);
+            } else {
+                intent = prepareIntent(TITLE_USER_CERT, data, USER_CERTIFICATE,
+                        issuer, distinctName);
+                if (!TextUtils.isEmpty(privateKeyPEM)) {
+                    addExtraIntentInfo(intent, USER_KEY, privateKeyPEM);
+                }
+            }
+            freeX509Certificate(handle);
+        }
+        if (intent != null) context.startActivity(intent);
+    }
+}
diff --git a/keystore/java/android/security/Keystore.java b/keystore/java/android/security/Keystore.java
index 2a3e6a7..462645a6 100644
--- a/keystore/java/android/security/Keystore.java
+++ b/keystore/java/android/security/Keystore.java
@@ -20,6 +20,7 @@
  * The Keystore class provides the functions to list the certs/keys in keystore.
  * {@hide}
  */
+
 public abstract class Keystore {
     private static final String TAG = "Keystore";
     private static final String[] NOTFOUND = new String[0];
@@ -30,25 +31,18 @@
         return new FileKeystore();
     }
 
-    // for compatiblity, start from here
-    /**
-     */
-    public abstract String getUserkey(String key);
+    public abstract int lock();
+    public abstract int unlock(String password);
+    public abstract int getState();
+    public abstract int changePassword(String oldPassword, String newPassword);
+    public abstract int setPassword(String firstPassword);
+    public abstract String[] listKeys(String namespace);
+    public abstract int put(String namespace, String keyname, String value);
+    public abstract String get(String namespace, String keyname);
+    public abstract int remove(String namespace, String keyname);
+    public abstract int reset();
 
-    /**
-     */
-    public abstract String getCertificate(String key);
-
-    /**
-     */
-    public abstract String[] getAllCertificateKeys();
-
-    /**
-     */
-    public abstract String[] getAllUserkeyKeys();
-
-    // to here
-
+    // TODO: for migrating to the mini-keystore, clean up from here
     /**
      */
     public abstract String getCaCertificate(String key);
@@ -89,101 +83,41 @@
             int keyStrengthIndex, String challenge, String organizations);
 
     public abstract void addCertificate(byte[] cert);
+    // to here
 
     private static class FileKeystore extends Keystore {
         private static final String SERVICE_NAME = "keystore";
-        private static final String LIST_CA_CERTIFICATES = "listcacerts";
-        private static final String LIST_USER_CERTIFICATES = "listusercerts";
-        private static final String GET_CA_CERTIFICATE = "getcacert";
-        private static final String GET_USER_CERTIFICATE = "getusercert";
-        private static final String GET_USER_KEY = "getuserkey";
-        private static final String ADD_CA_CERTIFICATE = "addcacert";
-        private static final String ADD_USER_CERTIFICATE = "addusercert";
-        private static final String ADD_USER_KEY = "adduserkey";
-        private static final String COMMAND_DELIMITER = "\t";
+        private static final String CA_CERTIFICATE = "CaCertificate";
+        private static final String USER_CERTIFICATE = "UserCertificate";
+        private static final String USER_KEY = "UserPrivateKey";
+        private static final String COMMAND_DELIMITER = " ";
         private static final ServiceCommand mServiceCommand =
                 new ServiceCommand(SERVICE_NAME);
 
-        // for compatiblity, start from here
-
-        private static final String LIST_CERTIFICATES = "listcerts";
-        private static final String LIST_USERKEYS = "listuserkeys";
-        private static final String PATH = "/data/misc/keystore/";
-        private static final String USERKEY_PATH = PATH + "userkeys/";
-        private static final String CERT_PATH = PATH + "certs/";
-
-        @Override
-        public String getUserkey(String key) {
-            return USERKEY_PATH + key;
-        }
-
-        @Override
-        public String getCertificate(String key) {
-            return CERT_PATH + key;
-        }
-
-        @Override
-        public String[] getAllCertificateKeys() {
-            try {
-                String result = mServiceCommand.execute(LIST_CERTIFICATES);
-                if (result != null) return result.split("\\s+");
-                return NOTFOUND;
-            } catch (NumberFormatException ex) {
-                return NOTFOUND;
-            }
-        }
-
-        @Override
-        public String[] getAllUserkeyKeys() {
-            try {
-                String result = mServiceCommand.execute(LIST_USERKEYS);
-                if (result != null) return result.split("\\s+");
-                return NOTFOUND;
-            } catch (NumberFormatException ex) {
-                return NOTFOUND;
-            }
-        }
-
-        // to here
-
+        // TODO: for migrating to the mini-keystore, start from here
         @Override
         public String getUserPrivateKey(String key) {
-            return mServiceCommand.execute(
-                    GET_USER_KEY + COMMAND_DELIMITER + key);
+            return "";
         }
 
         @Override
         public String getUserCertificate(String key) {
-            return mServiceCommand.execute(
-                    GET_USER_CERTIFICATE + COMMAND_DELIMITER + key);
+            return "";
         }
 
         @Override
         public String getCaCertificate(String key) {
-            return mServiceCommand.execute(
-                    GET_CA_CERTIFICATE + COMMAND_DELIMITER + key);
+            return "";
         }
 
         @Override
         public String[] getAllUserCertificateKeys() {
-            try {
-                String result = mServiceCommand.execute(LIST_USER_CERTIFICATES);
-                if (result != null) return result.split("\\s+");
-                return NOTFOUND;
-            } catch (NumberFormatException ex) {
-                return NOTFOUND;
-            }
+            return new String[0];
         }
 
         @Override
         public String[] getAllCaCertificateKeys() {
-            try {
-                String result = mServiceCommand.execute(LIST_CA_CERTIFICATES);
-                if (result != null) return result.split("\\s+");
-                return NOTFOUND;
-            } catch (NumberFormatException ex) {
-                return NOTFOUND;
-            }
+          return new String[0];
         }
 
         @Override
@@ -221,25 +155,77 @@
             // TODO: real implementation
         }
 
-        private boolean addUserCertificate(String key, String certificate,
-                String privateKey) {
-            if(mServiceCommand.execute(ADD_USER_CERTIFICATE + COMMAND_DELIMITER
-                    + key + COMMAND_DELIMITER + certificate) != null) {
-                if (mServiceCommand.execute(ADD_USER_KEY + COMMAND_DELIMITER
-                        + key + COMMAND_DELIMITER + privateKey) != null) {
-                    return true;
-                }
-            }
-            return false;
+        // to here
+
+        @Override
+        public int lock() {
+            Reply result = mServiceCommand.execute(ServiceCommand.LOCK, null);
+            return (result != null) ? result.returnCode : -1;
         }
 
-        private boolean addCaCertificate(String key, String content) {
-            if (mServiceCommand.execute(ADD_CA_CERTIFICATE + COMMAND_DELIMITER
-                    + key + COMMAND_DELIMITER + content) != null) {
-                return true;
-            }
-            return false;
+        @Override
+        public int unlock(String password) {
+            Reply result = mServiceCommand.execute(ServiceCommand.UNLOCK,
+                    password);
+            return (result != null) ? result.returnCode : -1;
         }
 
+        @Override
+        public int getState() {
+            Reply result = mServiceCommand.execute(ServiceCommand.GET_STATE,
+                    null);
+            return (result != null) ? result.returnCode : -1;
+        }
+
+        @Override
+        public int changePassword(String oldPassword, String newPassword) {
+            Reply result = mServiceCommand.execute(ServiceCommand.PASSWD,
+                    oldPassword + " " + newPassword);
+            return (result != null) ? result.returnCode : -1;
+        }
+
+        @Override
+        public int setPassword(String firstPassword) {
+            Reply result = mServiceCommand.execute(ServiceCommand.PASSWD,
+                    firstPassword);
+            return (result != null) ? result.returnCode : -1;
+        }
+
+        @Override
+        public String[] listKeys(String namespace) {
+            Reply result = mServiceCommand.execute(ServiceCommand.LIST_KEYS,
+                    namespace);
+            return (result != null) ? ((result.returnCode != 0) ? NOTFOUND :
+                    new String(result.data, 0, result.len).split("\\s+"))
+                    : NOTFOUND;
+        }
+
+        @Override
+        public int put(String namespace, String keyname, String value) {
+            Reply result = mServiceCommand.execute(ServiceCommand.PUT_KEY,
+                    namespace + " " + keyname + " " + value);
+            return (result != null) ? result.returnCode : -1;
+        }
+
+        @Override
+        public String get(String namespace, String keyname) {
+            Reply result = mServiceCommand.execute(ServiceCommand.GET_KEY,
+                    namespace + " " + keyname);
+            return (result != null) ? ((result.returnCode != 0) ? null :
+                    new String(result.data, 0, result.len)) : null;
+        }
+
+        @Override
+        public int remove(String namespace, String keyname) {
+            Reply result = mServiceCommand.execute(ServiceCommand.REMOVE_KEY,
+                    namespace + " " + keyname);
+            return (result != null) ? result.returnCode : -1;
+        }
+
+        @Override
+        public int reset() {
+            Reply result = mServiceCommand.execute(ServiceCommand.RESET, null);
+            return (result != null) ? result.returnCode : -1;
+        }
     }
 }
diff --git a/keystore/java/android/security/Reply.java b/keystore/java/android/security/Reply.java
new file mode 100644
index 0000000..15a0dde
--- /dev/null
+++ b/keystore/java/android/security/Reply.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security;
+
+/*
+ * {@hide}
+ */
+public class Reply {
+    public int len;
+    public int returnCode;
+    public byte[] data = new byte[ServiceCommand.BUFFER_LENGTH];
+}
diff --git a/keystore/java/android/security/ServiceCommand.java b/keystore/java/android/security/ServiceCommand.java
index f1d4302..2f335be 100644
--- a/keystore/java/android/security/ServiceCommand.java
+++ b/keystore/java/android/security/ServiceCommand.java
@@ -35,15 +35,25 @@
     public static final String SUCCESS = "0";
     public static final String FAILED = "-1";
 
+    // Opcodes for keystore commands.
+    public static final int LOCK = 0;
+    public static final int UNLOCK = 1;
+    public static final int PASSWD = 2;
+    public static final int GET_STATE = 3;
+    public static final int LIST_KEYS = 4;
+    public static final int GET_KEY = 5;
+    public static final int PUT_KEY = 6;
+    public static final int REMOVE_KEY = 7;
+    public static final int RESET = 8;
+    public static final int MAX_CMD_INDEX = 9;
+
+    public static final int BUFFER_LENGTH = 4096;
+
     private String mServiceName;
     private String mTag;
     private InputStream mIn;
     private OutputStream mOut;
     private LocalSocket mSocket;
-    private static final int BUFFER_LENGTH = 1024;
-
-    private byte buf[] = new byte[BUFFER_LENGTH];
-    private int buflen = 0;
 
     private boolean connect() {
         if (mSocket != null) {
@@ -104,35 +114,47 @@
         return false;
     }
 
-    private boolean readReply() {
-        int len, ret;
-        buflen = 0;
+    private Reply readReply() {
+        byte buf[] = new byte[4];
+        Reply reply = new Reply();
 
-        if (!readBytes(buf, 2)) return false;
-        ret = (((int) buf[0]) & 0xff) | ((((int) buf[1]) & 0xff) << 8);
-        if (ret != 0) return false;
+        if (!readBytes(buf, 4)) return null;
+        reply.len = (((int) buf[0]) & 0xff) | ((((int) buf[1]) & 0xff) << 8) |
+                ((((int) buf[2]) & 0xff) << 16) |
+                ((((int) buf[3]) & 0xff) << 24);
 
-        if (!readBytes(buf, 2)) return false;
-        len = (((int) buf[0]) & 0xff) | ((((int) buf[1]) & 0xff) << 8);
-        if (len > BUFFER_LENGTH) {
-            Log.e(mTag,"invalid reply length (" + len + ")");
+        if (!readBytes(buf, 4)) return null;
+        reply.returnCode = (((int) buf[0]) & 0xff) |
+                ((((int) buf[1]) & 0xff) << 8) |
+                ((((int) buf[2]) & 0xff) << 16) |
+                ((((int) buf[3]) & 0xff) << 24);
+
+        if (reply.len > BUFFER_LENGTH) {
+            Log.e(mTag,"invalid reply length (" + reply.len + ")");
             disconnect();
-            return false;
+            return null;
         }
-        if (!readBytes(buf, len)) return false;
-        buflen = len;
-        return true;
+        if (!readBytes(reply.data, reply.len)) return null;
+        return reply;
     }
 
-    private boolean writeCommand(String _cmd) {
-        byte[] cmd = _cmd.getBytes();
-        int len = cmd.length;
-        if ((len < 1) || (len > BUFFER_LENGTH)) return false;
+    private boolean writeCommand(int cmd, String _data) {
+        byte buf[] = new byte[8];
+        byte[] data = _data.getBytes();
+        int len = data.length;
+        // the length of data
         buf[0] = (byte) (len & 0xff);
         buf[1] = (byte) ((len >> 8) & 0xff);
+        buf[2] = (byte) ((len >> 16) & 0xff);
+        buf[3] = (byte) ((len >> 24) & 0xff);
+        // the opcode of the command
+        buf[4] = (byte) (cmd & 0xff);
+        buf[5] = (byte) ((cmd >> 8) & 0xff);
+        buf[6] = (byte) ((cmd >> 16) & 0xff);
+        buf[7] = (byte) ((cmd >> 24) & 0xff);
         try {
-            mOut.write(buf, 0, 2);
-            mOut.write(cmd, 0, len);
+            mOut.write(buf, 0, 8);
+            mOut.write(data, 0, len);
         } catch (IOException ex) {
             Log.e(mTag,"write error");
             disconnect();
@@ -141,32 +163,28 @@
         return true;
     }
 
-    private String executeCommand(String cmd) {
-        if (!writeCommand(cmd)) {
+    private Reply executeCommand(int cmd, String data) {
+        if (!writeCommand(cmd, data)) {
             /* If service died and restarted in the background
              * (unlikely but possible) we'll fail on the next
              * write (this one).  Try to reconnect and write
              * the command one more time before giving up.
              */
             Log.e(mTag, "write command failed? reconnect!");
-            if (!connect() || !writeCommand(cmd)) {
+            if (!connect() || !writeCommand(cmd, data)) {
                 return null;
             }
         }
-        if (readReply()) {
-            return new String(buf, 0, buflen);
-        } else {
-            return null;
-        }
+        return readReply();
     }
 
-    public synchronized String execute(String cmd) {
-      String result;
+    public synchronized Reply execute(int cmd, String data) {
+      Reply result;
       if (!connect()) {
           Log.e(mTag, "connection failed");
           return null;
       }
-      result = executeCommand(cmd);
+      result = executeCommand(cmd, data);
       disconnect();
       return result;
     }
diff --git a/keystore/jni/Android.mk b/keystore/jni/Android.mk
new file mode 100644
index 0000000..92c2d6d
--- /dev/null
+++ b/keystore/jni/Android.mk
@@ -0,0 +1,31 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+    cert.c certtool.c
+
+LOCAL_C_INCLUDES += \
+  $(JNI_H_INCLUDE) \
+  external/openssl/include
+
+LOCAL_SHARED_LIBRARIES := \
+  libcutils \
+  libnativehelper \
+  libutils \
+  libcrypto
+
+ifeq ($(TARGET_SIMULATOR),true)
+ifeq ($(TARGET_OS),linux)
+ifeq ($(TARGET_ARCH),x86)
+LOCAL_LDLIBS += -lpthread -ldl -lrt -lssl
+endif
+endif
+endif
+
+ifeq ($(WITH_MALLOC_LEAK_CHECK),true)
+  LOCAL_CFLAGS += -DMALLOC_LEAK_CHECK
+endif
+
+LOCAL_MODULE:= libcerttool_jni
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/keystore/jni/cert.c b/keystore/jni/cert.c
new file mode 100644
index 0000000..07f0e86
--- /dev/null
+++ b/keystore/jni/cert.c
@@ -0,0 +1,249 @@
+/*
+**
+** Copyright 2009, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#define LOG_TAG "CertTool"
+
+#include <stdio.h>
+#include <openssl/engine.h>
+#include <openssl/pem.h>
+#include <openssl/pkcs12.h>
+#include <openssl/rsa.h>
+#include <openssl/x509v3.h>
+#include <cutils/log.h>
+
+#include "cert.h"
+
+static PKEY_STORE pkey_store[KEYGEN_STORE_SIZE];
+static int store_index = 0;
+
+static char emsg[][30] = {
+    "",
+    STR(ERR_INVALID_KEY_LENGTH),
+    STR(ERR_CONSTRUCT_NEW_DATA),
+    STR(ERR_RSA_KEYGEN),
+    STR(ERR_X509_PROCESS),
+    STR(ERR_BIO_READ),
+};
+
+static void save_in_store(X509_REQ *req, EVP_PKEY *pkey)
+{
+    EVP_PKEY *newpkey = EVP_PKEY_new();
+    RSA *rsa = EVP_PKEY_get1_RSA(pkey);
+    EVP_PKEY_set1_RSA(newpkey, rsa);
+    PKEY_STORE_free(pkey_store[store_index]);
+    pkey_store[store_index].key_len =
+    i2d_X509_PUBKEY(req->req_info->pubkey, &pkey_store[store_index].public_key);
+    pkey_store[store_index++].pkey = newpkey;
+    store_index %= KEYGEN_STORE_SIZE;
+    RSA_free(rsa);
+}
+
+static EVP_PKEY *get_pkey_from_store(X509 *cert)
+{
+    int i, key_len;
+    unsigned char *buf = NULL;
+    if ((key_len = i2d_X509_PUBKEY(X509_get_X509_PUBKEY(cert), &buf)) == 0) {
+        return NULL;
+    }
+    for (i = 0 ; i < KEYGEN_STORE_SIZE ; ++i) {
+        if ((key_len == pkey_store[i].key_len) &&
+            memcmp(buf, pkey_store[i].public_key, key_len) == 0) {
+            break;
+        }
+    }
+    free(buf);
+    return (i == KEYGEN_STORE_SIZE) ? NULL : pkey_store[i].pkey;
+}
+
+int gen_csr(int bits, const char *organizations, char reply[REPLY_MAX])
+{
+    int len, ret_code = 0;
+    BIGNUM *bn = NULL;
+    BIO *bio = NULL;
+    EVP_PKEY *pkey = NULL;
+    RSA *rsa = NULL;
+    X509_REQ *req = NULL;
+    X509_NAME *name = NULL;
+
+    if ((bio = BIO_new(BIO_s_mem())) == NULL) goto err;
+
+    if ((bits != KEYLENGTH_MEDIUM) && (bits != KEYLENGTH_MAXIMUM)) {
+        ret_code = ERR_INVALID_KEY_LENGTH;
+        goto err;
+    }
+
+    if (((pkey = EVP_PKEY_new()) == NULL) ||
+        ((req = X509_REQ_new()) == NULL) ||
+        ((rsa = RSA_new()) == NULL) || ((bn = BN_new()) == NULL)) {
+        ret_code = ERR_CONSTRUCT_NEW_DATA;
+        goto err;
+    }
+
+    if (!BN_set_word(bn, RSA_F4) ||
+        !RSA_generate_key_ex(rsa, bits, bn, NULL) ||
+        !EVP_PKEY_assign_RSA(pkey, rsa)) {
+        ret_code = ERR_RSA_KEYGEN;
+        goto err;
+    }
+
+    // rsa will be part of the req, it will be freed in X509_REQ_free(req)
+    rsa = NULL;
+
+    X509_REQ_set_pubkey(req, pkey);
+    name = X509_REQ_get_subject_name(req);
+
+    X509_NAME_add_entry_by_txt(name, "C",  MBSTRING_ASC,
+                               (const unsigned char *)"US", -1, -1, 0);
+    X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC,
+                               (const unsigned char *) ANDROID_KEYSTORE,
+                               -1, -1, 0);
+    X509_NAME_add_entry_by_txt(name, "O", MBSTRING_ASC,
+                               (const unsigned char *)organizations, -1, -1, 0);
+
+    if (!X509_REQ_sign(req, pkey, EVP_md5()) ||
+        (PEM_write_bio_X509_REQ(bio, req) <= 0)) {
+        ret_code = ERR_X509_PROCESS;
+        goto err;
+    }
+    if ((len = BIO_read(bio, reply, REPLY_MAX - 1)) > 0) {
+      reply[len] = 0;
+      save_in_store(req, pkey);
+    } else {
+      ret_code = ERR_BIO_READ;
+    }
+
+err:
+    if (rsa) RSA_free(rsa);
+    if (bn) BN_free(bn);
+    if (req) X509_REQ_free(req);
+    if (pkey) EVP_PKEY_free(pkey);
+    if (bio) BIO_free(bio);
+    if ((ret_code > 0) && (ret_code < ERR_MAXIMUM)) LOGE(emsg[ret_code]);
+    return ret_code;
+}
+
+int is_pkcs12(const char *buf, int bufLen)
+{
+    int ret = 0;
+    BIO *bp = NULL;
+    PKCS12  *p12 = NULL;
+
+    if (!buf || bufLen < 1) 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;
+    }
+err:
+    if (bp) BIO_free(bp);
+    return ret;
+}
+
+X509* parse_cert(const char *buf, int bufLen)
+{
+    X509 *cert = NULL;
+    BIO *bp = NULL;
+
+    if(!buf || bufLen < 1)
+        return NULL;
+
+    bp = BIO_new(BIO_s_mem());
+    if (!bp) goto err;
+
+    if (!BIO_write(bp, buf, bufLen)) goto err;
+
+    cert = PEM_read_bio_X509(bp, NULL, NULL, NULL);
+    if (!cert) {
+        BIO_free(bp);
+        if((bp = BIO_new(BIO_s_mem())) == NULL) goto err;
+
+        if(!BIO_write(bp, (char *) buf, bufLen)) goto err;
+        cert = d2i_X509_bio(bp, NULL);
+   }
+
+err:
+    if (bp) BIO_free(bp);
+    return cert;
+}
+
+static int get_distinct_name(X509_NAME *dname, char *buf, int size)
+{
+   int i, len;
+   char *p, *name;
+
+   if (X509_NAME_oneline(dname, buf, size) == NULL) {
+      return -1;
+   }
+   name = strstr(buf, "/CN=");
+   p = name = name ? (name + 4) : buf;
+   while (*p != 0) {
+       if (*p == ' ') *p = '_';
+       if (*p == '/') {
+          *p = 0;
+          break;
+       }
+       ++p;
+   }
+   return 0;
+}
+
+int get_cert_name(X509 *cert, char *buf, int size)
+{
+   if (!cert) return -1;
+   return get_distinct_name(X509_get_subject_name(cert), buf, size);
+}
+
+int get_issuer_name(X509 *cert, char *buf, int size)
+{
+   if (!cert) return -1;
+   return get_distinct_name(X509_get_issuer_name(cert), buf, size);
+}
+
+int is_ca_cert(X509 *cert)
+{
+    int ret = 0;
+    BASIC_CONSTRAINTS *bs = (BASIC_CONSTRAINTS *)
+            X509_get_ext_d2i(cert, NID_basic_constraints, NULL, NULL);
+    if (bs != NULL) ret = bs->ca;
+    if (bs) BASIC_CONSTRAINTS_free(bs);
+    return ret;
+}
+
+int get_private_key_pem(X509 *cert, char *buf, int size)
+{
+    int len = 0;
+    BIO *bio = NULL;
+    EVP_PKEY *pkey = get_pkey_from_store(cert);
+
+    if (pkey == NULL) return -1;
+
+    bio = BIO_new(BIO_s_mem());
+    if ((bio = BIO_new(BIO_s_mem())) == NULL) goto err;
+    if (!PEM_write_bio_PrivateKey(bio, pkey, NULL,NULL,0,NULL, NULL)) {
+        goto err;
+    }
+    if ((len = BIO_read(bio, buf, size - 1)) > 0) {
+        buf[len] = 0;
+    }
+err:
+    if (bio) BIO_free(bio);
+    return (len == 0) ? -1 : 0;
+}
diff --git a/keystore/jni/cert.h b/keystore/jni/cert.h
new file mode 100644
index 0000000..a9807b1
--- /dev/null
+++ b/keystore/jni/cert.h
@@ -0,0 +1,59 @@
+/*
+**
+** Copyright 2009, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef __CERT_H__
+#define __CERT_H__
+
+#define ANDROID_KEYSTORE "Android Keystore"
+#define KEYGEN_STORE_SIZE     5
+#define KEYLENGTH_MEDIUM      1024
+#define KEYLENGTH_MAXIMUM     2048
+#define MAX_CERT_NAME_LEN     128
+#define MAX_PEM_LENGTH        4096
+#define REPLY_MAX             MAX_PEM_LENGTH
+
+
+#define STR(token) #token
+#define ERR_INVALID_KEY_LENGTH  1
+#define ERR_CONSTRUCT_NEW_DATA  2
+#define ERR_RSA_KEYGEN          3
+#define ERR_X509_PROCESS        4
+#define ERR_BIO_READ            5
+#define ERR_MAXIMUM             6
+
+typedef struct {
+    EVP_PKEY *pkey;
+    unsigned char *public_key;
+    int key_len;
+} PKEY_STORE;
+
+#define PKEY_STORE_free(x) { \
+    if(x.pkey) EVP_PKEY_free(x.pkey); \
+    if(x.public_key) free(x.public_key); \
+}
+
+#define nelem(x) (sizeof (x) / sizeof *(x))
+
+int gen_csr(int bits, const char *organizations, char reply[REPLY_MAX]);
+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);
+int get_issuer_name(X509 *cert, char *buf, int size);
+int is_ca_cert(X509 *cert);
+int get_private_key_pem(X509 *cert, char *buf, int size);
+
+#endif
diff --git a/keystore/jni/certtool.c b/keystore/jni/certtool.c
new file mode 100644
index 0000000..c2a137e
--- /dev/null
+++ b/keystore/jni/certtool.c
@@ -0,0 +1,176 @@
+/*
+**
+** Copyright 2009, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+#define LOG_TAG "CertTool"
+
+#include <string.h>
+#include <jni.h>
+#include <cutils/log.h>
+#include <openssl/x509v3.h>
+
+#include "cert.h"
+
+jstring
+android_security_CertTool_generateCertificateRequest(JNIEnv* env,
+                                                     jobject thiz,
+                                                     jint bits,
+                                                     jstring subject)
+
+{
+    char csr[REPLY_MAX];
+    if (gen_csr(bits, subject, csr) == 0) {
+        return (*env)->NewStringUTF(env, csr);
+    }
+    return NULL;
+}
+
+jboolean
+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;
+    (*env)->GetByteArrayRegion(env, data, 0, len, (jbyte*)buf);
+    return (jboolean) is_pkcs12(buf, len);
+}
+
+jint
+android_security_CertTool_generateX509Certificate(JNIEnv* env,
+                                                  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 (jint) parse_cert(buf, len);
+}
+
+jboolean android_security_CertTool_isCaCertificate(JNIEnv* env,
+                                                   jobject thiz,
+                                                   jint handle)
+{
+    return (handle == 0) ? (jboolean)0 : (jboolean) is_ca_cert((X509*)handle);
+}
+
+jstring android_security_CertTool_getIssuerDN(JNIEnv* env,
+                                              jobject thiz,
+                                              jint handle)
+{
+    char issuer[MAX_CERT_NAME_LEN];
+
+    if (handle == 0) return NULL;
+    if (get_issuer_name((X509*)handle, issuer, MAX_CERT_NAME_LEN)) return NULL;
+    return (*env)->NewStringUTF(env, issuer);
+}
+
+jstring android_security_CertTool_getCertificateDN(JNIEnv* env,
+                                                   jobject thiz,
+                                                   jint handle)
+{
+    char name[MAX_CERT_NAME_LEN];
+    if (handle == 0) return NULL;
+    if (get_cert_name((X509*)handle, name, MAX_CERT_NAME_LEN)) return NULL;
+    return (*env)->NewStringUTF(env, name);
+}
+
+jstring android_security_CertTool_getPrivateKeyPEM(JNIEnv* env,
+                                                   jobject thiz,
+                                                   jint handle)
+{
+    char pem[MAX_PEM_LENGTH];
+    if (handle == 0) return NULL;
+    if (get_private_key_pem((X509*)handle, pem, MAX_PEM_LENGTH)) return NULL;
+    return (*env)->NewStringUTF(env, pem);
+}
+
+void android_security_CertTool_freeX509Certificate(JNIEnv* env,
+                                                   jobject thiz,
+                                                   jint handle)
+{
+    if (handle != 0) X509_free((X509*)handle);
+}
+
+/*
+ * Table of methods associated with the CertTool class.
+ */
+static JNINativeMethod gCertToolMethods[] = {
+    /* name, signature, funcPtr */
+    {"generateCertificateRequest", "(ILjava/lang/String;)Ljava/lang/String;",
+        (void*)android_security_CertTool_generateCertificateRequest},
+    {"isPkcs12Keystore", "(B[)I",
+        (void*)android_security_CertTool_isPkcs12Keystore},
+    {"generateX509Certificate", "(B[)I",
+        (void*)android_security_CertTool_generateX509Certificate},
+    {"isCaCertificate", "(I)Z",
+        (void*)android_security_CertTool_isCaCertificate},
+    {"getIssuerDN", "(I)Ljava/lang/String;",
+        (void*)android_security_CertTool_getIssuerDN},
+    {"getCertificateDN", "(I)Ljava/lang/String;",
+        (void*)android_security_CertTool_getCertificateDN},
+    {"getPrivateKeyPEM", "(I)Ljava/lang/String;",
+        (void*)android_security_CertTool_getPrivateKeyPEM},
+    {"freeX509Certificate", "(I)V",
+        (void*)android_security_CertTool_freeX509Certificate},
+};
+
+/*
+ * Register several native methods for one class.
+ */
+static int registerNatives(JNIEnv* env, const char* className,
+                           JNINativeMethod* gMethods, int numMethods)
+{
+    jclass clazz;
+
+    clazz = (*env)->FindClass(env, className);
+    if (clazz == NULL) {
+        LOGE("Can not find class %s\n", className);
+        return JNI_FALSE;
+    }
+
+    if ((*env)->RegisterNatives(env, clazz, gMethods, numMethods) < 0) {
+        LOGE("Can not RegisterNatives\n");
+        return JNI_FALSE;
+    }
+
+    return JNI_TRUE;
+}
+
+jint JNI_OnLoad(JavaVM* vm, void* reserved)
+{
+    JNIEnv* env = NULL;
+    jint result = -1;
+
+
+    if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_4) != JNI_OK) {
+        goto bail;
+    }
+
+    if (!registerNatives(env, "android/security/CertTool",
+                         gCertToolMethods, nelem(gCertToolMethods))) {
+        goto bail;
+    }
+
+    /* success -- return valid version number */
+    result = JNI_VERSION_1_4;
+
+bail:
+    return result;
+}