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

Commit bf214766 authored by Kenny Root's avatar Kenny Root Committed by Kenny Root
Browse files

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 2eeda728)

Bug: 8122243
Change-Id: I5ecd9251ec79ec53a3b68c0fff8dfba10873e36e
parent 6fb172b1
Loading
Loading
Loading
Loading
+12 −5
Original line number Original line Diff line number Diff line
@@ -49,10 +49,7 @@ import java.security.spec.X509EncodedKeySpec;
 *
 *
 * {@hide}
 * {@hide}
 */
 */
@SuppressWarnings("deprecation")
public class AndroidKeyPairGenerator extends KeyPairGeneratorSpi {
public class AndroidKeyPairGenerator extends KeyPairGeneratorSpi {
    public static final String NAME = "AndroidKeyPairGenerator";

    private android.security.KeyStore mKeyStore;
    private android.security.KeyStore mKeyStore;


    private AndroidKeyPairGeneratorSpec mSpec;
    private AndroidKeyPairGeneratorSpec mSpec;
@@ -79,12 +76,21 @@ public class AndroidKeyPairGenerator extends KeyPairGeneratorSpi {
                    "Must call initialize with an AndroidKeyPairGeneratorSpec first");
                    "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();
        final String alias = mSpec.getKeystoreAlias();


        Credentials.deleteAllTypesForAlias(mKeyStore, alias);
        Credentials.deleteAllTypesForAlias(mKeyStore, alias);


        final String privateKeyAlias = Credentials.USER_PRIVATE_KEY + 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 PrivateKey privKey;
        final OpenSSLEngine engine = OpenSSLEngine.getInstance("keystore");
        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);
            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);
            Credentials.deleteAllTypesForAlias(mKeyStore, alias);
            throw new IllegalStateException("Can't store certificate in AndroidKeyStore");
            throw new IllegalStateException("Can't store certificate in AndroidKeyStore");
        }
        }
