summaryrefslogtreecommitdiffstats
path: root/keystore
diff options
context:
space:
mode:
authorChung-yih Wang <cywang@google.com>2009-07-02 00:22:04 +0800
committerChung-yih Wang <cywang@google.com>2009-07-02 18:56:12 +0800
commiteec11827a6c06b029030f43c8d54fd871cc3347d (patch)
tree6ed138af9f9d632cc6e74dbb517626adb802ac96 /keystore
parentd45dcbec856710f9478ffc5689e7cdf95d757ab8 (diff)
downloadframeworks_base-eec11827a6c06b029030f43c8d54fd871cc3347d.zip
frameworks_base-eec11827a6c06b029030f43c8d54fd871cc3347d.tar.gz
frameworks_base-eec11827a6c06b029030f43c8d54fd871cc3347d.tar.bz2
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.
Diffstat (limited to 'keystore')
-rw-r--r--keystore/java/android/security/CertTool.java143
-rw-r--r--keystore/java/android/security/Keystore.java198
-rw-r--r--keystore/java/android/security/Reply.java26
-rw-r--r--keystore/java/android/security/ServiceCommand.java88
-rw-r--r--keystore/jni/Android.mk31
-rw-r--r--keystore/jni/cert.c249
-rw-r--r--keystore/jni/cert.h59
-rw-r--r--keystore/jni/certtool.c176
8 files changed, 829 insertions, 141 deletions
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..462645a 100644
--- a/keystore/java/android/security/Keystore.java
+++ b/keystore/java/android/security/Keystore.java
@@ -20,6 +20,7 @@ package android.security;
* 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 @@ public abstract class Keystore {
return new FileKeystore();
}
- // for compatiblity, start from here
- /**
- */
- public abstract String getUserkey(String key);
-
- /**
- */
- public abstract String getCertificate(String key);
-
- /**
- */
- public abstract String[] getAllCertificateKeys();
-
- /**
- */
- public abstract String[] getAllUserkeyKeys();
-
- // to here
-
+ 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();
+
+ // TODO: for migrating to the mini-keystore, clean up from here
/**
*/
public abstract String getCaCertificate(String key);
@@ -89,101 +83,41 @@ public abstract class Keystore {
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 @@ public abstract class Keystore {
// 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;
+ }
+
+ @Override
+ public int unlock(String password) {
+ Reply result = mServiceCommand.execute(ServiceCommand.UNLOCK,
+ password);
+ 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 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 class ServiceCommand {
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 @@ public class ServiceCommand {
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 @@ public class ServiceCommand {
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;
+}