diff options
author | Kenny Root <kroot@google.com> | 2013-04-10 11:30:58 -0700 |
---|---|---|
committer | Kenny Root <kroot@google.com> | 2013-04-12 15:19:48 -0700 |
commit | 2eeda7286f3c7cb79f7eb71ae6464cad213d12a3 (patch) | |
tree | cf77426b72b1287ce66c230da7c67d2d5c8cdd8a /keystore/java | |
parent | a3788b00bb221e20abdd42f747d2af419e0a088c (diff) | |
download | frameworks_base-2eeda7286f3c7cb79f7eb71ae6464cad213d12a3.zip frameworks_base-2eeda7286f3c7cb79f7eb71ae6464cad213d12a3.tar.gz frameworks_base-2eeda7286f3c7cb79f7eb71ae6464cad213d12a3.tar.bz2 |
AndroidKeyStore: Add encrypted flag
Add the encrypted flag for the KeyPairGenerator and the KeyStore so that
applications can choose to allow entries when there is no lockscreen.
Bug: 8122243
Change-Id: Ia802afe965f2377ad3f282dab8c512388c705850
Diffstat (limited to 'keystore/java')
6 files changed, 270 insertions, 51 deletions
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 18225a5..b126f03 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,10 +46,10 @@ 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. */ -public class AndroidKeyPairGeneratorSpec implements AlgorithmParameterSpec { +public final class AndroidKeyPairGeneratorSpec implements AlgorithmParameterSpec { private final String mKeystoreAlias; private final Context mContext; @@ -63,6 +62,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 @@ -93,7 +94,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)) { @@ -116,51 +118,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} @@ -175,16 +198,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; @@ -197,6 +221,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"); @@ -266,6 +298,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 @@ -273,7 +316,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..44f57c4 --- /dev/null +++ b/keystore/java/android/security/AndroidKeyStoreParameter.java @@ -0,0 +1,123 @@ +/* + * 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. + */ +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 309d3d3..45385ee 100644 --- a/keystore/java/android/security/KeyStore.java +++ b/keystore/java/android/security/KeyStore.java @@ -40,7 +40,11 @@ public class KeyStore { public static final int UNDEFINED_ACTION = 9; public static final int WRONG_PASSWORD = 10; - // Flags for "put" and "import" + // 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 @@ -104,7 +108,7 @@ public class KeyStore { } 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) { @@ -117,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) { @@ -130,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) { @@ -143,7 +147,7 @@ public class KeyStore { } public String[] saw(String prefix) { - return saw(prefix, -1); + return saw(prefix, UID_SELF); } public boolean reset() { @@ -206,7 +210,7 @@ public class KeyStore { } public boolean generate(String key) { - return generate(key, -1); + return generate(key, UID_SELF); } public boolean importKey(String keyName, byte[] key, int uid, int flags) { @@ -223,7 +227,7 @@ public class KeyStore { } public boolean importKey(String keyName, byte[] key) { - return importKey(keyName, key, -1); + return importKey(keyName, key, UID_SELF); } public byte[] getPubkey(String key) { @@ -245,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) { |