+72 −29
Original line number Original line Diff line number Diff line
@@ -32,10 +32,9 @@ import javax.security.auth.x500.X500Principal;
 * {@code KeyPairGenerator} that works with <a href="{@docRoot}
 * {@code KeyPairGenerator} that works with <a href="{@docRoot}
 * guide/topics/security/keystore.html">Android KeyStore facility</a>. The
 * guide/topics/security/keystore.html">Android KeyStore facility</a>. The
 * Android KeyStore facility is accessed through a
 * Android KeyStore facility is accessed through a
 * {@link java.security.KeyPairGenerator} API using the
 * {@link java.security.KeyPairGenerator} API using the {@code AndroidKeyStore}
 * {@code AndroidKeyPairGenerator} provider. The {@code context} passed in may
 * provider. The {@code context} passed in may be used to pop up some UI to ask
 * be used to pop up some UI to ask the user to unlock or initialize the Android
 * the user to unlock or initialize the Android KeyStore facility.
 * keystore facility.
 * <p>
 * <p>
 * After generation, the {@code keyStoreAlias} is used with the
 * After generation, the {@code keyStoreAlias} is used with the
 * {@link java.security.KeyStore#getEntry(String, java.security.KeyStore.ProtectionParameter)}
 * {@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
 * Distinguished Name along with the other parameters specified with the
 * {@link Builder}.
 * {@link Builder}.
 * <p>
 * <p>
 * The self-signed certificate may be replaced at a later time by a certificate
 * The self-signed X.509 certificate may be replaced at a later time by a
 * signed by a real Certificate Authority.
 * certificate signed by a real Certificate Authority.
 *
 *
 * @hide
 * @hide
 */
 */
public class AndroidKeyPairGeneratorSpec implements AlgorithmParameterSpec {
public final class AndroidKeyPairGeneratorSpec implements AlgorithmParameterSpec {
    private final String mKeystoreAlias;
    private final String mKeystoreAlias;


    private final Context mContext;
    private final Context mContext;
@@ -65,6 +64,8 @@ public class AndroidKeyPairGeneratorSpec implements AlgorithmParameterSpec {


    private final Date mEndDate;
    private final Date mEndDate;


    private final int mFlags;

    /**
    /**
     * Parameter specification for the "{@code AndroidKeyPairGenerator}"
     * Parameter specification for the "{@code AndroidKeyPairGenerator}"
     * instance of the {@link java.security.KeyPairGenerator} API. The
     * instance of the {@link java.security.KeyPairGenerator} API. The
@@ -95,7 +96,8 @@ public class AndroidKeyPairGeneratorSpec implements AlgorithmParameterSpec {
     * @hide should be built with AndroidKeyPairGeneratorSpecBuilder
     * @hide should be built with AndroidKeyPairGeneratorSpecBuilder
     */
     */
    public AndroidKeyPairGeneratorSpec(Context context, String keyStoreAlias,
    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) {
        if (context == null) {
            throw new IllegalArgumentException("context == null");
            throw new IllegalArgumentException("context == null");
        } else if (TextUtils.isEmpty(keyStoreAlias)) {
        } else if (TextUtils.isEmpty(keyStoreAlias)) {
@@ -118,50 +120,71 @@ public class AndroidKeyPairGeneratorSpec implements AlgorithmParameterSpec {
        mSerialNumber = serialNumber;
        mSerialNumber = serialNumber;
        mStartDate = startDate;
        mStartDate = startDate;
        mEndDate = endDate;
        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;
        return mKeystoreAlias;
    }
    }


    /**
    /**
     * @hide
     * Gets the Android context used for operations with this instance.
     */
     */
    Context getContext() {
    public Context getContext() {
        return mContext;
        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;
        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;
        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;
        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;
        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.
     * Builder class for {@link AndroidKeyPairGeneratorSpec} objects.
     * <p>
     * <p>
@@ -177,16 +200,17 @@ public class AndroidKeyPairGeneratorSpec implements AlgorithmParameterSpec {
     * Calendar end = new Calendar();
     * Calendar end = new Calendar();
     * end.add(1, Calendar.YEAR);
     * end.add(1, Calendar.YEAR);
     *
     *
     * AndroidKeyPairGeneratorSpec spec = new AndroidKeyPairGeneratorSpec.Builder(mContext)
     * AndroidKeyPairGeneratorSpec spec =
     *         .setAlias("myKey")
     *         new AndroidKeyPairGeneratorSpec.Builder(mContext)
     *         .setSubject(new X500Principal("CN=myKey"))
     *                 .setAlias(&quot;myKey&quot;)
     *                 .setSubject(new X500Principal(&quot;CN=myKey&quot;))
     *                 .setSerial(BigInteger.valueOf(1337))
     *                 .setSerial(BigInteger.valueOf(1337))
     *                 .setStartDate(start.getTime())
     *                 .setStartDate(start.getTime())
     *                 .setEndDate(end.getTime())
     *                 .setEndDate(end.getTime())
     *                 .build();
     *                 .build();
     * </pre>
     * </pre>
     */
     */
    public static class Builder {
    public final static class Builder {
        private final Context mContext;
        private final Context mContext;


        private String mKeystoreAlias;
        private String mKeystoreAlias;
@@ -199,6 +223,14 @@ public class AndroidKeyPairGeneratorSpec implements AlgorithmParameterSpec {


        private Date mEndDate;
        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) {
        public Builder(Context context) {
            if (context == null) {
            if (context == null) {
                throw new NullPointerException("context == null");
                throw new NullPointerException("context == null");
@@ -267,6 +299,17 @@ public class AndroidKeyPairGeneratorSpec implements AlgorithmParameterSpec {
            return this;
            return this;
        }
        }


        /**
         * 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}.
         * Builds the instance of the {@code AndroidKeyPairGeneratorSpec}.
         *
         *
@@ -275,7 +318,7 @@ public class AndroidKeyPairGeneratorSpec implements AlgorithmParameterSpec {
         */
         */
        public AndroidKeyPairGeneratorSpec build() {
        public AndroidKeyPairGeneratorSpec build() {
            return new AndroidKeyPairGeneratorSpec(mContext, mKeystoreAlias, mSubjectDN,
            return new AndroidKeyPairGeneratorSpec(mContext, mKeystoreAlias, mSubjectDN,
                    mSerialNumber, mStartDate, mEndDate);
                    mSerialNumber, mStartDate, mEndDate, mFlags);
        }
        }
    }
    }
}
}
+50 −7
Original line number Original line Diff line number Diff line
@@ -27,6 +27,10 @@ import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStream;
import java.security.InvalidKeyException;
import java.security.InvalidKeyException;
import java.security.Key;
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.KeyStoreException;
import java.security.KeyStoreSpi;
import java.security.KeyStoreSpi;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchAlgorithmException;
@@ -198,14 +202,14 @@ public class AndroidKeyStore extends KeyStoreSpi {
        }
        }


        if (key instanceof PrivateKey) {
        if (key instanceof PrivateKey) {
            setPrivateKeyEntry(alias, (PrivateKey) key, chain);
            setPrivateKeyEntry(alias, (PrivateKey) key, chain, null);
        } else {
        } else {
            throw new KeyStoreException("Only PrivateKeys are supported");
            throw new KeyStoreException("Only PrivateKeys are supported");
        }
        }
    }
    }


    private void setPrivateKeyEntry(String alias, PrivateKey key, Certificate[] chain)
    private void setPrivateKeyEntry(String alias, PrivateKey key, Certificate[] chain,
            throws KeyStoreException {
            AndroidKeyStoreParameter params) throws KeyStoreException {
        byte[] keyBytes = null;
        byte[] keyBytes = null;


        final String pkeyAlias;
        final String pkeyAlias;
@@ -317,15 +321,20 @@ public class AndroidKeyStore extends KeyStoreSpi {
            Credentials.deleteCertificateTypesForAlias(mKeyStore, alias);
            Credentials.deleteCertificateTypesForAlias(mKeyStore, alias);
        }
        }


        final int flags = (params == null) ? 0 : params.getFlags();

        if (shouldReplacePrivateKey
        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);
            Credentials.deleteAllTypesForAlias(mKeyStore, alias);
            throw new KeyStoreException("Couldn't put private key in keystore");
            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);
            Credentials.deleteAllTypesForAlias(mKeyStore, alias);
            throw new KeyStoreException("Couldn't put certificate #1 in keystore");
            throw new KeyStoreException("Couldn't put certificate #1 in keystore");
        } else if (chainBytes != null
        } 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);
            Credentials.deleteAllTypesForAlias(mKeyStore, alias);
            throw new KeyStoreException("Couldn't put certificate chain in keystore");
            throw new KeyStoreException("Couldn't put certificate chain in keystore");
        }
        }
@@ -355,7 +364,8 @@ public class AndroidKeyStore extends KeyStoreSpi {
            throw new KeyStoreException(e);
            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?");
            throw new KeyStoreException("Couldn't insert certificate; is KeyStore initialized?");
        }
        }
    }
    }
@@ -517,4 +527,37 @@ public class AndroidKeyStore extends KeyStoreSpi {
        mKeyStore = android.security.KeyStore.getInstance();
        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);
    }

}
}
+125 −0
Original line number Original line Diff line number Diff line
/*
 * 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);
        }
    }
}
+1 −2
Original line number Original line Diff line number Diff line
@@ -33,7 +33,6 @@ public class AndroidKeyStoreProvider extends Provider {
        put("KeyStore." + AndroidKeyStore.NAME, AndroidKeyStore.class.getName());
        put("KeyStore." + AndroidKeyStore.NAME, AndroidKeyStore.class.getName());


        // java.security.KeyPairGenerator
        // java.security.KeyPairGenerator
        put("KeyPairGenerator." + AndroidKeyPairGenerator.NAME,
        put("KeyPairGenerator." + AndroidKeyStore.NAME, AndroidKeyPairGenerator.class.getName());
                AndroidKeyPairGenerator.class.getName());
    }
    }
}
}
Loading