diff options
25 files changed, 884 insertions, 198 deletions
diff --git a/api/current.txt b/api/current.txt index e46dbeb..7462415 100644 --- a/api/current.txt +++ b/api/current.txt @@ -15916,7 +15916,7 @@ package android.os { method public static void startMethodTracing(java.lang.String, int); method public static void startMethodTracing(java.lang.String, int, int); method public static void startNativeTracing(); - method public static void stopAllocCounting(); + method public static deprecated void stopAllocCounting(); method public static void stopMethodTracing(); method public static void stopNativeTracing(); method public static long threadCpuTimeNanos(); diff --git a/cmds/app_process/app_main.cpp b/cmds/app_process/app_main.cpp index 0668be6..6f37180 100644 --- a/cmds/app_process/app_main.cpp +++ b/cmds/app_process/app_main.cpp @@ -7,6 +7,7 @@ #define LOG_TAG "appproc" +#include <cutils/properties.h> #include <binder/IPCThreadState.h> #include <binder/ProcessState.h> #include <utils/Log.h> @@ -144,7 +145,10 @@ int main(int argc, char* const argv[]) * This breaks some programs which improperly embed * an out of date copy of Android's linker. */ - if (getenv("NO_ADDR_COMPAT_LAYOUT_FIXUP") == NULL) { + char value[PROPERTY_VALUE_MAX]; + property_get("ro.kernel.qemu", value, ""); + bool is_qemu = (strcmp(value, "1") == 0); + if ((getenv("NO_ADDR_COMPAT_LAYOUT_FIXUP") == NULL) && !is_qemu) { int current = personality(0xFFFFFFFF); if ((current & ADDR_COMPAT_LAYOUT) == 0) { personality(current | ADDR_COMPAT_LAYOUT); diff --git a/cmds/input/src/com/android/commands/input/Input.java b/cmds/input/src/com/android/commands/input/Input.java index a21df0d..6dc9f3d 100644 --- a/cmds/input/src/com/android/commands/input/Input.java +++ b/cmds/input/src/com/android/commands/input/Input.java @@ -56,12 +56,14 @@ public class Input { return; } } else if (command.equals("keyevent")) { - if (args.length == 2) { - int keyCode = KeyEvent.keyCodeFromString(args[1]); - if (keyCode == KeyEvent.KEYCODE_UNKNOWN) { - keyCode = KeyEvent.keyCodeFromString("KEYCODE_" + args[1]); + if (args.length >= 2) { + for (int i=1; i < args.length; i++) { + int keyCode = KeyEvent.keyCodeFromString(args[i]); + if (keyCode == KeyEvent.KEYCODE_UNKNOWN) { + keyCode = KeyEvent.keyCodeFromString("KEYCODE_" + args[i]); + } + sendKeyEvent(keyCode); } - sendKeyEvent(keyCode); return; } } else if (command.equals("tap")) { @@ -223,7 +225,7 @@ public class Input { DEFAULT_META_STATE, DEFAULT_PRECISION_X, DEFAULT_PRECISION_Y, DEFAULT_DEVICE_ID, DEFAULT_EDGE_FLAGS); event.setSource(inputSource); - Log.i("Input", "injectMotionEvent: " + event); + Log.i(TAG, "injectMotionEvent: " + event); InputManager.getInstance().injectInputEvent(event, InputManager.INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH); } @@ -235,7 +237,7 @@ public class Input { private void showUsage() { System.err.println("usage: input ..."); System.err.println(" input text <string>"); - System.err.println(" input keyevent <key code number or name>"); + System.err.println(" input keyevent <key code number or name> ..."); System.err.println(" input [touchscreen|touchpad] tap <x> <y>"); System.err.println(" input [touchscreen|touchpad] swipe <x1> <y1> <x2> <y2>"); System.err.println(" input trackball press"); diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java index 6f1cc94..d6b90c3 100644 --- a/core/java/android/inputmethodservice/InputMethodService.java +++ b/core/java/android/inputmethodservice/InputMethodService.java @@ -424,7 +424,7 @@ public class InputMethodService extends AbstractInputMethodService { showWindow(true); } // If user uses hard keyboard, IME button should always be shown. - boolean showing = onEvaluateInputViewShown(); + boolean showing = isInputViewShown(); mImm.setImeWindowStatus(mToken, IME_ACTIVE | (showing ? IME_VISIBLE : 0), mBackDisposition); if (resultReceiver != null) { diff --git a/core/java/android/net/http/CertificateChainValidator.java b/core/java/android/net/http/CertificateChainValidator.java index f66075d..155ab93 100644 --- a/core/java/android/net/http/CertificateChainValidator.java +++ b/core/java/android/net/http/CertificateChainValidator.java @@ -17,18 +17,19 @@ package android.net.http; +import java.io.ByteArrayInputStream; import java.io.IOException; import java.security.GeneralSecurityException; import java.security.KeyManagementException; import java.security.cert.Certificate; import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; import javax.net.ssl.DefaultHostnameVerifier; import javax.net.ssl.SSLHandshakeException; import javax.net.ssl.SSLSession; import javax.net.ssl.SSLSocket; import javax.net.ssl.X509TrustManager; -import org.apache.harmony.security.provider.cert.X509CertImpl; import org.apache.harmony.xnet.provider.jsse.SSLParametersImpl; import org.apache.harmony.xnet.provider.jsse.TrustManagerImpl; @@ -118,8 +119,14 @@ public class CertificateChainValidator { X509Certificate[] serverCertificates = new X509Certificate[certChain.length]; - for (int i = 0; i < certChain.length; ++i) { - serverCertificates[i] = new X509CertImpl(certChain[i]); + try { + CertificateFactory cf = CertificateFactory.getInstance("X.509"); + for (int i = 0; i < certChain.length; ++i) { + serverCertificates[i] = (X509Certificate) cf.generateCertificate( + new ByteArrayInputStream(certChain[i])); + } + } catch (CertificateException e) { + throw new IOException("can't read certificate", e); } return verifyServerDomainAndCertificates(serverCertificates, domain, authType); diff --git a/core/java/android/os/Debug.java b/core/java/android/os/Debug.java index 2e77237..fd01da9 100644 --- a/core/java/android/os/Debug.java +++ b/core/java/android/os/Debug.java @@ -577,6 +577,7 @@ href="{@docRoot}guide/developing/tools/traceview.html">Traceview: A Graphical Lo * * @see #startAllocCounting() */ + @Deprecated public static void stopAllocCounting() { VMDebug.stopAllocCounting(); } diff --git a/core/java/android/security/IKeystoreService.java b/core/java/android/security/IKeystoreService.java index e1cc90e..3d75dc8 100644 --- a/core/java/android/security/IKeystoreService.java +++ b/core/java/android/security/IKeystoreService.java @@ -78,7 +78,7 @@ public interface IKeystoreService extends IInterface { return _result; } - public int insert(String name, byte[] item, int uid) throws RemoteException { + public int insert(String name, byte[] item, int uid, int flags) throws RemoteException { Parcel _data = Parcel.obtain(); Parcel _reply = Parcel.obtain(); int _result; @@ -87,6 +87,7 @@ public interface IKeystoreService extends IInterface { _data.writeString(name); _data.writeByteArray(item); _data.writeInt(uid); + _data.writeInt(flags); mRemote.transact(Stub.TRANSACTION_insert, _data, _reply, 0); _reply.readException(); _result = _reply.readInt(); @@ -243,7 +244,7 @@ public interface IKeystoreService extends IInterface { return _result; } - public int generate(String name, int uid) throws RemoteException { + public int generate(String name, int uid, int flags) throws RemoteException { Parcel _data = Parcel.obtain(); Parcel _reply = Parcel.obtain(); int _result; @@ -251,6 +252,7 @@ public interface IKeystoreService extends IInterface { _data.writeInterfaceToken(DESCRIPTOR); _data.writeString(name); _data.writeInt(uid); + _data.writeInt(flags); mRemote.transact(Stub.TRANSACTION_generate, _data, _reply, 0); _reply.readException(); _result = _reply.readInt(); @@ -261,7 +263,8 @@ public interface IKeystoreService extends IInterface { return _result; } - public int import_key(String name, byte[] data, int uid) throws RemoteException { + public int import_key(String name, byte[] data, int uid, int flags) + throws RemoteException { Parcel _data = Parcel.obtain(); Parcel _reply = Parcel.obtain(); int _result; @@ -270,6 +273,7 @@ public interface IKeystoreService extends IInterface { _data.writeString(name); _data.writeByteArray(data); _data.writeInt(uid); + _data.writeInt(flags); mRemote.transact(Stub.TRANSACTION_import, _data, _reply, 0); _reply.readException(); _result = _reply.readInt(); @@ -538,7 +542,7 @@ public interface IKeystoreService extends IInterface { public byte[] get(String name) throws RemoteException; - public int insert(String name, byte[] item, int uid) throws RemoteException; + public int insert(String name, byte[] item, int uid, int flags) throws RemoteException; public int del(String name, int uid) throws RemoteException; @@ -556,9 +560,9 @@ public interface IKeystoreService extends IInterface { public int zero() throws RemoteException; - public int generate(String name, int uid) throws RemoteException; + public int generate(String name, int uid, int flags) throws RemoteException; - public int import_key(String name, byte[] data, int uid) throws RemoteException; + public int import_key(String name, byte[] data, int uid, int flags) throws RemoteException; public byte[] sign(String name, byte[] data) throws RemoteException; diff --git a/core/java/android/util/TimeUtils.java b/core/java/android/util/TimeUtils.java index 5a4f322..33964a0 100644 --- a/core/java/android/util/TimeUtils.java +++ b/core/java/android/util/TimeUtils.java @@ -235,7 +235,7 @@ public class TimeUtils { * during the lifetime of an activity. */ public static String getTimeZoneDatabaseVersion() { - return ZoneInfoDB.getVersion(); + return ZoneInfoDB.getInstance().getVersion(); } /** @hide Field length that can hold 999 days of time */ diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java index c2a3e58..4649a8b 100644 --- a/core/java/android/view/KeyEvent.java +++ b/core/java/android/view/KeyEvent.java @@ -628,8 +628,8 @@ public class KeyEvent extends InputEvent implements Parcelable { // NOTE: If you add a new keycode here you must also add it to: // isSystem() - // native/include/android/keycodes.h - // frameworks/base/include/ui/KeycodeLabels.h + // frameworks/native/include/android/keycodes.h + // frameworks/base/include/androidfw/KeycodeLabels.h // external/webkit/WebKit/android/plugins/ANPKeyCodes.h // frameworks/base/core/res/res/values/attrs.xml // emulator? diff --git a/core/java/android/webkit/BrowserFrame.java b/core/java/android/webkit/BrowserFrame.java index 023dfa8..e627af1 100644 --- a/core/java/android/webkit/BrowserFrame.java +++ b/core/java/android/webkit/BrowserFrame.java @@ -40,13 +40,13 @@ import android.view.WindowManager; import junit.framework.Assert; +import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.lang.ref.WeakReference; import java.net.URLEncoder; -import java.nio.charset.Charsets; import java.security.PrivateKey; -import java.security.cert.CertificateEncodingException; +import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.HashMap; @@ -55,7 +55,6 @@ import java.util.Iterator; import java.util.Map; import java.util.Set; -import org.apache.harmony.security.provider.cert.X509CertImpl; import org.apache.harmony.xnet.provider.jsse.OpenSSLKey; import org.apache.harmony.xnet.provider.jsse.OpenSSLKeyHolder; @@ -1079,10 +1078,12 @@ class BrowserFrame extends Handler { String url) { final SslError sslError; try { - X509Certificate cert = new X509CertImpl(certDER); + CertificateFactory cf = CertificateFactory.getInstance("X.509"); + X509Certificate cert = (X509Certificate) cf.generateCertificate( + new ByteArrayInputStream(certDER)); SslCertificate sslCert = new SslCertificate(cert); sslError = SslError.SslErrorFromChromiumErrorCode(certError, sslCert, url); - } catch (IOException e) { + } catch (Exception e) { // Can't get the certificate, not much to do. Log.e(LOGTAG, "Can't get the certificate from WebKit, canceling"); nativeSslCertErrorCancel(handle, certError); @@ -1200,9 +1201,11 @@ class BrowserFrame extends Handler { */ private void setCertificate(byte cert_der[]) { try { - X509Certificate cert = new X509CertImpl(cert_der); + CertificateFactory cf = CertificateFactory.getInstance("X.509"); + X509Certificate cert = (X509Certificate) cf.generateCertificate( + new ByteArrayInputStream(cert_der)); mCallbackProxy.onReceivedCertificate(new SslCertificate(cert)); - } catch (IOException e) { + } catch (Exception e) { // Can't get the certificate, not much to do. Log.e(LOGTAG, "Can't get the certificate from WebKit, canceling"); return; diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java index 9e43749..4ac16d8 100644 --- a/core/java/com/android/internal/os/ZygoteInit.java +++ b/core/java/com/android/internal/os/ZygoteInit.java @@ -35,6 +35,7 @@ import dalvik.system.Zygote; import libcore.io.IoUtils; import libcore.io.Libcore; +import libcore.io.OsConstants; import java.io.BufferedReader; import java.io.FileDescriptor; @@ -479,12 +480,25 @@ public class ZygoteInit { */ private static boolean startSystemServer() throws MethodAndArgsCaller, RuntimeException { + long capabilities = posixCapabilitiesAsBits( + OsConstants.CAP_KILL, + OsConstants.CAP_NET_ADMIN, + OsConstants.CAP_NET_BIND_SERVICE, + OsConstants.CAP_NET_BROADCAST, + OsConstants.CAP_NET_RAW, + OsConstants.CAP_SYS_BOOT, + OsConstants.CAP_SYS_MODULE, + OsConstants.CAP_SYS_NICE, + OsConstants.CAP_SYS_RESOURCE, + OsConstants.CAP_SYS_TIME, + OsConstants.CAP_SYS_TTY_CONFIG + ); /* Hardcoded command line to start the system server */ String args[] = { "--setuid=1000", "--setgid=1000", "--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,1018,3001,3002,3003,3006,3007", - "--capabilities=130104352,130104352", + "--capabilities=" + capabilities + "," + capabilities, "--runtime-init", "--nice-name=system_server", "com.android.server.SystemServer", @@ -518,6 +532,20 @@ public class ZygoteInit { return true; } + /** + * Gets the bit array representation of the provided list of POSIX capabilities. + */ + private static long posixCapabilitiesAsBits(int... capabilities) { + long result = 0; + for (int capability : capabilities) { + if ((capability < 0) || (capability > OsConstants.CAP_LAST_CAP)) { + throw new IllegalArgumentException(String.valueOf(capability)); + } + result |= (1L << capability); + } + return result; + } + public static void main(String argv[]) { try { // Start profiling the zygote initialization. diff --git a/docs/html/guide/topics/data/backup.jd b/docs/html/guide/topics/data/backup.jd index 598b08a..4903852 100644 --- a/docs/html/guide/topics/data/backup.jd +++ b/docs/html/guide/topics/data/backup.jd @@ -680,7 +680,7 @@ android.app.backup.BackupAgentHelper} looks like this:</p> <pre> public class MyFileBackupAgent extends BackupAgentHelper { - // The name of the SharedPreferences file + // The name of the file static final String TOP_SCORES = "scores"; static final String PLAYER_STATS = "stats"; diff --git a/keystore/java/android/security/AndroidKeyPairGenerator.java b/keystore/java/android/security/AndroidKeyPairGenerator.java index c42001b..6975583 100644 --- a/keystore/java/android/security/AndroidKeyPairGenerator.java +++ b/keystore/java/android/security/AndroidKeyPairGenerator.java @@ -49,10 +49,7 @@ import java.security.spec.X509EncodedKeySpec; * * {@hide} */ -@SuppressWarnings("deprecation") public class AndroidKeyPairGenerator extends KeyPairGeneratorSpi { - public static final String NAME = "AndroidKeyPairGenerator"; - private android.security.KeyStore mKeyStore; private AndroidKeyPairGeneratorSpec mSpec; @@ -79,12 +76,21 @@ public class AndroidKeyPairGenerator extends KeyPairGeneratorSpi { "Must call initialize with an AndroidKeyPairGeneratorSpec first"); } + if (((mSpec.getFlags() & KeyStore.FLAG_ENCRYPTED) != 0) + && (mKeyStore.state() != KeyStore.State.UNLOCKED)) { + throw new IllegalStateException( + "Android keystore must be in initialized and unlocked state " + + "if encryption is required"); + } + final String alias = mSpec.getKeystoreAlias(); Credentials.deleteAllTypesForAlias(mKeyStore, alias); final String privateKeyAlias = Credentials.USER_PRIVATE_KEY + alias; - mKeyStore.generate(privateKeyAlias); + if (!mKeyStore.generate(privateKeyAlias, KeyStore.UID_SELF, mSpec.getFlags())) { + throw new IllegalStateException("could not generate key in keystore"); + } final PrivateKey privKey; final OpenSSLEngine engine = OpenSSLEngine.getInstance("keystore"); @@ -131,7 +137,8 @@ public class AndroidKeyPairGenerator extends KeyPairGeneratorSpi { throw new IllegalStateException("Can't get encoding of certificate", e); } - if (!mKeyStore.put(Credentials.USER_CERTIFICATE + alias, certBytes)) { + if (!mKeyStore.put(Credentials.USER_CERTIFICATE + alias, certBytes, KeyStore.UID_SELF, + mSpec.getFlags())) { Credentials.deleteAllTypesForAlias(mKeyStore, alias); throw new IllegalStateException("Can't store certificate in AndroidKeyStore"); } diff --git a/keystore/java/android/security/AndroidKeyPairGeneratorSpec.java b/keystore/java/android/security/AndroidKeyPairGeneratorSpec.java index 83faf35..8d6bfa6 100644 --- a/keystore/java/android/security/AndroidKeyPairGeneratorSpec.java +++ b/keystore/java/android/security/AndroidKeyPairGeneratorSpec.java @@ -32,10 +32,9 @@ import javax.security.auth.x500.X500Principal; * {@code KeyPairGenerator} that works with <a href="{@docRoot} * guide/topics/security/keystore.html">Android KeyStore facility</a>. The * Android KeyStore facility is accessed through a - * {@link java.security.KeyPairGenerator} API using the - * {@code AndroidKeyPairGenerator} provider. The {@code context} passed in may - * be used to pop up some UI to ask the user to unlock or initialize the Android - * keystore facility. + * {@link java.security.KeyPairGenerator} API using the {@code AndroidKeyStore} + * provider. The {@code context} passed in may be used to pop up some UI to ask + * the user to unlock or initialize the Android KeyStore facility. * <p> * After generation, the {@code keyStoreAlias} is used with the * {@link java.security.KeyStore#getEntry(String, java.security.KeyStore.ProtectionParameter)} @@ -47,12 +46,12 @@ import javax.security.auth.x500.X500Principal; * Distinguished Name along with the other parameters specified with the * {@link Builder}. * <p> - * The self-signed certificate may be replaced at a later time by a certificate - * signed by a real Certificate Authority. + * The self-signed X.509 certificate may be replaced at a later time by a + * certificate signed by a real Certificate Authority. * * @hide */ -public class AndroidKeyPairGeneratorSpec implements AlgorithmParameterSpec { +public final class AndroidKeyPairGeneratorSpec implements AlgorithmParameterSpec { private final String mKeystoreAlias; private final Context mContext; @@ -65,6 +64,8 @@ public class AndroidKeyPairGeneratorSpec implements AlgorithmParameterSpec { private final Date mEndDate; + private final int mFlags; + /** * Parameter specification for the "{@code AndroidKeyPairGenerator}" * instance of the {@link java.security.KeyPairGenerator} API. The @@ -95,7 +96,8 @@ public class AndroidKeyPairGeneratorSpec implements AlgorithmParameterSpec { * @hide should be built with AndroidKeyPairGeneratorSpecBuilder */ public AndroidKeyPairGeneratorSpec(Context context, String keyStoreAlias, - X500Principal subjectDN, BigInteger serialNumber, Date startDate, Date endDate) { + X500Principal subjectDN, BigInteger serialNumber, Date startDate, Date endDate, + int flags) { if (context == null) { throw new IllegalArgumentException("context == null"); } else if (TextUtils.isEmpty(keyStoreAlias)) { @@ -118,51 +120,72 @@ public class AndroidKeyPairGeneratorSpec implements AlgorithmParameterSpec { mSerialNumber = serialNumber; mStartDate = startDate; mEndDate = endDate; + mFlags = flags; } /** - * @hide + * Returns the alias that will be used in the {@code java.security.KeyStore} + * in conjunction with the {@code AndroidKeyStore}. */ - String getKeystoreAlias() { + public String getKeystoreAlias() { return mKeystoreAlias; } /** - * @hide + * Gets the Android context used for operations with this instance. */ - Context getContext() { + public Context getContext() { return mContext; } /** - * @hide + * Gets the subject distinguished name to be used on the X.509 certificate + * that will be put in the {@link java.security.KeyStore}. */ - X500Principal getSubjectDN() { + public X500Principal getSubjectDN() { return mSubjectDN; } /** - * @hide + * Gets the serial number to be used on the X.509 certificate that will be + * put in the {@link java.security.KeyStore}. */ - BigInteger getSerialNumber() { + public BigInteger getSerialNumber() { return mSerialNumber; } /** - * @hide + * Gets the start date to be used on the X.509 certificate that will be put + * in the {@link java.security.KeyStore}. */ - Date getStartDate() { + public Date getStartDate() { return mStartDate; } /** - * @hide + * Gets the end date to be used on the X.509 certificate that will be put in + * the {@link java.security.KeyStore}. */ - Date getEndDate() { + public Date getEndDate() { return mEndDate; } /** + * @hide + */ + int getFlags() { + return mFlags; + } + + /** + * Returns {@code true} if this parameter will require generated keys to be + * encrypted in the {@link java.security.KeyStore}. + */ + public boolean isEncryptionRequired() { + return (mFlags & KeyStore.FLAG_ENCRYPTED) != 0; + } + + /** * Builder class for {@link AndroidKeyPairGeneratorSpec} objects. * <p> * This will build a parameter spec for use with the <a href="{@docRoot} @@ -177,16 +200,17 @@ public class AndroidKeyPairGeneratorSpec implements AlgorithmParameterSpec { * Calendar end = new Calendar(); * end.add(1, Calendar.YEAR); * - * AndroidKeyPairGeneratorSpec spec = new AndroidKeyPairGeneratorSpec.Builder(mContext) - * .setAlias("myKey") - * .setSubject(new X500Principal("CN=myKey")) - * .setSerial(BigInteger.valueOf(1337)) - * .setStartDate(start.getTime()) - * .setEndDate(end.getTime()) - * .build(); + * AndroidKeyPairGeneratorSpec spec = + * new AndroidKeyPairGeneratorSpec.Builder(mContext) + * .setAlias("myKey") + * .setSubject(new X500Principal("CN=myKey")) + * .setSerial(BigInteger.valueOf(1337)) + * .setStartDate(start.getTime()) + * .setEndDate(end.getTime()) + * .build(); * </pre> */ - public static class Builder { + public final static class Builder { private final Context mContext; private String mKeystoreAlias; @@ -199,6 +223,14 @@ public class AndroidKeyPairGeneratorSpec implements AlgorithmParameterSpec { private Date mEndDate; + private int mFlags; + + /** + * Creates a new instance of the {@code Builder} with the given + * {@code context}. The {@code context} passed in may be used to pop up + * some UI to ask the user to unlock or initialize the Android KeyStore + * facility. + */ public Builder(Context context) { if (context == null) { throw new NullPointerException("context == null"); @@ -268,6 +300,17 @@ public class AndroidKeyPairGeneratorSpec implements AlgorithmParameterSpec { } /** + * Indicates that this key must be encrypted at rest on storage. Note + * that enabling this will require that the user enable a strong lock + * screen (e.g., PIN, password) before creating or using the generated + * key is successful. + */ + public Builder setEncryptionRequired() { + mFlags |= KeyStore.FLAG_ENCRYPTED; + return this; + } + + /** * Builds the instance of the {@code AndroidKeyPairGeneratorSpec}. * * @throws IllegalArgumentException if a required field is missing @@ -275,7 +318,7 @@ public class AndroidKeyPairGeneratorSpec implements AlgorithmParameterSpec { */ public AndroidKeyPairGeneratorSpec build() { return new AndroidKeyPairGeneratorSpec(mContext, mKeystoreAlias, mSubjectDN, - mSerialNumber, mStartDate, mEndDate); + mSerialNumber, mStartDate, mEndDate, mFlags); } } } diff --git a/keystore/java/android/security/AndroidKeyStore.java b/keystore/java/android/security/AndroidKeyStore.java index 8a9826b..dcc9516 100644 --- a/keystore/java/android/security/AndroidKeyStore.java +++ b/keystore/java/android/security/AndroidKeyStore.java @@ -27,6 +27,10 @@ import java.io.InputStream; import java.io.OutputStream; import java.security.InvalidKeyException; import java.security.Key; +import java.security.KeyStore.Entry; +import java.security.KeyStore.PrivateKeyEntry; +import java.security.KeyStore.ProtectionParameter; +import java.security.KeyStore; import java.security.KeyStoreException; import java.security.KeyStoreSpi; import java.security.NoSuchAlgorithmException; @@ -198,14 +202,14 @@ public class AndroidKeyStore extends KeyStoreSpi { } if (key instanceof PrivateKey) { - setPrivateKeyEntry(alias, (PrivateKey) key, chain); + setPrivateKeyEntry(alias, (PrivateKey) key, chain, null); } else { throw new KeyStoreException("Only PrivateKeys are supported"); } } - private void setPrivateKeyEntry(String alias, PrivateKey key, Certificate[] chain) - throws KeyStoreException { + private void setPrivateKeyEntry(String alias, PrivateKey key, Certificate[] chain, + AndroidKeyStoreParameter params) throws KeyStoreException { byte[] keyBytes = null; final String pkeyAlias; @@ -317,15 +321,20 @@ public class AndroidKeyStore extends KeyStoreSpi { Credentials.deleteCertificateTypesForAlias(mKeyStore, alias); } + final int flags = (params == null) ? 0 : params.getFlags(); + if (shouldReplacePrivateKey - && !mKeyStore.importKey(Credentials.USER_PRIVATE_KEY + alias, keyBytes)) { + && !mKeyStore.importKey(Credentials.USER_PRIVATE_KEY + alias, keyBytes, + android.security.KeyStore.UID_SELF, flags)) { Credentials.deleteAllTypesForAlias(mKeyStore, alias); throw new KeyStoreException("Couldn't put private key in keystore"); - } else if (!mKeyStore.put(Credentials.USER_CERTIFICATE + alias, userCertBytes)) { + } else if (!mKeyStore.put(Credentials.USER_CERTIFICATE + alias, userCertBytes, + android.security.KeyStore.UID_SELF, flags)) { Credentials.deleteAllTypesForAlias(mKeyStore, alias); throw new KeyStoreException("Couldn't put certificate #1 in keystore"); } else if (chainBytes != null - && !mKeyStore.put(Credentials.CA_CERTIFICATE + alias, chainBytes)) { + && !mKeyStore.put(Credentials.CA_CERTIFICATE + alias, chainBytes, + android.security.KeyStore.UID_SELF, flags)) { Credentials.deleteAllTypesForAlias(mKeyStore, alias); throw new KeyStoreException("Couldn't put certificate chain in keystore"); } @@ -355,7 +364,8 @@ public class AndroidKeyStore extends KeyStoreSpi { throw new KeyStoreException(e); } - if (!mKeyStore.put(Credentials.CA_CERTIFICATE + alias, encoded)) { + if (!mKeyStore.put(Credentials.CA_CERTIFICATE + alias, encoded, + android.security.KeyStore.UID_SELF, android.security.KeyStore.FLAG_NONE)) { throw new KeyStoreException("Couldn't insert certificate; is KeyStore initialized?"); } } @@ -517,4 +527,37 @@ public class AndroidKeyStore extends KeyStoreSpi { mKeyStore = android.security.KeyStore.getInstance(); } + @Override + public void engineSetEntry(String alias, Entry entry, ProtectionParameter param) + throws KeyStoreException { + if (entry == null) { + throw new KeyStoreException("entry == null"); + } + + if (engineContainsAlias(alias)) { + engineDeleteEntry(alias); + } + + if (entry instanceof KeyStore.TrustedCertificateEntry) { + KeyStore.TrustedCertificateEntry trE = (KeyStore.TrustedCertificateEntry) entry; + engineSetCertificateEntry(alias, trE.getTrustedCertificate()); + return; + } + + if (param != null && !(param instanceof AndroidKeyStoreParameter)) { + throw new KeyStoreException("protParam should be AndroidKeyStoreParameter; was: " + + param.getClass().getName()); + } + + if (entry instanceof PrivateKeyEntry) { + PrivateKeyEntry prE = (PrivateKeyEntry) entry; + setPrivateKeyEntry(alias, prE.getPrivateKey(), prE.getCertificateChain(), + (AndroidKeyStoreParameter) param); + return; + } + + throw new KeyStoreException( + "Entry must be a PrivateKeyEntry or TrustedCertificateEntry; was " + entry); + } + } diff --git a/keystore/java/android/security/AndroidKeyStoreParameter.java b/keystore/java/android/security/AndroidKeyStoreParameter.java new file mode 100644 index 0000000..4ce9f09 --- /dev/null +++ b/keystore/java/android/security/AndroidKeyStoreParameter.java @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2013 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.security.AndroidKeyPairGeneratorSpec.Builder; + +import java.security.KeyPairGenerator; +import java.security.PrivateKey; +import java.security.KeyStore.ProtectionParameter; +import java.security.cert.Certificate; + +/** + * This provides the optional parameters that can be specified for + * {@code KeyStore} entries that work with <a href="{@docRoot} + * guide/topics/security/keystore.html">Android KeyStore facility</a>. The + * Android KeyStore facility is accessed through a + * {@link java.security.KeyStore} API using the {@code AndroidKeyStore} + * provider. The {@code context} passed in may be used to pop up some UI to ask + * the user to unlock or initialize the Android KeyStore facility. + * <p> + * Any entries placed in the {@code KeyStore} may be retrieved later. Note that + * there is only one logical instance of the {@code KeyStore} per application + * UID so apps using the {@code sharedUid} facility will also share a + * {@code KeyStore}. + * <p> + * Keys may be generated using the {@link KeyPairGenerator} facility with a + * {@link AndroidKeyPairGeneratorSpec} to specify the entry's {@code alias}. A + * self-signed X.509 certificate will be attached to generated entries, but that + * may be replaced at a later time by a certificate signed by a real Certificate + * Authority. + * + * @hide + */ +public final class AndroidKeyStoreParameter implements ProtectionParameter { + private int mFlags; + + private AndroidKeyStoreParameter(int flags) { + mFlags = flags; + } + + /** + * @hide + */ + public int getFlags() { + return mFlags; + } + + /** + * Returns {@code true} if this parameter requires entries to be encrypted + * on the disk. + */ + public boolean isEncryptionRequired() { + return (mFlags & KeyStore.FLAG_ENCRYPTED) != 0; + } + + /** + * Builder class for {@link AndroidKeyStoreParameter} objects. + * <p> + * This will build protection parameters for use with the <a + * href="{@docRoot} guide/topics/security/keystore.html">Android KeyStore + * facility</a>. + * <p> + * This can be used to require that KeyStore entries be stored encrypted. + * <p> + * Example: + * + * <pre class="prettyprint"> + * AndroidKeyStoreParameter params = + * new AndroidKeyStoreParameter.Builder(mContext).setEncryptionRequired().build(); + * </pre> + */ + public final static class Builder { + private int mFlags; + + /** + * Creates a new instance of the {@code Builder} with the given + * {@code context}. The {@code context} passed in may be used to pop up + * some UI to ask the user to unlock or initialize the Android KeyStore + * facility. + */ + public Builder(Context context) { + if (context == null) { + throw new NullPointerException("context == null"); + } + + // Context is currently not used, but will be in the future. + } + + /** + * Indicates that this key must be encrypted at rest on storage. Note + * that enabling this will require that the user enable a strong lock + * screen (e.g., PIN, password) before creating or using the generated + * key is successful. + */ + public Builder setEncryptionRequired() { + mFlags |= KeyStore.FLAG_ENCRYPTED; + return this; + } + + /** + * Builds the instance of the {@code AndroidKeyPairGeneratorSpec}. + * + * @throws IllegalArgumentException if a required field is missing + * @return built instance of {@code AndroidKeyPairGeneratorSpec} + */ + public AndroidKeyStoreParameter build() { + return new AndroidKeyStoreParameter(mFlags); + } + } +} diff --git a/keystore/java/android/security/AndroidKeyStoreProvider.java b/keystore/java/android/security/AndroidKeyStoreProvider.java index 40d7e1a..8ca301e 100644 --- a/keystore/java/android/security/AndroidKeyStoreProvider.java +++ b/keystore/java/android/security/AndroidKeyStoreProvider.java @@ -33,7 +33,6 @@ public class AndroidKeyStoreProvider extends Provider { put("KeyStore." + AndroidKeyStore.NAME, AndroidKeyStore.class.getName()); // java.security.KeyPairGenerator - put("KeyPairGenerator." + AndroidKeyPairGenerator.NAME, - AndroidKeyPairGenerator.class.getName()); + put("KeyPairGenerator." + AndroidKeyStore.NAME, AndroidKeyPairGenerator.class.getName()); } } diff --git a/keystore/java/android/security/KeyStore.java b/keystore/java/android/security/KeyStore.java index 852f0bb..45385ee 100644 --- a/keystore/java/android/security/KeyStore.java +++ b/keystore/java/android/security/KeyStore.java @@ -40,6 +40,13 @@ public class KeyStore { public static final int UNDEFINED_ACTION = 9; public static final int WRONG_PASSWORD = 10; + // Used for UID field to indicate the calling UID. + public static final int UID_SELF = -1; + + // Flags for "put" "import" and "generate" + public static final int FLAG_NONE = 0; + public static final int FLAG_ENCRYPTED = 1; + // States public enum State { UNLOCKED, LOCKED, UNINITIALIZED }; @@ -87,17 +94,21 @@ public class KeyStore { } } - public boolean put(String key, byte[] value, int uid) { + public boolean put(String key, byte[] value, int uid, int flags) { try { - return mBinder.insert(key, value, uid) == NO_ERROR; + return mBinder.insert(key, value, uid, flags) == NO_ERROR; } catch (RemoteException e) { Log.w(TAG, "Cannot connect to keystore", e); return false; } } + public boolean put(String key, byte[] value, int uid) { + return put(key, value, uid, FLAG_ENCRYPTED); + } + public boolean put(String key, byte[] value) { - return put(key, value, -1); + return put(key, value, UID_SELF); } public boolean delete(String key, int uid) { @@ -110,7 +121,7 @@ public class KeyStore { } public boolean delete(String key) { - return delete(key, -1); + return delete(key, UID_SELF); } public boolean contains(String key, int uid) { @@ -123,7 +134,7 @@ public class KeyStore { } public boolean contains(String key) { - return contains(key, -1); + return contains(key, UID_SELF); } public String[] saw(String prefix, int uid) { @@ -136,7 +147,7 @@ public class KeyStore { } public String[] saw(String prefix) { - return saw(prefix, -1); + return saw(prefix, UID_SELF); } public boolean reset() { @@ -185,30 +196,38 @@ public class KeyStore { } } - public boolean generate(String key, int uid) { + public boolean generate(String key, int uid, int flags) { try { - return mBinder.generate(key, uid) == NO_ERROR; + return mBinder.generate(key, uid, flags) == NO_ERROR; } catch (RemoteException e) { Log.w(TAG, "Cannot connect to keystore", e); return false; } } + public boolean generate(String key, int uid) { + return generate(key, uid, FLAG_ENCRYPTED); + } + public boolean generate(String key) { - return generate(key, -1); + return generate(key, UID_SELF); } - public boolean importKey(String keyName, byte[] key, int uid) { + public boolean importKey(String keyName, byte[] key, int uid, int flags) { try { - return mBinder.import_key(keyName, key, uid) == NO_ERROR; + return mBinder.import_key(keyName, key, uid, flags) == NO_ERROR; } catch (RemoteException e) { Log.w(TAG, "Cannot connect to keystore", e); return false; } } + public boolean importKey(String keyName, byte[] key, int uid) { + return importKey(keyName, key, uid, FLAG_ENCRYPTED); + } + public boolean importKey(String keyName, byte[] key) { - return importKey(keyName, key, -1); + return importKey(keyName, key, UID_SELF); } public byte[] getPubkey(String key) { @@ -230,7 +249,7 @@ public class KeyStore { } public boolean delKey(String key) { - return delKey(key, -1); + return delKey(key, UID_SELF); } public byte[] sign(String key, byte[] data) { diff --git a/keystore/tests/src/android/security/AndroidKeyPairGeneratorSpecTest.java b/keystore/tests/src/android/security/AndroidKeyPairGeneratorSpecTest.java index 3d275cd..5d4ab9c 100644 --- a/keystore/tests/src/android/security/AndroidKeyPairGeneratorSpecTest.java +++ b/keystore/tests/src/android/security/AndroidKeyPairGeneratorSpecTest.java @@ -39,8 +39,9 @@ public class AndroidKeyPairGeneratorSpecTest extends AndroidTestCase { private static final Date NOW_PLUS_10_YEARS = new Date(NOW.getYear() + 10, 0, 1); public void testConstructor_Success() throws Exception { - AndroidKeyPairGeneratorSpec spec = new AndroidKeyPairGeneratorSpec(getContext(), - TEST_ALIAS_1, TEST_DN_1, SERIAL_1, NOW, NOW_PLUS_10_YEARS); + AndroidKeyPairGeneratorSpec spec = + new AndroidKeyPairGeneratorSpec(getContext(), TEST_ALIAS_1, TEST_DN_1, SERIAL_1, + NOW, NOW_PLUS_10_YEARS, 0); assertEquals("Context should be the one specified", getContext(), spec.getContext()); @@ -60,6 +61,7 @@ public class AndroidKeyPairGeneratorSpecTest extends AndroidTestCase { .setSerialNumber(SERIAL_1) .setStartDate(NOW) .setEndDate(NOW_PLUS_10_YEARS) + .setEncryptionRequired() .build(); assertEquals("Context should be the one specified", getContext(), spec.getContext()); @@ -71,12 +73,14 @@ public class AndroidKeyPairGeneratorSpecTest extends AndroidTestCase { assertEquals("startDate should be the one specified", NOW, spec.getStartDate()); assertEquals("endDate should be the one specified", NOW_PLUS_10_YEARS, spec.getEndDate()); + + assertEquals("encryption flag should be on", KeyStore.FLAG_ENCRYPTED, spec.getFlags()); } public void testConstructor_NullContext_Failure() throws Exception { try { new AndroidKeyPairGeneratorSpec(null, TEST_ALIAS_1, TEST_DN_1, SERIAL_1, NOW, - NOW_PLUS_10_YEARS); + NOW_PLUS_10_YEARS, 0); fail("Should throw IllegalArgumentException when context is null"); } catch (IllegalArgumentException success) { } @@ -85,7 +89,7 @@ public class AndroidKeyPairGeneratorSpecTest extends AndroidTestCase { public void testConstructor_NullKeystoreAlias_Failure() throws Exception { try { new AndroidKeyPairGeneratorSpec(getContext(), null, TEST_DN_1, SERIAL_1, NOW, - NOW_PLUS_10_YEARS); + NOW_PLUS_10_YEARS, 0); fail("Should throw IllegalArgumentException when keystoreAlias is null"); } catch (IllegalArgumentException success) { } @@ -94,7 +98,7 @@ public class AndroidKeyPairGeneratorSpecTest extends AndroidTestCase { public void testConstructor_NullSubjectDN_Failure() throws Exception { try { new AndroidKeyPairGeneratorSpec(getContext(), TEST_ALIAS_1, null, SERIAL_1, NOW, - NOW_PLUS_10_YEARS); + NOW_PLUS_10_YEARS, 0); fail("Should throw IllegalArgumentException when subjectDN is null"); } catch (IllegalArgumentException success) { } @@ -103,7 +107,7 @@ public class AndroidKeyPairGeneratorSpecTest extends AndroidTestCase { public void testConstructor_NullSerial_Failure() throws Exception { try { new AndroidKeyPairGeneratorSpec(getContext(), TEST_ALIAS_1, TEST_DN_1, null, NOW, - NOW_PLUS_10_YEARS); + NOW_PLUS_10_YEARS, 0); fail("Should throw IllegalArgumentException when startDate is null"); } catch (IllegalArgumentException success) { } @@ -112,7 +116,7 @@ public class AndroidKeyPairGeneratorSpecTest extends AndroidTestCase { public void testConstructor_NullStartDate_Failure() throws Exception { try { new AndroidKeyPairGeneratorSpec(getContext(), TEST_ALIAS_1, TEST_DN_1, SERIAL_1, null, - NOW_PLUS_10_YEARS); + NOW_PLUS_10_YEARS, 0); fail("Should throw IllegalArgumentException when startDate is null"); } catch (IllegalArgumentException success) { } @@ -121,7 +125,7 @@ public class AndroidKeyPairGeneratorSpecTest extends AndroidTestCase { public void testConstructor_NullEndDate_Failure() throws Exception { try { new AndroidKeyPairGeneratorSpec(getContext(), TEST_ALIAS_1, TEST_DN_1, SERIAL_1, NOW, - null); + null, 0); fail("Should throw IllegalArgumentException when keystoreAlias is null"); } catch (IllegalArgumentException success) { } @@ -130,7 +134,7 @@ public class AndroidKeyPairGeneratorSpecTest extends AndroidTestCase { public void testConstructor_EndBeforeStart_Failure() throws Exception { try { new AndroidKeyPairGeneratorSpec(getContext(), TEST_ALIAS_1, TEST_DN_1, SERIAL_1, - NOW_PLUS_10_YEARS, NOW); + NOW_PLUS_10_YEARS, NOW, 0); fail("Should throw IllegalArgumentException when end is before start"); } catch (IllegalArgumentException success) { } diff --git a/keystore/tests/src/android/security/AndroidKeyPairGeneratorTest.java b/keystore/tests/src/android/security/AndroidKeyPairGeneratorTest.java index 69007c4..c5cf514 100644 --- a/keystore/tests/src/android/security/AndroidKeyPairGeneratorTest.java +++ b/keystore/tests/src/android/security/AndroidKeyPairGeneratorTest.java @@ -27,6 +27,7 @@ import java.security.SecureRandom; import java.security.cert.Certificate; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; +import java.text.SimpleDateFormat; import java.util.Date; import javax.security.auth.x500.X500Principal; @@ -64,22 +65,34 @@ public class AndroidKeyPairGeneratorTest extends AndroidTestCase { assertFalse(mAndroidKeyStore.isUnlocked()); + mGenerator = java.security.KeyPairGenerator.getInstance("AndroidKeyStore"); + } + + private void setupPassword() { assertTrue(mAndroidKeyStore.password("1111")); assertTrue(mAndroidKeyStore.isUnlocked()); String[] aliases = mAndroidKeyStore.saw(""); assertNotNull(aliases); assertEquals(0, aliases.length); - - mGenerator = java.security.KeyPairGenerator.getInstance(AndroidKeyPairGenerator.NAME); } - public void testKeyPairGenerator_Initialize_Params_Success() throws Exception { - mGenerator.initialize(new AndroidKeyPairGeneratorSpec(getContext(), TEST_ALIAS_1, - TEST_DN_1, TEST_SERIAL_1, NOW, NOW_PLUS_10_YEARS)); + public void testKeyPairGenerator_Initialize_Params_Encrypted_Success() throws Exception { + setupPassword(); + + mGenerator.initialize(new AndroidKeyPairGeneratorSpec.Builder(getContext()) + .setAlias(TEST_ALIAS_1) + .setSubject(TEST_DN_1) + .setSerialNumber(TEST_SERIAL_1) + .setStartDate(NOW) + .setEndDate(NOW_PLUS_10_YEARS) + .setEncryptionRequired() + .build()); } - public void testKeyPairGenerator_Initialize_KeySize_Failure() throws Exception { + public void testKeyPairGenerator_Initialize_KeySize_Encrypted_Failure() throws Exception { + setupPassword(); + try { mGenerator.initialize(1024); fail("KeyPairGenerator should not support setting the key size"); @@ -87,7 +100,10 @@ public class AndroidKeyPairGeneratorTest extends AndroidTestCase { } } - public void testKeyPairGenerator_Initialize_KeySizeAndSecureRandom_Failure() throws Exception { + public void testKeyPairGenerator_Initialize_KeySizeAndSecureRandom_Encrypted_Failure() + throws Exception { + setupPassword(); + try { mGenerator.initialize(1024, new SecureRandom()); fail("KeyPairGenerator should not support setting the key size"); @@ -95,14 +111,48 @@ public class AndroidKeyPairGeneratorTest extends AndroidTestCase { } } - public void testKeyPairGenerator_Initialize_ParamsAndSecureRandom_Failure() throws Exception { - mGenerator.initialize(new AndroidKeyPairGeneratorSpec(getContext(), TEST_ALIAS_1, - TEST_DN_1, TEST_SERIAL_1, NOW, NOW_PLUS_10_YEARS), new SecureRandom()); + public void testKeyPairGenerator_Initialize_ParamsAndSecureRandom_Encrypted_Failure() + throws Exception { + setupPassword(); + + mGenerator.initialize( + new AndroidKeyPairGeneratorSpec.Builder(getContext()) + .setAlias(TEST_ALIAS_1) + .setSubject(TEST_DN_1) + .setSerialNumber(TEST_SERIAL_1) + .setStartDate(NOW) + .setEndDate(NOW_PLUS_10_YEARS) + .setEncryptionRequired() + .build(), + new SecureRandom()); } - public void testKeyPairGenerator_GenerateKeyPair_Success() throws Exception { - mGenerator.initialize(new AndroidKeyPairGeneratorSpec(getContext(), TEST_ALIAS_1, - TEST_DN_1, TEST_SERIAL_1, NOW, NOW_PLUS_10_YEARS)); + public void testKeyPairGenerator_GenerateKeyPair_Encrypted_Success() throws Exception { + setupPassword(); + + mGenerator.initialize(new AndroidKeyPairGeneratorSpec.Builder(getContext()) + .setAlias(TEST_ALIAS_1) + .setSubject(TEST_DN_1) + .setSerialNumber(TEST_SERIAL_1) + .setStartDate(NOW) + .setEndDate(NOW_PLUS_10_YEARS) + .setEncryptionRequired() + .build()); + + final KeyPair pair = mGenerator.generateKeyPair(); + assertNotNull("The KeyPair returned should not be null", pair); + + assertKeyPairCorrect(pair, TEST_ALIAS_1, TEST_DN_1, TEST_SERIAL_1, NOW, NOW_PLUS_10_YEARS); + } + + public void testKeyPairGenerator_GenerateKeyPair_Unencrypted_Success() throws Exception { + mGenerator.initialize(new AndroidKeyPairGeneratorSpec.Builder(getContext()) + .setAlias(TEST_ALIAS_1) + .setSubject(TEST_DN_1) + .setSerialNumber(TEST_SERIAL_1) + .setStartDate(NOW) + .setEndDate(NOW_PLUS_10_YEARS) + .build()); final KeyPair pair = mGenerator.generateKeyPair(); assertNotNull("The KeyPair returned should not be null", pair); @@ -113,8 +163,13 @@ public class AndroidKeyPairGeneratorTest extends AndroidTestCase { public void testKeyPairGenerator_GenerateKeyPair_Replaced_Success() throws Exception { // Generate the first key { - mGenerator.initialize(new AndroidKeyPairGeneratorSpec(getContext(), TEST_ALIAS_1, - TEST_DN_1, TEST_SERIAL_1, NOW, NOW_PLUS_10_YEARS)); + mGenerator.initialize(new AndroidKeyPairGeneratorSpec.Builder(getContext()) + .setAlias(TEST_ALIAS_1) + .setSubject(TEST_DN_1) + .setSerialNumber(TEST_SERIAL_1) + .setStartDate(NOW) + .setEndDate(NOW_PLUS_10_YEARS) + .build()); final KeyPair pair1 = mGenerator.generateKeyPair(); assertNotNull("The KeyPair returned should not be null", pair1); assertKeyPairCorrect(pair1, TEST_ALIAS_1, TEST_DN_1, TEST_SERIAL_1, NOW, @@ -123,8 +178,13 @@ public class AndroidKeyPairGeneratorTest extends AndroidTestCase { // Replace the original key { - mGenerator.initialize(new AndroidKeyPairGeneratorSpec(getContext(), TEST_ALIAS_2, - TEST_DN_2, TEST_SERIAL_2, NOW, NOW_PLUS_10_YEARS)); + mGenerator.initialize(new AndroidKeyPairGeneratorSpec.Builder(getContext()) + .setAlias(TEST_ALIAS_2) + .setSubject(TEST_DN_2) + .setSerialNumber(TEST_SERIAL_2) + .setStartDate(NOW) + .setEndDate(NOW_PLUS_10_YEARS) + .build()); final KeyPair pair2 = mGenerator.generateKeyPair(); assertNotNull("The KeyPair returned should not be null", pair2); assertKeyPairCorrect(pair2, TEST_ALIAS_2, TEST_DN_2, TEST_SERIAL_2, NOW, @@ -132,6 +192,49 @@ public class AndroidKeyPairGeneratorTest extends AndroidTestCase { } } + public void testKeyPairGenerator_GenerateKeyPair_Replaced_UnencryptedToEncrypted_Success() + throws Exception { + // Generate the first key + { + mGenerator.initialize(new AndroidKeyPairGeneratorSpec.Builder(getContext()) + .setAlias(TEST_ALIAS_1) + .setSubject(TEST_DN_1) + .setSerialNumber(TEST_SERIAL_1) + .setStartDate(NOW) + .setEndDate(NOW_PLUS_10_YEARS) + .build()); + final KeyPair pair1 = mGenerator.generateKeyPair(); + assertNotNull("The KeyPair returned should not be null", pair1); + assertKeyPairCorrect(pair1, TEST_ALIAS_1, TEST_DN_1, TEST_SERIAL_1, NOW, + NOW_PLUS_10_YEARS); + } + + // Attempt to replace previous key + { + mGenerator.initialize(new AndroidKeyPairGeneratorSpec.Builder(getContext()) + .setAlias(TEST_ALIAS_1) + .setSubject(TEST_DN_2) + .setSerialNumber(TEST_SERIAL_2) + .setStartDate(NOW) + .setEndDate(NOW_PLUS_10_YEARS) + .setEncryptionRequired() + .build()); + try { + mGenerator.generateKeyPair(); + fail("Should not be able to generate encrypted key while not initialized"); + } catch (IllegalStateException expected) { + } + + assertTrue(mAndroidKeyStore.password("1111")); + assertTrue(mAndroidKeyStore.isUnlocked()); + + final KeyPair pair2 = mGenerator.generateKeyPair(); + assertNotNull("The KeyPair returned should not be null", pair2); + assertKeyPairCorrect(pair2, TEST_ALIAS_1, TEST_DN_2, TEST_SERIAL_2, NOW, + NOW_PLUS_10_YEARS); + } + } + private void assertKeyPairCorrect(KeyPair pair, String alias, X500Principal dn, BigInteger serial, Date start, Date end) throws Exception { final PublicKey pubKey = pair.getPublic(); @@ -163,10 +266,10 @@ public class AndroidKeyPairGeneratorTest extends AndroidTestCase { assertEquals("The Serial should be the one passed into the params", serial, x509userCert.getSerialNumber()); - assertEquals("The notBefore date should be the one passed into the params", start, + assertDateEquals("The notBefore date should be the one passed into the params", start, x509userCert.getNotBefore()); - assertEquals("The notAfter date should be the one passed into the params", end, + assertDateEquals("The notAfter date should be the one passed into the params", end, x509userCert.getNotAfter()); x509userCert.verify(pubKey); @@ -178,4 +281,13 @@ public class AndroidKeyPairGeneratorTest extends AndroidTestCase { assertNotNull("The keystore should return the public key for the generated key", pubKeyBytes); } + + private static void assertDateEquals(String message, Date date1, Date date2) throws Exception { + SimpleDateFormat formatter = new SimpleDateFormat("dd MMM yyyy HH:mm:ss"); + + String result1 = formatter.format(date1); + String result2 = formatter.format(date2); + + assertEquals(message, result1, result2); + } } diff --git a/keystore/tests/src/android/security/AndroidKeyStoreTest.java b/keystore/tests/src/android/security/AndroidKeyStoreTest.java index 8928e06..05ffe109 100644 --- a/keystore/tests/src/android/security/AndroidKeyStoreTest.java +++ b/keystore/tests/src/android/security/AndroidKeyStoreTest.java @@ -469,12 +469,14 @@ public class AndroidKeyStoreTest extends AndroidTestCase { assertTrue(mAndroidKeyStore.reset()); assertFalse(mAndroidKeyStore.isUnlocked()); + mKeyStore = java.security.KeyStore.getInstance("AndroidKeyStore"); + } + + private void setupPassword() { assertTrue(mAndroidKeyStore.password("1111")); assertTrue(mAndroidKeyStore.isUnlocked()); assertEquals(0, mAndroidKeyStore.saw("").length); - - mKeyStore = java.security.KeyStore.getInstance(AndroidKeyStore.NAME); } private void assertAliases(final String[] expectedAliases) throws KeyStoreException { @@ -495,7 +497,9 @@ public class AndroidKeyStoreTest extends AndroidTestCase { expectedAliases.length, count); } - public void testKeyStore_Aliases_Success() throws Exception { + public void testKeyStore_Aliases_Encrypted_Success() throws Exception { + setupPassword(); + mKeyStore.load(null, null); assertAliases(new String[] {}); @@ -509,7 +513,9 @@ public class AndroidKeyStoreTest extends AndroidTestCase { assertAliases(new String[] { TEST_ALIAS_1, TEST_ALIAS_2 }); } - public void testKeyStore_Aliases_NotInitialized_Failure() throws Exception { + public void testKeyStore_Aliases_NotInitialized_Encrypted_Failure() throws Exception { + setupPassword(); + try { mKeyStore.aliases(); fail("KeyStore should throw exception when not initialized"); @@ -517,7 +523,9 @@ public class AndroidKeyStoreTest extends AndroidTestCase { } } - public void testKeyStore_ContainsAliases_PrivateAndCA_Success() throws Exception { + public void testKeyStore_ContainsAliases_PrivateAndCA_Encrypted_Success() throws Exception { + setupPassword(); + mKeyStore.load(null, null); assertAliases(new String[] {}); @@ -534,7 +542,9 @@ public class AndroidKeyStoreTest extends AndroidTestCase { mKeyStore.containsAlias(TEST_ALIAS_3)); } - public void testKeyStore_ContainsAliases_CAOnly_Success() throws Exception { + public void testKeyStore_ContainsAliases_CAOnly_Encrypted_Success() throws Exception { + setupPassword(); + mKeyStore.load(null, null); assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_2, FAKE_CA_1)); @@ -542,13 +552,17 @@ public class AndroidKeyStoreTest extends AndroidTestCase { assertTrue("Should contain added CA certificate", mKeyStore.containsAlias(TEST_ALIAS_2)); } - public void testKeyStore_ContainsAliases_NonExistent_Failure() throws Exception { + public void testKeyStore_ContainsAliases_NonExistent_Encrypted_Failure() throws Exception { + setupPassword(); + mKeyStore.load(null, null); assertFalse("Should contain added CA certificate", mKeyStore.containsAlias(TEST_ALIAS_1)); } - public void testKeyStore_DeleteEntry_Success() throws Exception { + public void testKeyStore_DeleteEntry_Encrypted_Success() throws Exception { + setupPassword(); + mKeyStore.load(null, null); // TEST_ALIAS_1 @@ -578,14 +592,18 @@ public class AndroidKeyStoreTest extends AndroidTestCase { assertAliases(new String[] { }); } - public void testKeyStore_DeleteEntry_EmptyStore_Success() throws Exception { + public void testKeyStore_DeleteEntry_EmptyStore_Encrypted_Success() throws Exception { + setupPassword(); + mKeyStore.load(null, null); // Should not throw when a non-existent entry is requested for delete. mKeyStore.deleteEntry(TEST_ALIAS_1); } - public void testKeyStore_DeleteEntry_NonExistent_Success() throws Exception { + public void testKeyStore_DeleteEntry_NonExistent_Encrypted_Success() throws Exception { + setupPassword(); + mKeyStore.load(null, null); // TEST_ALIAS_1 @@ -598,7 +616,9 @@ public class AndroidKeyStoreTest extends AndroidTestCase { mKeyStore.deleteEntry(TEST_ALIAS_2); } - public void testKeyStore_GetCertificate_Single_Success() throws Exception { + public void testKeyStore_GetCertificate_Single_Encrypted_Success() throws Exception { + setupPassword(); + mKeyStore.load(null, null); assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_CA_1)); @@ -618,14 +638,18 @@ public class AndroidKeyStoreTest extends AndroidTestCase { assertEquals("Actual and retrieved certificates should be the same", actual, retrieved); } - public void testKeyStore_GetCertificate_NonExist_Failure() throws Exception { + public void testKeyStore_GetCertificate_NonExist_Encrypted_Failure() throws Exception { + setupPassword(); + mKeyStore.load(null, null); assertNull("Certificate should not exist in keystore", mKeyStore.getCertificate(TEST_ALIAS_1)); } - public void testKeyStore_GetCertificateAlias_CAEntry_Success() throws Exception { + public void testKeyStore_GetCertificateAlias_CAEntry_Encrypted_Success() throws Exception { + setupPassword(); + mKeyStore.load(null, null); assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_CA_1)); @@ -637,7 +661,10 @@ public class AndroidKeyStoreTest extends AndroidTestCase { mKeyStore.getCertificateAlias(actual)); } - public void testKeyStore_GetCertificateAlias_PrivateKeyEntry_Success() throws Exception { + public void testKeyStore_GetCertificateAlias_PrivateKeyEntry_Encrypted_Success() + throws Exception { + setupPassword(); + mKeyStore.load(null, null); assertTrue(mAndroidKeyStore.importKey(Credentials.USER_PRIVATE_KEY + TEST_ALIAS_1, @@ -652,8 +679,10 @@ public class AndroidKeyStoreTest extends AndroidTestCase { mKeyStore.getCertificateAlias(actual)); } - public void testKeyStore_GetCertificateAlias_CAEntry_WithPrivateKeyUsingCA_Success() + public void testKeyStore_GetCertificateAlias_CAEntry_WithPrivateKeyUsingCA_Encrypted_Success() throws Exception { + setupPassword(); + mKeyStore.load(null, null); // Insert TrustedCertificateEntry with CA name @@ -672,7 +701,10 @@ public class AndroidKeyStoreTest extends AndroidTestCase { mKeyStore.getCertificateAlias(actual)); } - public void testKeyStore_GetCertificateAlias_NonExist_Empty_Failure() throws Exception { + public void testKeyStore_GetCertificateAlias_NonExist_Empty_Encrypted_Failure() + throws Exception { + setupPassword(); + mKeyStore.load(null, null); CertificateFactory f = CertificateFactory.getInstance("X.509"); @@ -682,7 +714,9 @@ public class AndroidKeyStoreTest extends AndroidTestCase { mKeyStore.getCertificateAlias(actual)); } - public void testKeyStore_GetCertificateAlias_NonExist_Failure() throws Exception { + public void testKeyStore_GetCertificateAlias_NonExist_Encrypted_Failure() throws Exception { + setupPassword(); + mKeyStore.load(null, null); assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_CA_1)); @@ -694,7 +728,9 @@ public class AndroidKeyStoreTest extends AndroidTestCase { mKeyStore.getCertificateAlias(userCert)); } - public void testKeyStore_GetCertificateChain_SingleLength_Success() throws Exception { + public void testKeyStore_GetCertificateChain_SingleLength_Encrypted_Success() throws Exception { + setupPassword(); + mKeyStore.load(null, null); assertTrue(mAndroidKeyStore.importKey(Credentials.USER_PRIVATE_KEY + TEST_ALIAS_1, @@ -720,14 +756,18 @@ public class AndroidKeyStoreTest extends AndroidTestCase { mKeyStore.getCertificateChain(TEST_ALIAS_2)); } - public void testKeyStore_GetCertificateChain_NonExist_Failure() throws Exception { + public void testKeyStore_GetCertificateChain_NonExist_Encrypted_Failure() throws Exception { + setupPassword(); + mKeyStore.load(null, null); assertNull("Stored certificate alias should not be found", mKeyStore.getCertificateChain(TEST_ALIAS_1)); } - public void testKeyStore_GetCreationDate_PrivateKeyEntry_Success() throws Exception { + public void testKeyStore_GetCreationDate_PrivateKeyEntry_Encrypted_Success() throws Exception { + setupPassword(); + mKeyStore.load(null, null); assertTrue(mAndroidKeyStore.importKey(Credentials.USER_PRIVATE_KEY + TEST_ALIAS_1, @@ -745,7 +785,29 @@ public class AndroidKeyStoreTest extends AndroidTestCase { assertTrue("Time should be close to current time", actual.after(expectedAfter)); } - public void testKeyStore_GetCreationDate_CAEntry_Success() throws Exception { + public void testKeyStore_GetCreationDate_PrivateKeyEntry_Unencrypted_Success() throws Exception { + mKeyStore.load(null, null); + + assertTrue(mAndroidKeyStore.importKey(Credentials.USER_PRIVATE_KEY + TEST_ALIAS_1, + FAKE_KEY_1, KeyStore.UID_SELF, KeyStore.FLAG_NONE)); + assertTrue(mAndroidKeyStore.put(Credentials.USER_CERTIFICATE + TEST_ALIAS_1, FAKE_USER_1, + KeyStore.UID_SELF, KeyStore.FLAG_NONE)); + assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_CA_1, + KeyStore.UID_SELF, KeyStore.FLAG_NONE)); + + Date now = new Date(); + Date actual = mKeyStore.getCreationDate(TEST_ALIAS_1); + + Date expectedAfter = new Date(now.getTime() - SLOP_TIME_MILLIS); + Date expectedBefore = new Date(now.getTime() + SLOP_TIME_MILLIS); + + assertTrue("Time should be close to current time", actual.before(expectedBefore)); + assertTrue("Time should be close to current time", actual.after(expectedAfter)); + } + + public void testKeyStore_GetCreationDate_CAEntry_Encrypted_Success() throws Exception { + setupPassword(); + mKeyStore.load(null, null); assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_CA_1)); @@ -761,7 +823,9 @@ public class AndroidKeyStoreTest extends AndroidTestCase { assertTrue("Time should be close to current time", actual.after(expectedAfter)); } - public void testKeyStore_GetEntry_NullParams_Success() throws Exception { + public void testKeyStore_GetEntry_NullParams_Encrypted_Success() throws Exception { + setupPassword(); + mKeyStore.load(null, null); assertTrue(mAndroidKeyStore.importKey(Credentials.USER_PRIVATE_KEY + TEST_ALIAS_1, @@ -779,6 +843,26 @@ public class AndroidKeyStoreTest extends AndroidTestCase { assertPrivateKeyEntryEquals(keyEntry, FAKE_KEY_1, FAKE_USER_1, FAKE_CA_1); } + public void testKeyStore_GetEntry_NullParams_Unencrypted_Success() throws Exception { + mKeyStore.load(null, null); + + assertTrue(mAndroidKeyStore.importKey(Credentials.USER_PRIVATE_KEY + TEST_ALIAS_1, + FAKE_KEY_1, KeyStore.UID_SELF, KeyStore.FLAG_NONE)); + assertTrue(mAndroidKeyStore.put(Credentials.USER_CERTIFICATE + TEST_ALIAS_1, FAKE_USER_1, + KeyStore.UID_SELF, KeyStore.FLAG_NONE)); + assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_CA_1, + KeyStore.UID_SELF, KeyStore.FLAG_NONE)); + + Entry entry = mKeyStore.getEntry(TEST_ALIAS_1, null); + assertNotNull("Entry should exist", entry); + + assertTrue("Should be a PrivateKeyEntry", entry instanceof PrivateKeyEntry); + + PrivateKeyEntry keyEntry = (PrivateKeyEntry) entry; + + assertPrivateKeyEntryEquals(keyEntry, FAKE_KEY_1, FAKE_USER_1, FAKE_CA_1); + } + @SuppressWarnings("unchecked") private void assertPrivateKeyEntryEquals(PrivateKeyEntry keyEntry, byte[] key, byte[] cert, byte[] ca) throws Exception { @@ -801,8 +885,9 @@ public class AndroidKeyStoreTest extends AndroidTestCase { private void assertPrivateKeyEntryEquals(PrivateKeyEntry keyEntry, PrivateKey expectedKey, Certificate expectedCert, Collection<Certificate> expectedChain) throws Exception { - assertEquals("Returned PrivateKey should be what we inserted", expectedKey, - keyEntry.getPrivateKey()); + assertEquals("Returned PrivateKey should be what we inserted", + ((RSAPrivateKey) expectedKey).getModulus(), + ((RSAPrivateKey) keyEntry.getPrivateKey()).getModulus()); assertEquals("Returned Certificate should be what we inserted", expectedCert, keyEntry.getCertificate()); @@ -823,14 +908,25 @@ public class AndroidKeyStoreTest extends AndroidTestCase { } } - public void testKeyStore_GetEntry_Nonexistent_NullParams_Failure() throws Exception { + public void testKeyStore_GetEntry_Nonexistent_NullParams_Encrypted_Failure() throws Exception { + setupPassword(); + mKeyStore.load(null, null); assertNull("A non-existent entry should return null", mKeyStore.getEntry(TEST_ALIAS_1, null)); } - public void testKeyStore_GetKey_NoPassword_Success() throws Exception { + public void testKeyStore_GetEntry_Nonexistent_NullParams_Unencrypted_Failure() throws Exception { + mKeyStore.load(null, null); + + assertNull("A non-existent entry should return null", + mKeyStore.getEntry(TEST_ALIAS_1, null)); + } + + public void testKeyStore_GetKey_NoPassword_Encrypted_Success() throws Exception { + setupPassword(); + mKeyStore.load(null, null); assertTrue(mAndroidKeyStore.importKey(Credentials.USER_PRIVATE_KEY + TEST_ALIAS_1, @@ -848,10 +944,37 @@ public class AndroidKeyStoreTest extends AndroidTestCase { KeyFactory keyFact = KeyFactory.getInstance("RSA"); PrivateKey expectedKey = keyFact.generatePrivate(new PKCS8EncodedKeySpec(FAKE_KEY_1)); - assertEquals("Inserted key should be same as retrieved key", actualKey, expectedKey); + assertEquals("Inserted key should be same as retrieved key", + ((RSAPrivateKey) expectedKey).getModulus(), actualKey.getModulus()); } - public void testKeyStore_GetKey_Certificate_Failure() throws Exception { + public void testKeyStore_GetKey_NoPassword_Unencrypted_Success() throws Exception { + mKeyStore.load(null, null); + + assertTrue(mAndroidKeyStore.importKey(Credentials.USER_PRIVATE_KEY + TEST_ALIAS_1, + FAKE_KEY_1, KeyStore.UID_SELF, KeyStore.FLAG_NONE)); + assertTrue(mAndroidKeyStore.put(Credentials.USER_CERTIFICATE + TEST_ALIAS_1, FAKE_USER_1, + KeyStore.UID_SELF, KeyStore.FLAG_NONE)); + assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_CA_1, + KeyStore.UID_SELF, KeyStore.FLAG_NONE)); + + Key key = mKeyStore.getKey(TEST_ALIAS_1, null); + assertNotNull("Key should exist", key); + + assertTrue("Should be a RSAPrivateKey", key instanceof RSAPrivateKey); + + RSAPrivateKey actualKey = (RSAPrivateKey) key; + + KeyFactory keyFact = KeyFactory.getInstance("RSA"); + PrivateKey expectedKey = keyFact.generatePrivate(new PKCS8EncodedKeySpec(FAKE_KEY_1)); + + assertEquals("Inserted key should be same as retrieved key", + ((RSAPrivateKey) expectedKey).getModulus(), actualKey.getModulus()); + } + + public void testKeyStore_GetKey_Certificate_Encrypted_Failure() throws Exception { + setupPassword(); + mKeyStore.load(null, null); assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_CA_1)); @@ -859,21 +982,28 @@ public class AndroidKeyStoreTest extends AndroidTestCase { assertNull("Certificate entries should return null", mKeyStore.getKey(TEST_ALIAS_1, null)); } - public void testKeyStore_GetKey_NonExistent_Failure() throws Exception { + public void testKeyStore_GetKey_NonExistent_Encrypted_Failure() throws Exception { + setupPassword(); + mKeyStore.load(null, null); assertNull("A non-existent entry should return null", mKeyStore.getKey(TEST_ALIAS_1, null)); } - public void testKeyStore_GetProvider_Success() throws Exception { + public void testKeyStore_GetProvider_Encrypted_Success() throws Exception { + assertEquals(AndroidKeyStoreProvider.PROVIDER_NAME, mKeyStore.getProvider().getName()); + setupPassword(); assertEquals(AndroidKeyStoreProvider.PROVIDER_NAME, mKeyStore.getProvider().getName()); } - public void testKeyStore_GetType_Success() throws Exception { + public void testKeyStore_GetType_Encrypted_Success() throws Exception { + assertEquals(AndroidKeyStore.NAME, mKeyStore.getType()); + setupPassword(); assertEquals(AndroidKeyStore.NAME, mKeyStore.getType()); } - public void testKeyStore_IsCertificateEntry_CA_Success() throws Exception { + public void testKeyStore_IsCertificateEntry_CA_Encrypted_Success() throws Exception { + setupPassword(); mKeyStore.load(null, null); assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_CA_1)); @@ -882,7 +1012,8 @@ public class AndroidKeyStoreTest extends AndroidTestCase { mKeyStore.isCertificateEntry(TEST_ALIAS_1)); } - public void testKeyStore_IsCertificateEntry_PrivateKey_Failure() throws Exception { + public void testKeyStore_IsCertificateEntry_PrivateKey_Encrypted_Failure() throws Exception { + setupPassword(); mKeyStore.load(null, null); assertTrue(mAndroidKeyStore.importKey(Credentials.USER_PRIVATE_KEY + TEST_ALIAS_1, @@ -894,14 +1025,23 @@ public class AndroidKeyStoreTest extends AndroidTestCase { mKeyStore.isCertificateEntry(TEST_ALIAS_1)); } - public void testKeyStore_IsCertificateEntry_NonExist_Failure() throws Exception { + public void testKeyStore_IsCertificateEntry_NonExist_Encrypted_Failure() throws Exception { + setupPassword(); + mKeyStore.load(null, null); + + assertFalse("Should return false for non-existent entry", + mKeyStore.isCertificateEntry(TEST_ALIAS_1)); + } + + public void testKeyStore_IsCertificateEntry_NonExist_Unencrypted_Failure() throws Exception { mKeyStore.load(null, null); assertFalse("Should return false for non-existent entry", mKeyStore.isCertificateEntry(TEST_ALIAS_1)); } - public void testKeyStore_IsKeyEntry_PrivateKey_Success() throws Exception { + public void testKeyStore_IsKeyEntry_PrivateKey_Encrypted_Success() throws Exception { + setupPassword(); mKeyStore.load(null, null); assertTrue(mAndroidKeyStore.importKey(Credentials.USER_PRIVATE_KEY + TEST_ALIAS_1, @@ -912,7 +1052,8 @@ public class AndroidKeyStoreTest extends AndroidTestCase { assertTrue("Should return true for PrivateKeyEntry", mKeyStore.isKeyEntry(TEST_ALIAS_1)); } - public void testKeyStore_IsKeyEntry_CA_Failure() throws Exception { + public void testKeyStore_IsKeyEntry_CA_Encrypted_Failure() throws Exception { + setupPassword(); mKeyStore.load(null, null); assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_CA_1)); @@ -920,17 +1061,19 @@ public class AndroidKeyStoreTest extends AndroidTestCase { assertFalse("Should return false for CA certificate", mKeyStore.isKeyEntry(TEST_ALIAS_1)); } - public void testKeyStore_IsKeyEntry_NonExist_Failure() throws Exception { + public void testKeyStore_IsKeyEntry_NonExist_Encrypted_Failure() throws Exception { + setupPassword(); mKeyStore.load(null, null); assertFalse("Should return false for non-existent entry", mKeyStore.isKeyEntry(TEST_ALIAS_1)); } - public void testKeyStore_SetCertificate_CA_Success() throws Exception { + public void testKeyStore_SetCertificate_CA_Encrypted_Success() throws Exception { final CertificateFactory f = CertificateFactory.getInstance("X.509"); final Certificate actual = f.generateCertificate(new ByteArrayInputStream(FAKE_CA_1)); + setupPassword(); mKeyStore.load(null, null); mKeyStore.setCertificateEntry(TEST_ALIAS_1, actual); @@ -942,7 +1085,8 @@ public class AndroidKeyStoreTest extends AndroidTestCase { retrieved); } - public void testKeyStore_SetCertificate_CAExists_Overwrite_Success() throws Exception { + public void testKeyStore_SetCertificate_CAExists_Overwrite_Encrypted_Success() throws Exception { + setupPassword(); mKeyStore.load(null, null); assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_CA_1)); @@ -958,7 +1102,8 @@ public class AndroidKeyStoreTest extends AndroidTestCase { assertAliases(new String[] { TEST_ALIAS_1 }); } - public void testKeyStore_SetCertificate_PrivateKeyExists_Failure() throws Exception { + public void testKeyStore_SetCertificate_PrivateKeyExists_Encrypted_Failure() throws Exception { + setupPassword(); mKeyStore.load(null, null); assertTrue(mAndroidKeyStore.importKey(Credentials.USER_PRIVATE_KEY + TEST_ALIAS_1, @@ -978,7 +1123,8 @@ public class AndroidKeyStoreTest extends AndroidTestCase { } } - public void testKeyStore_SetEntry_PrivateKeyEntry_Success() throws Exception { + public void testKeyStore_SetEntry_PrivateKeyEntry_Encrypted_Success() throws Exception { + setupPassword(); mKeyStore.load(null, null); KeyFactory keyFact = KeyFactory.getInstance("RSA"); @@ -1005,8 +1151,63 @@ public class AndroidKeyStoreTest extends AndroidTestCase { assertPrivateKeyEntryEquals(actual, FAKE_KEY_1, FAKE_USER_1, FAKE_CA_1); } - public void testKeyStore_SetEntry_PrivateKeyEntry_Overwrites_PrivateKeyEntry_Success() + public void testKeyStore_SetEntry_PrivateKeyEntry_Unencrypted_Success() throws Exception { + mKeyStore.load(null, null); + + KeyFactory keyFact = KeyFactory.getInstance("RSA"); + PrivateKey expectedKey = keyFact.generatePrivate(new PKCS8EncodedKeySpec(FAKE_KEY_1)); + + final CertificateFactory f = CertificateFactory.getInstance("X.509"); + + final Certificate[] expectedChain = new Certificate[2]; + expectedChain[0] = f.generateCertificate(new ByteArrayInputStream(FAKE_USER_1)); + expectedChain[1] = f.generateCertificate(new ByteArrayInputStream(FAKE_CA_1)); + + PrivateKeyEntry expected = new PrivateKeyEntry(expectedKey, expectedChain); + + mKeyStore.setEntry(TEST_ALIAS_1, expected, null); + + Entry actualEntry = mKeyStore.getEntry(TEST_ALIAS_1, null); + assertNotNull("Retrieved entry should exist", actualEntry); + + assertTrue("Retrieved entry should be of type PrivateKeyEntry", + actualEntry instanceof PrivateKeyEntry); + + PrivateKeyEntry actual = (PrivateKeyEntry) actualEntry; + + assertPrivateKeyEntryEquals(actual, FAKE_KEY_1, FAKE_USER_1, FAKE_CA_1); + } + + public void testKeyStore_SetEntry_PrivateKeyEntry_Params_Unencrypted_Failure() throws Exception { + mKeyStore.load(null, null); + + KeyFactory keyFact = KeyFactory.getInstance("RSA"); + PrivateKey expectedKey = keyFact.generatePrivate(new PKCS8EncodedKeySpec(FAKE_KEY_1)); + + final CertificateFactory f = CertificateFactory.getInstance("X.509"); + + final Certificate[] expectedChain = new Certificate[2]; + expectedChain[0] = f.generateCertificate(new ByteArrayInputStream(FAKE_USER_1)); + expectedChain[1] = f.generateCertificate(new ByteArrayInputStream(FAKE_CA_1)); + + PrivateKeyEntry entry = new PrivateKeyEntry(expectedKey, expectedChain); + + try { + mKeyStore.setEntry(TEST_ALIAS_1, entry, + new AndroidKeyStoreParameter.Builder(getContext()) + .setEncryptionRequired() + .build()); + fail("Shouldn't be able to insert encrypted entry when KeyStore uninitialized"); + } catch (KeyStoreException expected) { + } + + assertNull(mKeyStore.getEntry(TEST_ALIAS_1, null)); + } + + public void + testKeyStore_SetEntry_PrivateKeyEntry_Overwrites_PrivateKeyEntry_Encrypted_Success() throws Exception { + setupPassword(); mKeyStore.load(null, null); final KeyFactory keyFact = KeyFactory.getInstance("RSA"); @@ -1060,7 +1261,9 @@ public class AndroidKeyStoreTest extends AndroidTestCase { } } - public void testKeyStore_SetEntry_CAEntry_Overwrites_PrivateKeyEntry_Success() throws Exception { + public void testKeyStore_SetEntry_CAEntry_Overwrites_PrivateKeyEntry_Encrypted_Success() + throws Exception { + setupPassword(); mKeyStore.load(null, null); final CertificateFactory f = CertificateFactory.getInstance("X.509"); @@ -1104,7 +1307,9 @@ public class AndroidKeyStoreTest extends AndroidTestCase { } } - public void testKeyStore_SetEntry_PrivateKeyEntry_Overwrites_CAEntry_Success() throws Exception { + public void testKeyStore_SetEntry_PrivateKeyEntry_Overwrites_CAEntry_Encrypted_Success() + throws Exception { + setupPassword(); mKeyStore.load(null, null); final CertificateFactory f = CertificateFactory.getInstance("X.509"); @@ -1148,8 +1353,11 @@ public class AndroidKeyStoreTest extends AndroidTestCase { } } - public void testKeyStore_SetEntry_PrivateKeyEntry_Overwrites_ShortPrivateKeyEntry_Success() + public + void + testKeyStore_SetEntry_PrivateKeyEntry_Overwrites_ShortPrivateKeyEntry_Encrypted_Success() throws Exception { + setupPassword(); mKeyStore.load(null, null); final CertificateFactory f = CertificateFactory.getInstance("X.509"); @@ -1198,7 +1406,9 @@ public class AndroidKeyStoreTest extends AndroidTestCase { } } - public void testKeyStore_SetEntry_CAEntry_Overwrites_CAEntry_Success() throws Exception { + public void testKeyStore_SetEntry_CAEntry_Overwrites_CAEntry_Encrypted_Success() + throws Exception { + setupPassword(); mKeyStore.load(null, null); final CertificateFactory f = CertificateFactory.getInstance("X.509"); @@ -1239,7 +1449,8 @@ public class AndroidKeyStoreTest extends AndroidTestCase { } } - public void testKeyStore_SetKeyEntry_ProtectedKey_Failure() throws Exception { + public void testKeyStore_SetKeyEntry_ProtectedKey_Encrypted_Failure() throws Exception { + setupPassword(); mKeyStore.load(null, null); final CertificateFactory f = CertificateFactory.getInstance("X.509"); @@ -1259,7 +1470,8 @@ public class AndroidKeyStoreTest extends AndroidTestCase { } } - public void testKeyStore_SetKeyEntry_Success() throws Exception { + public void testKeyStore_SetKeyEntry_Encrypted_Success() throws Exception { + setupPassword(); mKeyStore.load(null, null); final CertificateFactory f = CertificateFactory.getInstance("X.509"); @@ -1285,7 +1497,8 @@ public class AndroidKeyStoreTest extends AndroidTestCase { assertPrivateKeyEntryEquals(actual, FAKE_KEY_1, FAKE_USER_1, FAKE_CA_1); } - public void testKeyStore_SetKeyEntry_Replaced_Success() throws Exception { + public void testKeyStore_SetKeyEntry_Replaced_Encrypted_Success() throws Exception { + setupPassword(); mKeyStore.load(null, null); final CertificateFactory f = CertificateFactory.getInstance("X.509"); @@ -1376,7 +1589,8 @@ public class AndroidKeyStoreTest extends AndroidTestCase { return cert; } - public void testKeyStore_SetKeyEntry_ReplacedChain_Success() throws Exception { + public void testKeyStore_SetKeyEntry_ReplacedChain_Encrypted_Success() throws Exception { + setupPassword(); mKeyStore.load(null, null); // Create key #1 @@ -1429,8 +1643,9 @@ public class AndroidKeyStoreTest extends AndroidTestCase { } } - public void testKeyStore_SetKeyEntry_ReplacedChain_DifferentPrivateKey_Failure() + public void testKeyStore_SetKeyEntry_ReplacedChain_DifferentPrivateKey_Encrypted_Failure() throws Exception { + setupPassword(); mKeyStore.load(null, null); // Create key #1 @@ -1472,7 +1687,48 @@ public class AndroidKeyStoreTest extends AndroidTestCase { } } - public void testKeyStore_Size_Success() throws Exception { + public void testKeyStore_SetKeyEntry_ReplacedChain_UnencryptedToEncrypted_Failure() + throws Exception { + mKeyStore.load(null, null); + + // Create key #1 + { + final String privateKeyAlias = Credentials.USER_PRIVATE_KEY + TEST_ALIAS_1; + assertTrue(mAndroidKeyStore.generate(privateKeyAlias, + android.security.KeyStore.UID_SELF, android.security.KeyStore.FLAG_NONE)); + + X509Certificate cert = + generateCertificate(mAndroidKeyStore, TEST_ALIAS_1, TEST_SERIAL_1, TEST_DN_1, + NOW, NOW_PLUS_10_YEARS); + + assertTrue(mAndroidKeyStore.put(Credentials.USER_CERTIFICATE + TEST_ALIAS_1, + cert.getEncoded(), android.security.KeyStore.UID_SELF, + android.security.KeyStore.FLAG_NONE)); + } + + // Replace with one that requires encryption + { + Entry entry = mKeyStore.getEntry(TEST_ALIAS_1, null); + + try { + mKeyStore.setEntry(TEST_ALIAS_1, entry, new AndroidKeyStoreParameter.Builder( + getContext()).setEncryptionRequired().build()); + fail("Should not allow setting of Entry without unlocked keystore"); + } catch (KeyStoreException success) { + } + + assertTrue(mAndroidKeyStore.password("1111")); + assertTrue(mAndroidKeyStore.isUnlocked()); + + mKeyStore.setEntry(TEST_ALIAS_1, entry, + new AndroidKeyStoreParameter.Builder(getContext()) + .setEncryptionRequired() + .build()); + } + } + + public void testKeyStore_Size_Encrypted_Success() throws Exception { + setupPassword(); mKeyStore.load(null, null); assertTrue(mAndroidKeyStore.put(Credentials.CA_CERTIFICATE + TEST_ALIAS_1, FAKE_CA_1)); @@ -1501,7 +1757,8 @@ public class AndroidKeyStoreTest extends AndroidTestCase { assertAliases(new String[] { TEST_ALIAS_2 }); } - public void testKeyStore_Store_LoadStoreParam_Failure() throws Exception { + public void testKeyStore_Store_LoadStoreParam_Encrypted_Failure() throws Exception { + setupPassword(); mKeyStore.load(null, null); try { @@ -1511,7 +1768,7 @@ public class AndroidKeyStoreTest extends AndroidTestCase { } } - public void testKeyStore_Load_InputStreamSupplied_Failure() throws Exception { + public void testKeyStore_Load_InputStreamSupplied_Encrypted_Failure() throws Exception { byte[] buf = "FAKE KEYSTORE".getBytes(); ByteArrayInputStream is = new ByteArrayInputStream(buf); @@ -1522,7 +1779,7 @@ public class AndroidKeyStoreTest extends AndroidTestCase { } } - public void testKeyStore_Load_PasswordSupplied_Failure() throws Exception { + public void testKeyStore_Load_PasswordSupplied_Encrypted_Failure() throws Exception { try { mKeyStore.load(null, "password".toCharArray()); fail("Should throw IllegalArgumentException when password is supplied"); @@ -1530,7 +1787,8 @@ public class AndroidKeyStoreTest extends AndroidTestCase { } } - public void testKeyStore_Store_OutputStream_Failure() throws Exception { + public void testKeyStore_Store_OutputStream_Encrypted_Failure() throws Exception { + setupPassword(); mKeyStore.load(null, null); OutputStream sink = new ByteArrayOutputStream(); @@ -1558,7 +1816,8 @@ public class AndroidKeyStoreTest extends AndroidTestCase { cert.getEncoded())); } - public void testKeyStore_KeyOperations_Wrap_Success() throws Exception { + public void testKeyStore_KeyOperations_Wrap_Encrypted_Success() throws Exception { + setupPassword(); mKeyStore.load(null, null); setupKey(); diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java index a296d34..72aa203 100644 --- a/services/java/com/android/server/InputMethodManagerService.java +++ b/services/java/com/android/server/InputMethodManagerService.java @@ -1493,7 +1493,9 @@ public class InputMethodManagerService extends IInputMethodManager.Stub if (mStatusBar != null) { mStatusBar.setImeWindowStatus(token, vis, backDisposition); } - final boolean iconVisibility = (vis & InputMethodService.IME_ACTIVE) != 0; + final boolean iconVisibility = ((vis & (InputMethodService.IME_ACTIVE)) != 0) + && (mWindowManagerService.isHardKeyboardAvailable() + || (vis & (InputMethodService.IME_VISIBLE)) != 0); final InputMethodInfo imi = mMethodMap.get(mCurMethodId); if (imi != null && iconVisibility && needsToShowImeSwitchOngoingNotification()) { // Used to load label diff --git a/services/java/com/android/server/pm/PackageManagerService.java b/services/java/com/android/server/pm/PackageManagerService.java index c16f0ee..92d1b89 100644 --- a/services/java/com/android/server/pm/PackageManagerService.java +++ b/services/java/com/android/server/pm/PackageManagerService.java @@ -8221,16 +8221,10 @@ public class PackageManagerService extends IPackageManager.Stub { mSettings.writeLPr(); } } - // A user ID was deleted here. Go through all users and remove it from - // KeyStore. - final int appId = outInfo.removedAppId; - if (appId != -1) { - final KeyStore keyStore = KeyStore.getInstance(); - if (keyStore != null) { - for (final int userId : sUserManager.getUserIds()) { - keyStore.clearUid(UserHandle.getUid(userId, appId)); - } - } + if (outInfo != null) { + // A user ID was deleted here. Go through all users and remove it + // from KeyStore. + removeKeystoreDataIfNeeded(UserHandle.USER_ALL, outInfo.removedAppId); } } @@ -8369,6 +8363,7 @@ public class PackageManagerService extends IPackageManager.Stub { outInfo.removedUsers = new int[] {removeUser}; } mInstaller.clearUserData(packageName, removeUser); + removeKeystoreDataIfNeeded(removeUser, appId); schedulePackageCleaning(packageName, removeUser, false); return true; } @@ -8520,29 +8515,34 @@ public class PackageManagerService extends IPackageManager.Stub { } PackageParser.Package p; boolean dataOnly = false; + final int appId; synchronized (mPackages) { p = mPackages.get(packageName); - if(p == null) { + if (p == null) { dataOnly = true; PackageSetting ps = mSettings.mPackages.get(packageName); - if((ps == null) || (ps.pkg == null)) { - Slog.w(TAG, "Package named '" + packageName +"' doesn't exist."); + if ((ps == null) || (ps.pkg == null)) { + Slog.w(TAG, "Package named '" + packageName + "' doesn't exist."); return false; } p = ps.pkg; } - } - - if (!dataOnly) { - //need to check this only for fully installed applications - if (p == null) { - Slog.w(TAG, "Package named '" + packageName +"' doesn't exist."); - return false; + if (!dataOnly) { + // need to check this only for fully installed applications + if (p == null) { + Slog.w(TAG, "Package named '" + packageName + "' doesn't exist."); + return false; + } + final ApplicationInfo applicationInfo = p.applicationInfo; + if (applicationInfo == null) { + Slog.w(TAG, "Package " + packageName + " has no applicationInfo."); + return false; + } } - final ApplicationInfo applicationInfo = p.applicationInfo; - if (applicationInfo == null) { - Slog.w(TAG, "Package " + packageName + " has no applicationInfo."); - return false; + if (p != null && p.applicationInfo != null) { + appId = p.applicationInfo.uid; + } else { + appId = -1; } } int retCode = mInstaller.clearUserData(packageName, userId); @@ -8551,9 +8551,33 @@ public class PackageManagerService extends IPackageManager.Stub { + packageName); return false; } + removeKeystoreDataIfNeeded(userId, appId); return true; } + /** + * Remove entries from the keystore daemon. Will only remove it if the + * {@code appId} is valid. + */ + private static void removeKeystoreDataIfNeeded(int userId, int appId) { + if (appId < 0) { + return; + } + + final KeyStore keyStore = KeyStore.getInstance(); + if (keyStore != null) { + if (userId == UserHandle.USER_ALL) { + for (final int individual : sUserManager.getUserIds()) { + keyStore.clearUid(UserHandle.getUid(individual, appId)); + } + } else { + keyStore.clearUid(UserHandle.getUid(userId, appId)); + } + } else { + Slog.w(TAG, "Could not contact keystore to clear entries for app id " + appId); + } + } + public void deleteApplicationCacheFiles(final String packageName, final IPackageDataObserver observer) { mContext.enforceCallingOrSelfPermission( diff --git a/services/java/com/android/server/pm/SELinuxMMAC.java b/services/java/com/android/server/pm/SELinuxMMAC.java index 3e507bd..04f43d9 100644 --- a/services/java/com/android/server/pm/SELinuxMMAC.java +++ b/services/java/com/android/server/pm/SELinuxMMAC.java @@ -58,7 +58,6 @@ public final class SELinuxMMAC { // Locations of potential install policy files. private static final File[] INSTALL_POLICY_FILE = { new File(Environment.getDataDirectory(), "security/mac_permissions.xml"), - new File(Environment.getDataDirectory(), "system/mac_permissions.xml"), new File(Environment.getRootDirectory(), "etc/security/mac_permissions.xml"), null}; diff --git a/tools/aapt/Main.cpp b/tools/aapt/Main.cpp index f398de0..74ffeb0 100644 --- a/tools/aapt/Main.cpp +++ b/tools/aapt/Main.cpp @@ -48,6 +48,7 @@ void usage(void) " List contents of Zip-compatible archive.\n\n", gProgName); fprintf(stderr, " %s d[ump] [--values] WHAT file.{apk} [asset [asset ...]]\n" + " strings Print the contents of the resource table string pool in the APK.\n" " badging Print the label and icon for the app declared in APK.\n" " permissions Print the permissions from the APK.\n" " resources Print the resource table from the APK.\n" |