From eedda45ad7d829b4d65936d33e8aa6fa9c9c1ecd Mon Sep 17 00:00:00 2001 From: Alex Klyubin Date: Thu, 7 May 2015 17:34:24 -0700 Subject: More Javadocs for AndroidKeyStore public classes. This adds more detailed class-level Javadocs (incl. examples) for the following public API of Android KeyStore facility: * KeyPairGeneratorSpec, * KeyGeneratorSpec, * KeyStoreParameter, * KeyStoreKeySpec. This also clarifies what encryption at rest means. Bug: 18088752 Change-Id: I9951a528c34dea322534763b596902a2b6ac64f9 --- .../java/android/security/KeyGeneratorSpec.java | 66 ++++++++++--- .../android/security/KeyPairGeneratorSpec.java | 101 ++++++++++++++------ .../java/android/security/KeyStoreKeySpec.java | 39 +++++++- .../java/android/security/KeyStoreParameter.java | 103 +++++++++++++++++---- 4 files changed, 251 insertions(+), 58 deletions(-) (limited to 'keystore/java/android') diff --git a/keystore/java/android/security/KeyGeneratorSpec.java b/keystore/java/android/security/KeyGeneratorSpec.java index 729646d..5a10a0a 100644 --- a/keystore/java/android/security/KeyGeneratorSpec.java +++ b/keystore/java/android/security/KeyGeneratorSpec.java @@ -16,6 +16,7 @@ package android.security; +import android.app.KeyguardManager; import android.content.Context; import android.text.TextUtils; @@ -24,19 +25,53 @@ import java.util.Date; import javax.crypto.Cipher; import javax.crypto.KeyGenerator; -import javax.crypto.SecretKey; /** - * {@link AlgorithmParameterSpec} for initializing a {@code KeyGenerator} that works with - * Android KeyStore facility. + * {@link AlgorithmParameterSpec} for initializing a {@link KeyGenerator} of the + * Android KeyStore facility. This class + * specifies whether user authentication is required for using the key, what uses the key is + * authorized for (e.g., only in {@code CBC} mode), whether the key should be encrypted at rest, the + * key's and validity start and end dates. * - *

The Android KeyStore facility is accessed through a {@link KeyGenerator} 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. + *

To generate a key, create an instance of this class using the {@link Builder}, initialize a + * {@code KeyGenerator} of the desired key type (e.g., {@code AES} or {@code HmacSHA256}) from the + * {@code AndroidKeyStore} provider with the {@code KeyGeneratorSpec} instance, and then generate a + * key using {@link KeyGenerator#generateKey()}. * - *

After generation, the {@code keyStoreAlias} is used with the - * {@link java.security.KeyStore#getEntry(String, java.security.KeyStore.ProtectionParameter)} - * interface to retrieve the {@link SecretKey}. + *

The generated key will be returned by the {@code KeyGenerator} and also stored in the Android + * KeyStore under the alias specified in this {@code KeyGeneratorSpec}. To obtain the key from the + * Android KeyStore use + * {@link java.security.KeyStore#getKey(String, char[]) KeyStore.getKey(String, null)} or + * {@link java.security.KeyStore#getEntry(String, java.security.KeyStore.ProtectionParameter) KeyStore.getEntry(String, null)}. + * + *

NOTE: The key material of the keys generating using the {@code KeyGeneratorSpec} is not + * accessible. + * + *

Example

+ * The following example illustrates how to generate an HMAC key in the Android KeyStore under alias + * {@code key1} authorized to be used only for HMAC with SHA-256 digest and only if the user has + * been authenticated within the last five minutes. + *
 {@code
+ * KeyGenerator keyGenerator = KeyGenerator.getInstance(
+ *         KeyStoreKeyProperties.Algorithm.HMAC_SHA256,
+ *         "AndroidKeyStore");
+ * keyGenerator.initialize(
+ *         new KeyGeneratorSpec.Builder(context)
+ *                 .setAlias("key1")
+ *                 .setPurposes(KeyStoreKeyProperties.Purpose.SIGN
+ *                         | KeyStoreKeyProperties.Purpose.VERIFY)
+ *                 // Only permit this key to be used if the user authenticated
+ *                 // within the last five minutes.
+ *                 .setUserAuthenticationRequired(true)
+ *                 .setUserAuthenticationValidityDurationSeconds(5 * 60)
+ *                 .build());
+ * SecretKey key = keyGenerator.generateKey();
+ *
+ * // The key can also be obtained from the Android KeyStore any time as follows:
+ * KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
+ * keyStore.load(null);
+ * SecretKey key = (SecretKey) keyStore.getKey("key1", null);
+ * }
*/ public class KeyGeneratorSpec implements AlgorithmParameterSpec { @@ -207,7 +242,8 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec { } /** - * Returns {@code true} if the key must be encrypted in the {@link java.security.KeyStore}. + * Returns {@code true} if the key must be encrypted at rest. This will protect the key with the + * secure lock screen credential (e.g., password, PIN, or pattern). */ public boolean isEncryptionRequired() { return (mFlags & KeyStore.FLAG_ENCRYPTED) != 0; @@ -266,9 +302,13 @@ public class KeyGeneratorSpec 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. + * Indicates that this key must be encrypted at rest. This will protect the key with the + * secure lock screen credential (e.g., password, PIN, or pattern). + * + *

Note that this feature requires that the secure lock screen (e.g., password, PIN, + * pattern) is set up. Otherwise key generation will fail. + * + * @see KeyguardManager#isDeviceSecure() */ public Builder setEncryptionRequired(boolean required) { if (required) { diff --git a/keystore/java/android/security/KeyPairGeneratorSpec.java b/keystore/java/android/security/KeyPairGeneratorSpec.java index 25c61fd..1fcb355 100644 --- a/keystore/java/android/security/KeyPairGeneratorSpec.java +++ b/keystore/java/android/security/KeyPairGeneratorSpec.java @@ -16,10 +16,12 @@ package android.security; +import android.app.KeyguardManager; import android.content.Context; import android.text.TextUtils; import java.math.BigInteger; +import java.security.KeyPairGenerator; import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; import java.security.cert.Certificate; @@ -29,26 +31,64 @@ import java.util.Date; import javax.security.auth.x500.X500Principal; /** - * This provides the required parameters needed for initializing the - * {@code KeyPairGenerator} that works with - * Android KeyStore - * facility. The Android KeyStore facility is accessed through a - * {@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. - *

- * After generation, the {@code keyStoreAlias} is used with the - * {@link java.security.KeyStore#getEntry(String, java.security.KeyStore.ProtectionParameter)} - * interface to retrieve the {@link PrivateKey} and its associated - * {@link Certificate} chain. - *

- * The KeyPair generator will create a self-signed certificate with the subject - * as its X.509v3 Subject Distinguished Name and as its X.509v3 Issuer - * Distinguished Name along with the other parameters specified with the - * {@link Builder}. - *

- * The self-signed X.509 certificate may be replaced at a later time by a - * certificate signed by a real Certificate Authority. + * {@link AlgorithmParameterSpec} for initializing a {@link KeyPairGenerator} of the + * Android KeyStore facility. This class + * specifies whether user authentication is required for using the private key, what uses the + * private key is authorized for (e.g., only for signing -- decryption not permitted), whether the + * private key should be encrypted at rest, the private key's and validity start and end dates. + * + *

To generate a key pair, create an instance of this class using the {@link Builder}, initialize + * a {@code KeyPairGenerator} of the desired key type (e.g., {@code EC} or {@code RSA}) from the + * {@code AndroidKeyStore} provider with the {@code KeyPairGeneratorSpec} instance, and then + * generate a key pair using {@link KeyPairGenerator#generateKeyPair()}. + * + *

The generated key pair will be returned by the {@code KeyPairGenerator} and also stored in the + * Android KeyStore under the alias specified in this {@code KeyPairGeneratorSpec}. To obtain the + * private key from the Android KeyStore use + * {@link java.security.KeyStore#getKey(String, char[]) KeyStore.getKey(String, null)} or + * {@link java.security.KeyStore#getEntry(String, java.security.KeyStore.ProtectionParameter) KeyStore.getEntry(String, null)}. + * To obtain the public key from the Android KeyStore use + * {@link java.security.KeyStore#getCertificate(String)} and then + * {@link Certificate#getPublicKey()}. + * + *

A self-signed X.509 certificate will be also generated and stored in the Android KeyStore. + * This is because the {@link java.security.KeyStore} abstraction does not support storing key pairs + * without a certificate. The subject, serial number, and validity dates of the certificate can be + * specified in this {@code KeyPairGeneratorSpec}. The self-signed certificate may be replaced at a + * later time by a certificate signed by a Certificate Authority (CA). + * + *

NOTE: The key material of the private keys generating using the {@code KeyPairGeneratorSpec} + * is not accessible. The key material of the public keys is accessible. + * + *

Example

+ * The following example illustrates how to generate an EC key pair in the Android KeyStore under + * alias {@code key2} authorized to be used only for signing using SHA-256, SHA-384, or SHA-512 + * digest and only if the user has been authenticated within the last five minutes. + *
 {@code
+ * KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(
+ *         KeyStoreKeyProperties.Algorithm.EC,
+ *         "AndroidKeyStore");
+ * keyPairGenerator.initialize(
+ *         new KeyGeneratorSpec.Builder(context)
+ *                 .setAlias("key2")
+ *                 .setPurposes(KeyStoreKeyProperties.Purpose.SIGN
+ *                         | KeyStoreKeyProperties.Purpose.VERIFY)
+ *                 .setDigests(KeyStoreKeyProperties.Digest.SHA256
+ *                         | KeyStoreKeyProperties.Digest.SHA384
+ *                         | KeyStoreKeyProperties.Digest.SHA512)
+ *                 // Only permit this key to be used if the user authenticated
+ *                 // within the last five minutes.
+ *                 .setUserAuthenticationRequired(true)
+ *                 .setUserAuthenticationValidityDurationSeconds(5 * 60)
+ *                 .build());
+ * KeyPair keyPair = keyPairGenerator.generateKey();
+ *
+ * // The key pair can also be obtained from the Android KeyStore any time as follows:
+ * KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
+ * keyStore.load(null);
+ * PrivateKey privateKey = (PrivateKey) keyStore.getKey("key2", null);
+ * PublicKey publicKey = keyStore.getCertificate("key2").getPublicKey();
+ * }
*/ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { @@ -307,8 +347,8 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { } /** - * Returns {@code true} if this parameter will require generated keys to be - * encrypted in the {@link java.security.KeyStore}. + * Returns {@code true} if the key must be encrypted at rest. This will protect the key pair + * with the secure lock screen credential (e.g., password, PIN, or pattern). */ public boolean isEncryptionRequired() { return (mFlags & KeyStore.FLAG_ENCRYPTED) != 0; @@ -614,10 +654,13 @@ public final class KeyPairGeneratorSpec 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. + * Indicates that this key must be encrypted at rest. This will protect the key pair with + * the secure lock screen credential (e.g., password, PIN, or pattern). + * + *

Note that this feature requires that the secure lock screen (e.g., password, PIN, + * pattern) is set up. Otherwise key pair generation will fail. + * + * @see KeyguardManager#isDeviceSecure() */ public Builder setEncryptionRequired() { mFlags |= KeyStore.FLAG_ENCRYPTED; @@ -689,6 +732,12 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { * *

This must be specified for all keys. There is no default. * + *

If the set of purposes for which the key can be used does not contain + * {@link KeyStoreKeyProperties.Purpose#SIGN}, the self-signed certificate generated by + * {@link KeyPairGenerator} of {@code AndroidKeyStore} provider will contain an invalid + * signature. This is OK if the certificate is only used for obtaining the public key from + * Android KeyStore. + * *

NOTE: This has currently no effect. */ public Builder setPurposes(@KeyStoreKeyProperties.PurposeEnum int purposes) { diff --git a/keystore/java/android/security/KeyStoreKeySpec.java b/keystore/java/android/security/KeyStoreKeySpec.java index a630a0a..0a9acbb 100644 --- a/keystore/java/android/security/KeyStoreKeySpec.java +++ b/keystore/java/android/security/KeyStoreKeySpec.java @@ -16,12 +16,49 @@ package android.security; +import java.security.PrivateKey; import java.security.spec.KeySpec; import java.util.Date; +import javax.crypto.SecretKey; + /** * Information about a key from the Android - * KeyStore. + * KeyStore. This class describes whether the key material is available in + * plaintext outside of secure hardware, whether user authentication is required for using the key + * and whether this requirement is enforced by secure hardware, the key's origin, what uses the key + * is authorized for (e.g., only in {@code CBC} mode, or signing only), whether the key should be + * encrypted at rest, the key's and validity start and end dates. + * + *

Example: Symmetric Key

+ * The following example illustrates how to obtain a {@link KeyStoreKeySpec} describing the provided + * Android KeyStore {@link SecretKey}. + *
 {@code
+ * SecretKey key = ...; // Android KeyStore key
+ *
+ * SecretKeyFactory factory = SecretKeyFactory.getInstance(key.getAlgorithm(), "AndroidKeyStore");
+ * KeyStoreKeySpec spec;
+ * try {
+ *     spec = (KeyStoreKeySpec) factory.getKeySpec(key, KeyStoreKeySpec.class);
+ * } catch (InvalidKeySpecException e) {
+ *     // Not an Android KeyStore key.
+ * }
+ * }
+ * + *

Example: Private Key

+ * The following example illustrates how to obtain a {@link KeyStoreKeySpec} describing the provided + * Android KeyStore {@link PrivateKey}. + *
 {@code
+ * PrivateKey key = ...; // Android KeyStore key
+ *
+ * KeyFactory factory = KeyFactory.getInstance(key.getAlgorithm(), "AndroidKeyStore");
+ * KeyStoreKeySpec spec;
+ * try {
+ *     spec = factory.getKeySpec(key, KeyStoreKeySpec.class);
+ * } catch (InvalidKeySpecException e) {
+ *     // Not an Android KeyStore key.
+ * }
+ * }
*/ public class KeyStoreKeySpec implements KeySpec { private final String mKeystoreAlias; diff --git a/keystore/java/android/security/KeyStoreParameter.java b/keystore/java/android/security/KeyStoreParameter.java index 8d7a19f..ea5ca71 100644 --- a/keystore/java/android/security/KeyStoreParameter.java +++ b/keystore/java/android/security/KeyStoreParameter.java @@ -16,27 +16,89 @@ package android.security; +import android.app.KeyguardManager; import android.content.Context; import java.security.Key; import java.security.KeyStore.ProtectionParameter; +import java.security.cert.Certificate; import java.util.Date; import javax.crypto.Cipher; /** - * Parameters specifying how to secure and restrict the use of a key being - * imported into the - * Android KeyStore - * facility. 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. - *

- * 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}. + * Parameters specifying how to secure and restrict the use of a key or key pair being imported into + * the Android KeyStore facility. This class + * specifies whether user authentication is required for using the key, what uses the key is + * authorized for (e.g., only in {@code CTR} mode, or only for signing -- decryption not permitted), + * whether the key should be encrypted at rest, the key's and validity start and end dates. + * + *

To import a key or key pair into the Android KeyStore, create an instance of this class using + * the {@link Builder} and pass the instance into {@link java.security.KeyStore#setEntry(String, java.security.KeyStore.Entry, ProtectionParameter) KeyStore.setEntry} + * with the key or key pair being imported. + * + *

To obtain the secret/symmetric or private key from the Android KeyStore use + * {@link java.security.KeyStore#getKey(String, char[]) KeyStore.getKey(String, null)} or + * {@link java.security.KeyStore#getEntry(String, java.security.KeyStore.ProtectionParameter) KeyStore.getEntry(String, null)}. + * To obtain the public key from the Android KeyStore use + * {@link java.security.KeyStore#getCertificate(String)} and then + * {@link Certificate#getPublicKey()}. + * + *

NOTE: The key material of keys stored in the Android KeyStore is not accessible. + * + *

Example: Symmetric Key

+ * The following example illustrates how to import an AES key into the Android KeyStore under alias + * {@code key1} authorized to be used only for encryption/decryption in CBC mode with PKCS#7 + * padding. The key must export its key material via {@link Key#getEncoded()} in {@code RAW} format. + *
 {@code
+ * SecretKey key = ...; // AES key
+ *
+ * KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
+ * keyStore.load(null);
+ * keyStore.setEntry(
+ *         "key1",
+ *         new KeyStore.SecretKeyEntry(key),
+ *         new KeyStoreParameter.Builder(context)
+ *                 .setPurposes(KeyStoreKeyProperties.Purpose.ENCRYPT
+ *                         | KeyStoreKeyProperties.Purpose.DECRYPT)
+ *                 .setBlockMode(KeyStoreKeyProperties.BlockMode.CBC)
+ *                 .setEncryptionPaddings(
+ *                         KeyStoreKeyProperties.EncryptionPaddings.PKCS7)
+ *                 .build());
+ * // Key imported, obtain a reference to it.
+ * SecretKey keyStoreKey = (SecretKey) keyStore.getKey("key1", null);
+ * // The original key can now be thrown away.
+ * }
+ * + *

Example: Asymmetric Key Pair

+ * The following example illustrates how to import an EC key pair into the Android KeyStore under + * alias {@code key2} authorized to be used only for signing with SHA-256 digest and only if + * the user has been authenticated within the last ten minutes. Both the private and the public key + * must export their key material via {@link Key#getEncoded()} in {@code PKCS#8} and {@code X.509} + * format respectively. + *
 {@code
+ * PrivateKey privateKey = ...;   // EC private key
+ * Certificate[] certChain = ...; // Certificate chain with the first certificate
+ *                                // containing the corresponding EC public key.
+ *
+ * KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
+ * keyStore.load(null);
+ * keyStore.setEntry(
+ *         "key2",
+ *         new KeyStore.PrivateKeyEntry(privateKey, certChain),
+ *         new KeyStoreParameter.Builder(context)
+ *                 .setPurposes(KeyStoreKeyProperties.Purpose.SIGN)
+ *                 .setDigests(KeyStoreKeyProperties.Digest.SHA256)
+ *                 // Only permit this key to be used if the user
+ *                 // authenticated within the last ten minutes.
+ *                 .setUserAuthenticationRequired(true)
+ *                 .setUserAuthenticationValidityDurationSeconds(10 * 60)
+ *                 .build());
+ * // Key pair imported, obtain a reference to it.
+ * PrivateKey keyStorePrivateKey = (PrivateKey) keyStore.getKey("key2", null);
+ * PublicKey publicKey = keyStore.getCertificate("key2").getPublicKey();
+ * // The original private key can now be thrown away.
+ * }
*/ public final class KeyStoreParameter implements ProtectionParameter { private final Context mContext; @@ -107,8 +169,9 @@ public final class KeyStoreParameter implements ProtectionParameter { } /** - * Returns {@code true} if this parameter requires entries to be encrypted - * on the disk. + * Returns {@code true} if the {@link java.security.KeyStore} entry must be encrypted at rest. + * This will protect the entry with the secure lock screen credential (e.g., password, PIN, or + * pattern). */ public boolean isEncryptionRequired() { return (mFlags & KeyStore.FLAG_ENCRYPTED) != 0; @@ -275,10 +338,14 @@ public final class KeyStoreParameter implements ProtectionParameter { } /** - * 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. + * Indicates that this {@link java.security.KeyStore} entry must be encrypted at rest. This + * will protect the entry with the secure lock screen credential (e.g., password, PIN, or + * pattern). + * + *

Note that enabling this feature requires that the secure lock screen (e.g., password, + * PIN, pattern) is set up. Otherwise setting the {@code KeyStore} entry will fail. + * + * @see KeyguardManager#isDeviceSecure() */ public Builder setEncryptionRequired(boolean required) { if (required) { -- cgit v1.1