Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit eedda45a authored by Alex Klyubin's avatar Alex Klyubin
Browse files

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
parent 3974fb23
Loading
Loading
Loading
Loading
+53 −13
Original line number Diff line number Diff line
@@ -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
 * <a href="{@docRoot}training/articles/keystore.html">Android KeyStore facility</a>.
 *
 * <p>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.
 *
 * <p>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}.
 * {@link AlgorithmParameterSpec} for initializing a {@link KeyGenerator} of the
 * <a href="{@docRoot}training/articles/keystore.html">Android KeyStore facility</a>. 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.
 *
 * <p>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()}.
 *
 * <p>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)}.
 *
 * <p>NOTE: The key material of the keys generating using the {@code KeyGeneratorSpec} is not
 * accessible.
 *
 * <p><h3>Example</h3>
 * 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.
 * <pre> {@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);
 * }</pre>
 */
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).
         *
         * <p>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) {
+75 −26
Original line number Diff line number Diff line
@@ -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
 * <a href="{@docRoot}training/articles/keystore.html">Android KeyStore
 * facility</a>. 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.
 * <p>
 * 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.
 * <p>
 * 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}.
 * <p>
 * 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
 * <a href="{@docRoot}training/articles/keystore.html">Android KeyStore facility</a>. 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.
 *
 * <p>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()}.
 *
 * <p>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()}.
 *
 * <p>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).
 *
 * <p>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.
 *
 * <p><h3>Example</h3>
 * 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.
 * <pre> {@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();
 * }</pre>
 */
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).
         *
         * <p>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 {
         *
         * <p>This must be specified for all keys. There is no default.
         *
         * <p>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.
         *
         * <p><b>NOTE: This has currently no effect.
         */
        public Builder setPurposes(@KeyStoreKeyProperties.PurposeEnum int purposes) {
+38 −1
Original line number Diff line number Diff line
@@ -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 <a href="{@docRoot}training/articles/keystore.html">Android
 * KeyStore</a>.
 * KeyStore</a>. 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.
 *
 * <p><h3>Example: Symmetric Key</h3>
 * The following example illustrates how to obtain a {@link KeyStoreKeySpec} describing the provided
 * Android KeyStore {@link SecretKey}.
 * <pre> {@code
 * SecretKey key = ...; // Android KeyStore key
 *
 * SecretKeyFactory factory = SecretKeyFactory.getInstance(key.getAlgorithm(), "AndroidKeyStore");
 * KeyStoreKeySpec spec;
 * try &#123;
 *     spec = (KeyStoreKeySpec) factory.getKeySpec(key, KeyStoreKeySpec.class);
 * &#125; catch (InvalidKeySpecException e) &#123;
 *     // Not an Android KeyStore key.
 * &#125;
 * }</pre>
 *
 * <p><h3>Example: Private Key</h3>
 * The following example illustrates how to obtain a {@link KeyStoreKeySpec} describing the provided
 * Android KeyStore {@link PrivateKey}.
 * <pre> {@code
 * PrivateKey key = ...; // Android KeyStore key
 *
 * KeyFactory factory = KeyFactory.getInstance(key.getAlgorithm(), "AndroidKeyStore");
 * KeyStoreKeySpec spec;
 * try &#123;
 *     spec = factory.getKeySpec(key, KeyStoreKeySpec.class);
 * &#125; catch (InvalidKeySpecException e) &#123;
 *     // Not an Android KeyStore key.
 * &#125;
 * }</pre>
 */
public class KeyStoreKeySpec implements KeySpec {
    private final String mKeystoreAlias;
+85 −18
Original line number Diff line number Diff line
@@ -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
 * <a href="{@docRoot}training/articles/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}.
 * Parameters specifying how to secure and restrict the use of a key or key pair being imported into
 * the <a href="{@docRoot}training/articles/keystore.html">Android KeyStore facility</a>. 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.
 *
 * <p>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.
 *
 * <p>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()}.
 *
 * <p>NOTE: The key material of keys stored in the Android KeyStore is not accessible.
 *
 * <p><h3>Example: Symmetric Key</h3>
 * 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.
 * <pre> {@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.
 * }</pre>
 *
 * <p><h3>Example: Asymmetric Key Pair</h3>
 * 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.
 * <pre> {@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.
 * }</pre>
 */
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).
         *
         * <p>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) {