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

Commit 82c30ee5 authored by Chung-yih Wang's avatar Chung-yih Wang Committed by The Android Open Source Project
Browse files

am eec11827: Add CertTool for handling the keygen and certificate download.

Merge commit 'eec11827'

* commit 'eec11827':
  Add CertTool for handling the keygen and certificate download.
parents 7d9e45b2 eec11827
Loading
Loading
Loading
Loading
+143 −0
Original line number Original line Diff line number Diff line
/*
 * 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);
    }
}
+92 −106
Original line number Original line Diff line number Diff line
@@ -20,6 +20,7 @@ package android.security;
 * The Keystore class provides the functions to list the certs/keys in keystore.
 * The Keystore class provides the functions to list the certs/keys in keystore.
 * {@hide}
 * {@hide}
 */
 */

public abstract class Keystore {
public abstract class Keystore {
    private static final String TAG = "Keystore";
    private static final String TAG = "Keystore";
    private static final String[] NOTFOUND = new String[0];
    private static final String[] NOTFOUND = new String[0];
@@ -30,25 +31,18 @@ public abstract class Keystore {
        return new FileKeystore();
        return new FileKeystore();
    }
    }


    // for compatiblity, start from here
    public abstract int lock();
    /**
    public abstract int unlock(String password);
     */
    public abstract int getState();
    public abstract String getUserkey(String key);
    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 getCertificate(String key);
    public abstract String get(String namespace, String keyname);

    public abstract int remove(String namespace, String keyname);
    /**
    public abstract int reset();
     */
    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);
    public abstract String getCaCertificate(String key);
