Merge change 24132 into eclair
* changes: Add two CertTool helper classes.
This commit is contained in:
@@ -16,6 +16,12 @@
|
|||||||
|
|
||||||
package android.security;
|
package android.security;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.security.Keystore;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.security.cert.Certificate;
|
import java.security.cert.Certificate;
|
||||||
@@ -23,16 +29,11 @@ import java.security.cert.CertificateException;
|
|||||||
import java.security.KeyStoreException;
|
import java.security.KeyStoreException;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
import java.security.UnrecoverableKeyException;
|
import java.security.UnrecoverableKeyException;
|
||||||
|
import java.util.ArrayList;
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.security.Keystore;
|
|
||||||
import android.text.TextUtils;
|
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The CertTool class provides the functions to list the certs/keys,
|
* The CertTool class provides the functions to list the certs/keys,
|
||||||
* generate the certificate request(csr) and store the certificate into
|
* generate the certificate request(csr) and store certificates into
|
||||||
* keystore.
|
* keystore.
|
||||||
*
|
*
|
||||||
* {@hide}
|
* {@hide}
|
||||||
@@ -51,19 +52,129 @@ public class CertTool {
|
|||||||
/** Keystore namespace for user private keys. */
|
/** Keystore namespace for user private keys. */
|
||||||
public static final String USER_KEY = "USRKEY";
|
public static final String USER_KEY = "USRKEY";
|
||||||
|
|
||||||
|
/** Action string for adding certificates to keystore. */
|
||||||
public static final String ACTION_ADD_CREDENTIAL =
|
public static final String ACTION_ADD_CREDENTIAL =
|
||||||
"android.security.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";
|
|
||||||
|
|
||||||
|
/** Action string for installing certificates to keystore from sdcard. */
|
||||||
|
public static final String ACTION_INSTALL_CERT_FROM_SDCARD =
|
||||||
|
"android.security.INSTALL_CERT_FROM_SDCARD";
|
||||||
|
|
||||||
|
/** Dialog title for adding a CA certificate. */
|
||||||
public static final String TITLE_CA_CERT = "CA Certificate";
|
public static final String TITLE_CA_CERT = "CA Certificate";
|
||||||
|
|
||||||
|
/** Dialog title for adding a user certificate. */
|
||||||
public static final String TITLE_USER_CERT = "User Certificate";
|
public static final String TITLE_USER_CERT = "User Certificate";
|
||||||
public static final String TITLE_PKCS12_KEYSTORE = "PKCS12 Keystore";
|
|
||||||
|
/** Dialog title for adding a user private key. */
|
||||||
public static final String TITLE_PRIVATE_KEY = "Private Key";
|
public static final String TITLE_PRIVATE_KEY = "Private Key";
|
||||||
|
|
||||||
|
/** Dialog title for adding a PKCS12 keystore. */
|
||||||
|
public static final String TITLE_PKCS12_KEYSTORE = "PKCS12 Keystore";
|
||||||
|
|
||||||
public static final int INCORRECT_PKCS12_PASSPHRASE = -100;
|
public static final int INCORRECT_PKCS12_PASSPHRASE = -100;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The builder class for building an add-credential-to-keystore intent.
|
||||||
|
*/
|
||||||
|
public static class AddCredentialIntentBuilder {
|
||||||
|
private Intent mIntent;
|
||||||
|
private int mCount;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a builder to build a add-credential-to-keystore intent.
|
||||||
|
*
|
||||||
|
* @param title title of the dialog for adding this credential
|
||||||
|
* @param descriptions description strings to show on the dialog
|
||||||
|
*/
|
||||||
|
public AddCredentialIntentBuilder(String title,
|
||||||
|
String... descriptions) {
|
||||||
|
Intent intent = new Intent(ACTION_ADD_CREDENTIAL);
|
||||||
|
intent.putExtra(KEY_TITLE, title);
|
||||||
|
|
||||||
|
int i = 0;
|
||||||
|
for (String description : descriptions) {
|
||||||
|
intent.putExtra(KEY_DESCRIPTION + (i++), description);
|
||||||
|
}
|
||||||
|
mIntent = intent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds credential data to the intent.
|
||||||
|
*
|
||||||
|
* @param namespace the namespace of the keystore to add the credential
|
||||||
|
* data to
|
||||||
|
* @param data the credential data
|
||||||
|
* @return this builder
|
||||||
|
*/
|
||||||
|
public AddCredentialIntentBuilder addCredential(String namespace,
|
||||||
|
byte[] data) {
|
||||||
|
mIntent.putExtra(KEY_NAMESPACE + mCount, namespace);
|
||||||
|
mIntent.putExtra(KEY_ITEM + mCount, data);
|
||||||
|
mCount++;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns the intent. */
|
||||||
|
public Intent build() {
|
||||||
|
return mIntent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request for adding credential data to keystore.
|
||||||
|
*/
|
||||||
|
public static class AddCredentialRequest {
|
||||||
|
private Intent mIntent;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an add-credential-data-to-keystore request.
|
||||||
|
*
|
||||||
|
* @param intent an add-credential-data-to-keystore intent
|
||||||
|
* @see AddCredentialIntentBuilder
|
||||||
|
*/
|
||||||
|
public AddCredentialRequest(Intent intent) {
|
||||||
|
mIntent = intent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns the dialog title. */
|
||||||
|
public String getTitle() {
|
||||||
|
return mIntent.getStringExtra(KEY_TITLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the i'th credential data.
|
||||||
|
* @return the data or null if not exists
|
||||||
|
*/
|
||||||
|
public byte[] getDataAt(int i) {
|
||||||
|
return mIntent.getByteArrayExtra(KEY_ITEM + i);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the namespace of the i'th credential data.
|
||||||
|
* @return the namespace string or null if missing
|
||||||
|
*/
|
||||||
|
public String getNamespaceAt(int i) {
|
||||||
|
return mIntent.getStringExtra(KEY_NAMESPACE + i);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns the descriptions of the credential data. */
|
||||||
|
public String[] getDescriptions() {
|
||||||
|
ArrayList<String> list = new ArrayList<String>();
|
||||||
|
for (int i = 0; ; i++) {
|
||||||
|
String s = mIntent.getStringExtra(KEY_DESCRIPTION + i);
|
||||||
|
if (s == null) break;
|
||||||
|
list.add(s);
|
||||||
|
}
|
||||||
|
return list.toArray(new String[list.size()]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final String KEY_TITLE = "typeName";
|
||||||
|
private static final String KEY_ITEM = "item";
|
||||||
|
private static final String KEY_NAMESPACE = "namespace";
|
||||||
|
private static final String KEY_DESCRIPTION = "description";
|
||||||
|
|
||||||
private static final String TAG = "CertTool";
|
private static final String TAG = "CertTool";
|
||||||
private static final String UNKNOWN = "Unknown";
|
private static final String UNKNOWN = "Unknown";
|
||||||
private static final String ISSUER_NAME = "Issuer Name:";
|
private static final String ISSUER_NAME = "Issuer Name:";
|
||||||
@@ -86,33 +197,55 @@ public class CertTool {
|
|||||||
private native String getPrivateKeyPEM(int handle);
|
private native String getPrivateKeyPEM(int handle);
|
||||||
private native void freeX509Certificate(int handle);
|
private native void freeX509Certificate(int handle);
|
||||||
|
|
||||||
private static CertTool singleton = null;
|
private static CertTool sSingleton = null;
|
||||||
|
|
||||||
private CertTool() { }
|
private CertTool() { }
|
||||||
|
|
||||||
public static final CertTool getInstance() {
|
public static final CertTool getInstance() {
|
||||||
if (singleton == null) {
|
if (sSingleton == null) {
|
||||||
singleton = new CertTool();
|
sSingleton = new CertTool();
|
||||||
}
|
}
|
||||||
return singleton;
|
return sSingleton;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the full key to retrieve the user private key from the keystore.
|
||||||
|
* @see #getAllUserCertificateKeys()
|
||||||
|
*/
|
||||||
public String getUserPrivateKey(String key) {
|
public String getUserPrivateKey(String key) {
|
||||||
return USER_KEY + KEYNAME_DELIMITER + key;
|
return USER_KEY + KEYNAME_DELIMITER + key;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the full key to retrieve the user certificate from the keystore.
|
||||||
|
* @see #getAllUserCertificateKeys()
|
||||||
|
*/
|
||||||
public String getUserCertificate(String key) {
|
public String getUserCertificate(String key) {
|
||||||
return USER_CERTIFICATE + KEYNAME_DELIMITER + key;
|
return USER_CERTIFICATE + KEYNAME_DELIMITER + key;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the full key to retrieve the CA certificate from the keystore.
|
||||||
|
* @see #getAllCaCertificateKeys()
|
||||||
|
*/
|
||||||
public String getCaCertificate(String key) {
|
public String getCaCertificate(String key) {
|
||||||
return CA_CERTIFICATE + KEYNAME_DELIMITER + key;
|
return CA_CERTIFICATE + KEYNAME_DELIMITER + key;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets all the keys to the user certificates/private keys stored in the
|
||||||
|
* keystore.
|
||||||
|
* @see #getUserCertificate(String)
|
||||||
|
* @see #getUserPrivateKey(String)
|
||||||
|
*/
|
||||||
public String[] getAllUserCertificateKeys() {
|
public String[] getAllUserCertificateKeys() {
|
||||||
return sKeystore.listKeys(USER_KEY);
|
return sKeystore.listKeys(USER_KEY);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets all the keys to the CA certificates stored in the keystore.
|
||||||
|
* @see #getCaCertificate(String)
|
||||||
|
*/
|
||||||
public String[] getAllCaCertificateKeys() {
|
public String[] getAllCaCertificateKeys() {
|
||||||
return sKeystore.listKeys(CA_CERTIFICATE);
|
return sKeystore.listKeys(CA_CERTIFICATE);
|
||||||
}
|
}
|
||||||
@@ -126,29 +259,21 @@ public class CertTool {
|
|||||||
return 1024;
|
return 1024;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates a key pair.
|
||||||
|
*
|
||||||
|
* @param keyStrengthIndex index to the array of supported key strengths;
|
||||||
|
* see {@link #getSupportedKeyStrenghs()}
|
||||||
|
* @param challenge the challenge string for generating the pair
|
||||||
|
* @param dirName (not used)
|
||||||
|
* @return a certificate request from the resulted public key
|
||||||
|
*/
|
||||||
public String generateKeyPair(int keyStrengthIndex, String challenge,
|
public String generateKeyPair(int keyStrengthIndex, String challenge,
|
||||||
String dirName) {
|
String dirName) {
|
||||||
return generateCertificateRequest(getKeyLength(keyStrengthIndex),
|
return generateCertificateRequest(getKeyLength(keyStrengthIndex),
|
||||||
challenge);
|
challenge);
|
||||||
}
|
}
|
||||||
|
|
||||||
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.getBytes());
|
|
||||||
intent.putExtra(KEY_NAMESPACE + "1", namespace);
|
|
||||||
}
|
|
||||||
|
|
||||||
private int extractAndStoreKeysFromPkcs12(int handle, String keyname) {
|
private int extractAndStoreKeysFromPkcs12(int handle, String keyname) {
|
||||||
int ret, i = 0;
|
int ret, i = 0;
|
||||||
String pemData;
|
String pemData;
|
||||||
@@ -171,6 +296,7 @@ public class CertTool {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Adds a PKCS12 keystore to the keystore. */
|
||||||
public int addPkcs12Keystore(byte[] p12Data, String password,
|
public int addPkcs12Keystore(byte[] p12Data, String password,
|
||||||
String keyname) {
|
String keyname) {
|
||||||
int handle, ret;
|
int handle, ret;
|
||||||
@@ -184,27 +310,35 @@ public class CertTool {
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a certificate to the keystore.
|
||||||
|
*
|
||||||
|
* @param data the certificate data
|
||||||
|
* @param context the context to send an add-credential-to-keystore intent
|
||||||
|
*/
|
||||||
public synchronized void addCertificate(byte[] data, Context context) {
|
public synchronized void addCertificate(byte[] data, Context context) {
|
||||||
int handle;
|
int handle;
|
||||||
Intent intent = null;
|
Intent intent = null;
|
||||||
|
|
||||||
Log.i("CertTool", "addCertificate()");
|
Log.i("CertTool", "addCertificate()");
|
||||||
if (isPkcs12Keystore(data)) {
|
if (isPkcs12Keystore(data)) {
|
||||||
intent = prepareIntent(TITLE_PKCS12_KEYSTORE, data, USER_KEY,
|
intent = prepareIntent(TITLE_PKCS12_KEYSTORE, UNKNOWN, UNKNOWN)
|
||||||
UNKNOWN, UNKNOWN);
|
.addCredential(USER_KEY, data).build();
|
||||||
} else if ((handle = generateX509Certificate(data)) != 0) {
|
} else if ((handle = generateX509Certificate(data)) != 0) {
|
||||||
String issuer = getIssuerDN(handle);
|
String issuer = getIssuerDN(handle);
|
||||||
String distinctName = getCertificateDN(handle);
|
String distinctName = getCertificateDN(handle);
|
||||||
String privateKeyPEM = getPrivateKeyPEM(handle);
|
String privateKeyPEM = getPrivateKeyPEM(handle);
|
||||||
if (isCaCertificate(handle)) {
|
if (isCaCertificate(handle)) {
|
||||||
intent = prepareIntent(TITLE_CA_CERT, data, CA_CERTIFICATE,
|
intent = prepareIntent(TITLE_CA_CERT, issuer, distinctName)
|
||||||
issuer, distinctName);
|
.addCredential(CA_CERTIFICATE, data).build();
|
||||||
} else {
|
} else {
|
||||||
intent = prepareIntent(TITLE_USER_CERT, data, USER_CERTIFICATE,
|
AddCredentialIntentBuilder builder =
|
||||||
issuer, distinctName);
|
prepareIntent(TITLE_USER_CERT, issuer, distinctName)
|
||||||
|
.addCredential(USER_CERTIFICATE, data);
|
||||||
if (!TextUtils.isEmpty(privateKeyPEM)) {
|
if (!TextUtils.isEmpty(privateKeyPEM)) {
|
||||||
addExtraIntentInfo(intent, USER_KEY, privateKeyPEM);
|
builder.addCredential(USER_KEY, privateKeyPEM.getBytes());
|
||||||
}
|
}
|
||||||
|
intent = builder.build();
|
||||||
}
|
}
|
||||||
freeX509Certificate(handle);
|
freeX509Certificate(handle);
|
||||||
}
|
}
|
||||||
@@ -214,4 +348,10 @@ public class CertTool {
|
|||||||
Log.w("CertTool", "incorrect data for addCertificate()");
|
Log.w("CertTool", "incorrect data for addCertificate()");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private AddCredentialIntentBuilder prepareIntent(
|
||||||
|
String title, String issuer, String distinctName) {
|
||||||
|
return new AddCredentialIntentBuilder(title, ISSUER_NAME + issuer,
|
||||||
|
DISTINCT_NAME + distinctName);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,8 +22,9 @@ package android.security;
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
public abstract class Keystore {
|
public abstract class Keystore {
|
||||||
private static final String TAG = "Keystore";
|
/** Action to unlock (or initialize) the keystore. */
|
||||||
private static final String[] NOTFOUND = new String[0];
|
public static final String ACTION_UNLOCK_CREDENTIAL_STORAGE =
|
||||||
|
"android.security.UNLOCK_CREDENTIAL_STORAGE";
|
||||||
|
|
||||||
// Keystore States
|
// Keystore States
|
||||||
public static final int BOOTUP = 0;
|
public static final int BOOTUP = 0;
|
||||||
@@ -31,8 +32,9 @@ public abstract class Keystore {
|
|||||||
public static final int LOCKED = 2;
|
public static final int LOCKED = 2;
|
||||||
public static final int UNLOCKED = 3;
|
public static final int UNLOCKED = 3;
|
||||||
|
|
||||||
/**
|
private static final String TAG = "Keystore";
|
||||||
*/
|
private static final String[] NOTFOUND = new String[0];
|
||||||
|
|
||||||
public static Keystore getInstance() {
|
public static Keystore getInstance() {
|
||||||
return new FileKeystore();
|
return new FileKeystore();
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user