diff options
author | Kenny Root <kroot@google.com> | 2013-04-10 11:30:58 -0700 |
---|---|---|
committer | Kenny Root <kroot@android.com> | 2013-04-15 19:33:15 +0000 |
commit | bf2147669e295384df17b50afc53a4d450b05bdd (patch) | |
tree | 594236183b0a773b440972214b5dc1ddc62eada1 /keystore/java | |
parent | 6fb172b12eefeea4b565c616a4d0a8d1fb015217 (diff) | |
download | frameworks_base-bf2147669e295384df17b50afc53a4d450b05bdd.zip frameworks_base-bf2147669e295384df17b50afc53a4d450b05bdd.tar.gz frameworks_base-bf2147669e295384df17b50afc53a4d450b05bdd.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.
(partial cherry pick from commit 2eeda7286f3c7cb79f7eb71ae6464cad213d12a3)
Bug: 8122243
Change-Id: I5ecd9251ec79ec53a3b68c0fff8dfba10873e36e
Diffstat (limited to 'keystore/java')
6 files changed, 272 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 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 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) { |