@@ -89,101 +83,41 @@ public abstract class Keystore {
            int keyStrengthIndex, String challenge, String organizations);
            int keyStrengthIndex, String challenge, String organizations);


    public abstract void addCertificate(byte[] cert);
    public abstract void addCertificate(byte[] cert);
    // to here


    private static class FileKeystore extends Keystore {
    private static class FileKeystore extends Keystore {
        private static final String SERVICE_NAME = "keystore";
        private static final String SERVICE_NAME = "keystore";
        private static final String LIST_CA_CERTIFICATES = "listcacerts";
        private static final String CA_CERTIFICATE = "CaCertificate";
        private static final String LIST_USER_CERTIFICATES = "listusercerts";
        private static final String USER_CERTIFICATE = "UserCertificate";
        private static final String GET_CA_CERTIFICATE = "getcacert";
        private static final String USER_KEY = "UserPrivateKey";
        private static final String GET_USER_CERTIFICATE = "getusercert";
        private static final String COMMAND_DELIMITER = " ";
        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 ServiceCommand mServiceCommand =
        private static final ServiceCommand mServiceCommand =
                new ServiceCommand(SERVICE_NAME);
                new ServiceCommand(SERVICE_NAME);


        // for compatiblity, start from here
        // TODO: for migrating to the mini-keystore, 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

        @Override
        @Override
        public String getUserPrivateKey(String key) {
        public String getUserPrivateKey(String key) {
            return mServiceCommand.execute(
            return "";
                    GET_USER_KEY + COMMAND_DELIMITER + key);
        }
        }


        @Override
        @Override
        public String getUserCertificate(String key) {
        public String getUserCertificate(String key) {
            return mServiceCommand.execute(
            return "";
                    GET_USER_CERTIFICATE + COMMAND_DELIMITER + key);
        }
        }


        @Override
        @Override
        public String getCaCertificate(String key) {
        public String getCaCertificate(String key) {
            return mServiceCommand.execute(
            return "";
                    GET_CA_CERTIFICATE + COMMAND_DELIMITER + key);
        }
        }


        @Override
        @Override
        public String[] getAllUserCertificateKeys() {
        public String[] getAllUserCertificateKeys() {
            try {
            return new String[0];
                String result = mServiceCommand.execute(LIST_USER_CERTIFICATES);
                if (result != null) return result.split("\\s+");
                return NOTFOUND;
            } catch (NumberFormatException ex) {
                return NOTFOUND;
            }
        }
        }


        @Override
        @Override
        public String[] getAllCaCertificateKeys() {
        public String[] getAllCaCertificateKeys() {
            try {
          return new String[0];
                String result = mServiceCommand.execute(LIST_CA_CERTIFICATES);
                if (result != null) return result.split("\\s+");
                return NOTFOUND;
            } catch (NumberFormatException ex) {
                return NOTFOUND;
            }
        }
        }


        @Override
        @Override
@@ -221,25 +155,77 @@ public abstract class Keystore {
            // TODO: real implementation
            // TODO: real implementation
        }
        }


        private boolean addUserCertificate(String key, String certificate,
        // to here
                String privateKey) {

            if(mServiceCommand.execute(ADD_USER_CERTIFICATE + COMMAND_DELIMITER
        @Override
                    + key + COMMAND_DELIMITER + certificate) != null) {
        public int lock() {
                if (mServiceCommand.execute(ADD_USER_KEY + COMMAND_DELIMITER
            Reply result = mServiceCommand.execute(ServiceCommand.LOCK, null);
                        + key + COMMAND_DELIMITER + privateKey) != null) {
            return (result != null) ? result.returnCode : -1;
                    return true;
        }
        }

        @Override
        public int unlock(String password) {
            Reply result = mServiceCommand.execute(ServiceCommand.UNLOCK,
                    password);
            return (result != null) ? result.returnCode : -1;
        }
        }
            return false;

        @Override
        public int getState() {
            Reply result = mServiceCommand.execute(ServiceCommand.GET_STATE,
                    null);
            return (result != null) ? result.returnCode : -1;
        }
        }


        private boolean addCaCertificate(String key, String content) {
        @Override
            if (mServiceCommand.execute(ADD_CA_CERTIFICATE + COMMAND_DELIMITER
        public int changePassword(String oldPassword, String newPassword) {
                    + key + COMMAND_DELIMITER + content) != null) {
            Reply result = mServiceCommand.execute(ServiceCommand.PASSWD,
                return true;
                    oldPassword + " " + newPassword);
            return (result != null) ? result.returnCode : -1;
        }
        }
            return false;

        @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;
        }
    }
    }
}
}
+26 −0
Original line number Original line Diff line number Diff line
/*
 * 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];
}
+53 −35
Original line number Original line Diff line number Diff line
@@ -35,15 +35,25 @@ public class ServiceCommand {
    public static final String SUCCESS = "0";
    public static final String SUCCESS = "0";
    public static final String FAILED = "-1";
    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 mServiceName;
    private String mTag;
    private String mTag;
    private InputStream mIn;
    private InputStream mIn;
    private OutputStream mOut;
    private OutputStream mOut;
    private LocalSocket mSocket;
    private LocalSocket mSocket;
    private static final int BUFFER_LENGTH = 1024;

    private byte buf[] = new byte[BUFFER_LENGTH];
    private int buflen = 0;


    private boolean connect() {
    private boolean connect() {
        if (mSocket != null) {
        if (mSocket != null) {
@@ -104,35 +114,47 @@ public class ServiceCommand {
        return false;
        return false;
    }
    }


    private boolean readReply() {
    private Reply readReply() {
        int len, ret;
        byte buf[] = new byte[4];
        buflen = 0;
        Reply reply = new Reply();


        if (!readBytes(buf, 2)) return false;
        if (!readBytes(buf, 4)) return null;
        ret = (((int) buf[0]) & 0xff) | ((((int) buf[1]) & 0xff) << 8);
        reply.len = (((int) buf[0]) & 0xff) | ((((int) buf[1]) & 0xff) << 8) |
        if (ret != 0) return false;
                ((((int) buf[2]) & 0xff) << 16) |
                ((((int) buf[3]) & 0xff) << 24);


        if (!readBytes(buf, 2)) return false;
        if (!readBytes(buf, 4)) return null;
        len = (((int) buf[0]) & 0xff) | ((((int) buf[1]) & 0xff) << 8);
        reply.returnCode = (((int) buf[0]) & 0xff) |
        if (len > BUFFER_LENGTH) {
                ((((int) buf[1]) & 0xff) << 8) |
            Log.e(mTag,"invalid reply length (" + len + ")");
                ((((int) buf[2]) & 0xff) << 16) |
                ((((int) buf[3]) & 0xff) << 24);

        if (reply.len > BUFFER_LENGTH) {
            Log.e(mTag,"invalid reply length (" + reply.len + ")");
            disconnect();
            disconnect();
            return false;
            return null;
        }
        }
        if (!readBytes(buf, len)) return false;
        if (!readBytes(reply.data, reply.len)) return null;
        buflen = len;
        return reply;
        return true;
    }
    }


    private boolean writeCommand(String _cmd) {
    private boolean writeCommand(int cmd, String _data) {
        byte[] cmd = _cmd.getBytes();
        byte buf[] = new byte[8];
        int len = cmd.length;
        byte[] data = _data.getBytes();
        if ((len < 1) || (len > BUFFER_LENGTH)) return false;
        int len = data.length;
        // the length of data
        buf[0] = (byte) (len & 0xff);
        buf[0] = (byte) (len & 0xff);
        buf[1] = (byte) ((len >> 8) & 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 {
        try {
            mOut.write(buf, 0, 2);
            mOut.write(buf, 0, 8);
            mOut.write(cmd, 0, len);
            mOut.write(data, 0, len);
        } catch (IOException ex) {
        } catch (IOException ex) {
            Log.e(mTag,"write error");
            Log.e(mTag,"write error");
            disconnect();
            disconnect();
@@ -141,32 +163,28 @@ public class ServiceCommand {
        return true;
        return true;
    }
    }


    private String executeCommand(String cmd) {
    private Reply executeCommand(int cmd, String data) {
        if (!writeCommand(cmd)) {
        if (!writeCommand(cmd, data)) {
            /* If service died and restarted in the background
            /* If service died and restarted in the background
             * (unlikely but possible) we'll fail on the next
             * (unlikely but possible) we'll fail on the next
             * write (this one).  Try to reconnect and write
             * write (this one).  Try to reconnect and write
             * the command one more time before giving up.
             * the command one more time before giving up.
             */
             */
            Log.e(mTag, "write command failed? reconnect!");
            Log.e(mTag, "write command failed? reconnect!");
            if (!connect() || !writeCommand(cmd)) {
            if (!connect() || !writeCommand(cmd, data)) {
                return null;
                return null;
            }
            }
        }
        }
        if (readReply()) {
        return readReply();
            return new String(buf, 0, buflen);
        } else {
            return null;
        }
    }
    }


    public synchronized String execute(String cmd) {
    public synchronized Reply execute(int cmd, String data) {
      String result;
      Reply result;
      if (!connect()) {
      if (!connect()) {
          Log.e(mTag, "connection failed");
          Log.e(mTag, "connection failed");
          return null;
          return null;
      }
      }
      result = executeCommand(cmd);
      result = executeCommand(cmd, data);
      disconnect();
      disconnect();
      return result;
      return result;
    }
    }
+31 −0
Original line number Original line Diff line number Diff line
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)
Loading