DevicePolicyManager: Add key generation functionality.

This is the crux of the Verified Access feature implementation:
Adding the ability to generate KeyChain keys directly by the
secure hardware, rather than installing software-generated keys
into KeyChain.

Add generateKeyPair to the DevicePolicyManager, which delegates key
generation (via the DevicePolicyManagerService) to the KeyChainService.

Design highlights:
* The key generation is delegated via the DevicePolicyManagerService to
  check that only authorized callers request key generation in KeyChain.
* KeyChainService performs the actual key generation so it owns the key
  in Keystore outright.
* DevicePolicyManagerService then grants the calling app access to the
  Keystore key, so it can actually be used.
* Loading the public/private key pair, as well as attestation
  certificate chain, is done in the client code (DevicePolicyManager)
  to save parceling / unparceling those objects across process
  boundaries twice (for no good reason).

NOTE: The key attestation functionality (that includes Device ID) is
missing/untested. Will be added in a follow-up CL as this one is quite
big already.

HIGHLIGHT FOR REVIEWERS:
* API: New API in DevicePolicyManager.

Bug: 63388672
Test: cts-tradefed run commandAndExit cts-dev -a armeabi-v7a -m CtsDevicePolicyManagerTestCases -t com.android.cts.devicepolicy.DeviceOwnerTest#testKeyManagement -l DEBUG; adb shell am instrument 'android.security.tests/android.support.test.runner.AndroidJUnitRunner' (After building the KeystoreTests target and installing the apk)
Change-Id: I73762c9123f32a94d454ba4f8b533883b55c44cc
diff --git a/api/current.txt b/api/current.txt
index d230aea..402aa8f 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -6320,6 +6320,7 @@
     method public android.os.UserHandle createAndManageUser(android.content.ComponentName, java.lang.String, android.content.ComponentName, android.os.PersistableBundle, int);
     method public void enableSystemApp(android.content.ComponentName, java.lang.String);
     method public int enableSystemApp(android.content.ComponentName, android.content.Intent);
+    method public android.security.AttestedKeyPair generateKeyPair(android.content.ComponentName, java.lang.String, android.security.keystore.KeyGenParameterSpec);
     method public java.lang.String[] getAccountTypesWithManagementDisabled();
     method public java.util.List<android.content.ComponentName> getActiveAdmins();
     method public java.util.Set<java.lang.String> getAffiliationIds(android.content.ComponentName);
@@ -37109,6 +37110,11 @@
 
 package android.security {
 
+  public final class AttestedKeyPair {
+    method public java.util.List<java.security.cert.Certificate> getAttestationRecord();
+    method public java.security.KeyPair getKeyPair();
+  }
+
   public final class KeyChain {
     ctor public KeyChain();
     method public static void choosePrivateKeyAlias(android.app.Activity, android.security.KeyChainAliasCallback, java.lang.String[], java.security.Principal[], java.lang.String, int, java.lang.String);
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index f0117f2..e94356d 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -57,7 +57,12 @@
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.provider.ContactsContract.Directory;
+import android.security.AttestedKeyPair;
 import android.security.Credentials;
+import android.security.KeyChain;
+import android.security.KeyChainException;
+import android.security.keystore.KeyGenParameterSpec;
+import android.security.keystore.ParcelableKeyGenParameterSpec;
 import android.service.restrictions.RestrictionsReceiver;
 import android.telephony.TelephonyManager;
 import android.util.ArraySet;
@@ -75,6 +80,7 @@
 import java.net.InetSocketAddress;
 import java.net.Proxy;
 import java.security.KeyFactory;
+import java.security.KeyPair;
 import java.security.NoSuchAlgorithmException;
 import java.security.PrivateKey;
 import java.security.cert.Certificate;
@@ -3943,6 +3949,50 @@
     }
 
     /**
+     * Called by a device or profile owner, or delegated certificate installer, to generate a
+     * new private/public key pair. If the device supports key generation via secure hardware,
+     * this method is useful for creating a key in KeyChain that never left the secure hardware.
+     *
+     * Access to the key is controlled the same way as in {@link #installKeyPair}.
+     * @param admin Which {@link DeviceAdminReceiver} this request is associated with, or
+     *            {@code null} if calling from a delegated certificate installer.
+     * @param algorithm The key generation algorithm, see {@link java.security.KeyPairGenerator}.
+     * @param keySpec Specification of the key to generate, see
+     * {@link java.security.KeyPairGenerator}.
+     * @return A non-null {@code AttestedKeyPair} if the key generation succeeded, null otherwise.
+     * @throws SecurityException if {@code admin} is not {@code null} and not a device or profile
+     *         owner.
+     * @throws IllegalArgumentException if the alias in {@code keySpec} is empty, or if the
+     *         algorithm specification in {@code keySpec} is not {@code RSAKeyGenParameterSpec}
+     *         or {@code ECGenParameterSpec}.
+     */
+    public AttestedKeyPair generateKeyPair(@Nullable ComponentName admin,
+            @NonNull String algorithm, @NonNull KeyGenParameterSpec keySpec) {
+        throwIfParentInstance("generateKeyPair");
+        try {
+            final ParcelableKeyGenParameterSpec parcelableSpec =
+                    new ParcelableKeyGenParameterSpec(keySpec);
+            final boolean success = mService.generateKeyPair(
+                    admin, mContext.getPackageName(), algorithm, parcelableSpec);
+            if (!success) {
+                Log.e(TAG, "Error generating key via DevicePolicyManagerService.");
+                return null;
+            }
+
+            final KeyPair keyPair = KeyChain.getKeyPair(mContext, keySpec.getKeystoreAlias());
+            return new AttestedKeyPair(keyPair, null);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        } catch (KeyChainException e) {
+            Log.w(TAG, "Failed to generate key", e);
+        } catch (InterruptedException e) {
+            Log.w(TAG, "Interrupted while generating key", e);
+            Thread.currentThread().interrupt();
+        }
+        return null;
+    }
+
+    /**
      * @return the alias of a given CA certificate in the certificate store, or {@code null} if it
      * doesn't exist.
      */
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 802d42f..21d7a25 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -36,6 +36,7 @@
 import android.os.PersistableBundle;
 import android.os.RemoteCallback;
 import android.os.UserHandle;
+import android.security.keystore.ParcelableKeyGenParameterSpec;
 
 import java.util.List;
 
@@ -165,6 +166,7 @@
             in byte[] certBuffer, in byte[] certChainBuffer, String alias, boolean requestAccess,
             boolean isUserSelectable);
     boolean removeKeyPair(in ComponentName who, in String callerPackage, String alias);
+    boolean generateKeyPair(in ComponentName who, in String callerPackage, in String algorithm, in ParcelableKeyGenParameterSpec keySpec);
     void choosePrivateKeyAlias(int uid, in Uri uri, in String alias, IBinder aliasCallback);
 
     void setDelegatedScopes(in ComponentName who, in String delegatePackage, in List<String> scopes);
diff --git a/keystore/java/android/security/AttestedKeyPair.java b/keystore/java/android/security/AttestedKeyPair.java
new file mode 100644
index 0000000..c6bff5c
--- /dev/null
+++ b/keystore/java/android/security/AttestedKeyPair.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2017 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 java.security.KeyPair;
+import java.security.cert.Certificate;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * The {@code AttestedKeyPair} class contains a {@code KeyPair} instance of
+ * keys generated by Keystore and owned by KeyChain, as well as an attestation
+ * record for the key.
+ *
+ * <p>Such keys can be obtained by calling
+ * {@link android.app.admin.DevicePolicyManager#generateKeyPair}.
+ */
+
+public final class AttestedKeyPair {
+    private final KeyPair mKeyPair;
+    private final Certificate[] mAttestationRecord;
+
+    /**
+     * @hide Only created by the platform, no need to expose as public API.
+     */
+    public AttestedKeyPair(KeyPair keyPair, Certificate[] attestationRecord) {
+        mKeyPair = keyPair;
+        mAttestationRecord = attestationRecord;
+    }
+
+    /**
+     * Returns the generated key pair associated with the attestation record
+     * in this instance.
+     */
+    public KeyPair getKeyPair() {
+        return mKeyPair;
+    }
+
+    /**
+     * Returns the attestation record for the key pair in this instance.
+     *
+     * The attestation record is a chain of certificates. The leaf certificate links to the public
+     * key of this key pair and other properties of the key or the device. If the key is in secure
+     * hardware, and if the secure hardware supports attestation, the leaf certificate will be
+     * signed by a chain of certificates rooted at a trustworthy CA key. Otherwise the chain will be
+     * rooted at an untrusted certificate.
+     *
+     * The attestation record could be for properties of the key, or include device identifiers.
+     *
+     * See {@link android.security.keystore.KeyGenParameterSpec.Builder#setAttestationChallenge}
+     * and  <a href="https://developer.android.com/training/articles/security-key-attestation.html">
+     * Key Attestation</a> for the format of the attestation record inside the certificate.
+     */
+    public List<Certificate> getAttestationRecord() {
+        if (mAttestationRecord == null) {
+            return new ArrayList();
+        }
+        return Arrays.asList(mAttestationRecord);
+    }
+}
diff --git a/keystore/java/android/security/IKeyChainService.aidl b/keystore/java/android/security/IKeyChainService.aidl
index 635432d..b4331b2 100644
--- a/keystore/java/android/security/IKeyChainService.aidl
+++ b/keystore/java/android/security/IKeyChainService.aidl
@@ -16,6 +16,7 @@
 package android.security;
 
 import android.content.pm.StringParceledListSlice;
+import android.security.keystore.ParcelableKeyGenParameterSpec;
 
 /**
  * Caller is required to ensure that {@link KeyStore#unlock
@@ -31,6 +32,8 @@
     boolean isUserSelectable(String alias);
     void setUserSelectable(String alias, boolean isUserSelectable);
 
+    boolean generateKeyPair(in String algorithm, in ParcelableKeyGenParameterSpec spec);
+
     // APIs used by CertInstaller and DevicePolicyManager
     String installCaCertificate(in byte[] caCertificate);
 
diff --git a/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.aidl b/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.aidl
new file mode 100644
index 0000000..3fb7b4f
--- /dev/null
+++ b/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2017 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.keystore;
+
+parcelable ParcelableKeyGenParameterSpec;
diff --git a/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java b/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java
new file mode 100644
index 0000000..b15e0a2
--- /dev/null
+++ b/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 2017 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.keystore;
+
+import android.os.Parcelable;
+import android.os.Parcel;
+
+import java.math.BigInteger;
+import java.security.spec.AlgorithmParameterSpec;
+import java.security.spec.ECGenParameterSpec;
+import java.security.spec.RSAKeyGenParameterSpec;
+import java.util.Date;
+
+import javax.security.auth.x500.X500Principal;
+
+/**
+ * A parcelable version of KeyGenParameterSpec
+ * @hide only used for communicating with the DPMS.
+ */
+public final class ParcelableKeyGenParameterSpec implements Parcelable {
+    private static final int ALGORITHM_PARAMETER_SPEC_NONE = 1;
+    private static final int ALGORITHM_PARAMETER_SPEC_RSA = 2;
+    private static final int ALGORITHM_PARAMETER_SPEC_EC = 3;
+
+    private final KeyGenParameterSpec mSpec;
+
+    public ParcelableKeyGenParameterSpec(
+            KeyGenParameterSpec spec) {
+        mSpec = spec;
+    }
+
+    public int describeContents() {
+        return 0;
+    }
+
+    private static void writeOptionalDate(Parcel out, Date date) {
+        if (date != null) {
+            out.writeBoolean(true);
+            out.writeLong(date.getTime());
+        } else {
+            out.writeBoolean(false);
+        }
+    }
+
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeString(mSpec.getKeystoreAlias());
+        out.writeInt(mSpec.getPurposes());
+        out.writeInt(mSpec.getUid());
+        out.writeInt(mSpec.getKeySize());
+
+        // Only needs to support RSAKeyGenParameterSpec and ECGenParameterSpec.
+        AlgorithmParameterSpec algoSpec = mSpec.getAlgorithmParameterSpec();
+        if (algoSpec == null) {
+            out.writeInt(ALGORITHM_PARAMETER_SPEC_NONE);
+        } else if (algoSpec instanceof RSAKeyGenParameterSpec) {
+            RSAKeyGenParameterSpec rsaSpec = (RSAKeyGenParameterSpec) algoSpec;
+            out.writeInt(ALGORITHM_PARAMETER_SPEC_RSA);
+            out.writeInt(rsaSpec.getKeysize());
+            out.writeByteArray(rsaSpec.getPublicExponent().toByteArray());
+        } else if (algoSpec instanceof ECGenParameterSpec) {
+            ECGenParameterSpec ecSpec = (ECGenParameterSpec) algoSpec;
+            out.writeInt(ALGORITHM_PARAMETER_SPEC_EC);
+            out.writeString(ecSpec.getName());
+        } else {
+            throw new IllegalArgumentException(
+                    String.format("Unknown algorithm parameter spec: %s", algoSpec.getClass()));
+        }
+        out.writeByteArray(mSpec.getCertificateSubject().getEncoded());
+        out.writeByteArray(mSpec.getCertificateSerialNumber().toByteArray());
+        writeOptionalDate(out, mSpec.getCertificateNotBefore());
+        writeOptionalDate(out, mSpec.getCertificateNotAfter());
+        writeOptionalDate(out, mSpec.getKeyValidityStart());
+        writeOptionalDate(out, mSpec.getKeyValidityForOriginationEnd());
+        writeOptionalDate(out, mSpec.getKeyValidityForConsumptionEnd());
+        out.writeStringArray(mSpec.getDigests());
+        out.writeStringArray(mSpec.getEncryptionPaddings());
+        out.writeStringArray(mSpec.getSignaturePaddings());
+        out.writeStringArray(mSpec.getBlockModes());
+        out.writeBoolean(mSpec.isRandomizedEncryptionRequired());
+        out.writeBoolean(mSpec.isUserAuthenticationRequired());
+        out.writeInt(mSpec.getUserAuthenticationValidityDurationSeconds());
+        out.writeByteArray(mSpec.getAttestationChallenge());
+        out.writeBoolean(mSpec.isUniqueIdIncluded());
+        out.writeBoolean(mSpec.isUserAuthenticationValidWhileOnBody());
+        out.writeBoolean(mSpec.isInvalidatedByBiometricEnrollment());
+    }
+
+    private static Date readDateOrNull(Parcel in) {
+        boolean hasDate = in.readBoolean();
+        if (hasDate) {
+            return new Date(in.readLong());
+        } else {
+            return null;
+        }
+    }
+
+    private ParcelableKeyGenParameterSpec(Parcel in) {
+        String keystoreAlias = in.readString();
+        int purposes = in.readInt();
+        KeyGenParameterSpec.Builder builder = new KeyGenParameterSpec.Builder(keystoreAlias, purposes);
+        builder.setUid(in.readInt());
+        builder.setKeySize(in.readInt());
+
+        int keySpecType = in.readInt();
+        AlgorithmParameterSpec algorithmSpec = null;
+        if (keySpecType == ALGORITHM_PARAMETER_SPEC_NONE) {
+            algorithmSpec = null;
+        } else if (keySpecType == ALGORITHM_PARAMETER_SPEC_RSA) {
+            int rsaKeySize = in.readInt();
+            BigInteger publicExponent = new BigInteger(in.createByteArray());
+            algorithmSpec = new RSAKeyGenParameterSpec(rsaKeySize, publicExponent);
+        } else if (keySpecType == ALGORITHM_PARAMETER_SPEC_EC) {
+            String stdName = in.readString();
+            algorithmSpec = new ECGenParameterSpec(stdName);
+        } else {
+            throw new IllegalArgumentException(
+                    String.format("Unknown algorithm parameter spec: %d", algorithmSpec));
+        }
+        builder.setAlgorithmParameterSpec(algorithmSpec);
+        builder.setCertificateSubject(new X500Principal(in.createByteArray()));
+        builder.setCertificateSerialNumber(new BigInteger(in.createByteArray()));
+        builder.setCertificateNotBefore(readDateOrNull(in));
+        builder.setCertificateNotAfter(readDateOrNull(in));
+        builder.setKeyValidityStart(readDateOrNull(in));
+        builder.setKeyValidityForOriginationEnd(readDateOrNull(in));
+        builder.setKeyValidityForConsumptionEnd(readDateOrNull(in));
+        builder.setDigests(in.createStringArray());
+        builder.setEncryptionPaddings(in.createStringArray());
+        builder.setSignaturePaddings(in.createStringArray());
+        builder.setBlockModes(in.createStringArray());
+        builder.setRandomizedEncryptionRequired(in.readBoolean());
+        builder.setUserAuthenticationRequired(in.readBoolean());
+        builder.setUserAuthenticationValidityDurationSeconds(in.readInt());
+        builder.setAttestationChallenge(in.createByteArray());
+        builder.setUniqueIdIncluded(in.readBoolean());
+        builder.setUserAuthenticationValidWhileOnBody(in.readBoolean());
+        builder.setInvalidatedByBiometricEnrollment(in.readBoolean());
+        mSpec = builder.build();
+    }
+
+    public static final Creator<ParcelableKeyGenParameterSpec> CREATOR = new Creator<ParcelableKeyGenParameterSpec>() {
+        @Override
+        public ParcelableKeyGenParameterSpec createFromParcel(Parcel in) {
+            return new ParcelableKeyGenParameterSpec(in);
+        }
+
+        @Override
+        public ParcelableKeyGenParameterSpec[] newArray(int size) {
+            return new ParcelableKeyGenParameterSpec[size];
+        }
+    };
+
+    public KeyGenParameterSpec getSpec() {
+        return mSpec;
+    }
+}
diff --git a/keystore/tests/Android.mk b/keystore/tests/Android.mk
new file mode 100644
index 0000000..51adde4
--- /dev/null
+++ b/keystore/tests/Android.mk
@@ -0,0 +1,34 @@
+# Copyright (C) 2017 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.
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+# LOCAL_MODULE := keystore
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_STATIC_JAVA_LIBRARIES := \
+    android-support-test \
+    legacy-android-test
+
+LOCAL_PACKAGE_NAME := KeystoreTests
+
+LOCAL_JAVA_LIBRARIES := android.test.runner
+
+LOCAL_CERTIFICATE := platform
+
+include $(BUILD_PACKAGE)
+
diff --git a/keystore/tests/AndroidManifest.xml b/keystore/tests/AndroidManifest.xml
new file mode 100644
index 0000000..9bf2d0c
--- /dev/null
+++ b/keystore/tests/AndroidManifest.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 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.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="android.security.tests">
+
+    <application>
+        <uses-library android:name="android.test.runner" />
+    </application>
+
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+        android:targetPackage="android.security.tests"
+        android:label="Tests for Keystore">
+    </instrumentation>
+</manifest>
+
diff --git a/keystore/tests/src/android/security/ParcelableKeyGenParameterSpecTest.java b/keystore/tests/src/android/security/ParcelableKeyGenParameterSpecTest.java
new file mode 100644
index 0000000..73b489f
--- /dev/null
+++ b/keystore/tests/src/android/security/ParcelableKeyGenParameterSpecTest.java
@@ -0,0 +1,180 @@
+/*
+ * Copyright (C) 2017 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 static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+
+import android.os.Parcel;
+import android.security.keystore.KeyGenParameterSpec;
+import android.security.keystore.ParcelableKeyGenParameterSpec;
+import android.security.keystore.KeyProperties;
+import android.support.test.runner.AndroidJUnit4;
+import java.math.BigInteger;
+import java.security.spec.ECGenParameterSpec;
+import java.security.spec.RSAKeyGenParameterSpec;
+import java.util.Date;
+import javax.security.auth.x500.X500Principal;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/** Unit tests for {@link ParcelableKeyGenParameterSpec}. */
+@RunWith(AndroidJUnit4.class)
+public final class ParcelableKeyGenParameterSpecTest {
+    static final String ALIAS = "keystore-alias";
+    static final String ANOTHER_ALIAS = "another-keystore-alias";
+    static final int KEY_PURPOSES = KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY;
+    static final int UID = 1230;
+    static final int KEYSIZE = 2048;
+    static final X500Principal SUBJECT = new X500Principal("CN=subject");
+    static final BigInteger SERIAL = new BigInteger("1234567890");
+    static final Date NOT_BEFORE = new Date(1511799590);
+    static final Date NOT_AFTER = new Date(1511899590);
+    static final Date KEY_VALIDITY_START = new Date(1511799591);
+    static final Date KEY_VALIDITY_FOR_ORIG_END = new Date(1511799593);
+    static final Date KEY_VALIDITY_FOR_CONSUMPTION_END = new Date(1511799594);
+    static final String DIGEST = KeyProperties.DIGEST_SHA256;
+    static final String ENCRYPTION_PADDING = KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1;
+    static final String SIGNATURE_PADDING = KeyProperties.SIGNATURE_PADDING_RSA_PSS;
+    static final String BLOCK_MODE = KeyProperties.BLOCK_MODE_CBC;
+    static final int USER_AUTHENTICATION_DURATION = 300;
+    static final byte[] ATTESTATION_CHALLENGE = new byte[] {'c', 'h'};
+
+    KeyGenParameterSpec configureDefaultSpec() {
+        return new KeyGenParameterSpec.Builder(ALIAS, KEY_PURPOSES)
+                .setUid(UID)
+                .setKeySize(KEYSIZE)
+                .setCertificateSubject(SUBJECT)
+                .setCertificateSerialNumber(SERIAL)
+                .setCertificateNotBefore(NOT_BEFORE)
+                .setCertificateNotAfter(NOT_AFTER)
+                .setKeyValidityStart(KEY_VALIDITY_START)
+                .setKeyValidityForOriginationEnd(KEY_VALIDITY_FOR_ORIG_END)
+                .setKeyValidityForConsumptionEnd(KEY_VALIDITY_FOR_CONSUMPTION_END)
+                .setDigests(DIGEST)
+                .setEncryptionPaddings(ENCRYPTION_PADDING)
+                .setSignaturePaddings(SIGNATURE_PADDING)
+                .setBlockModes(BLOCK_MODE)
+                .setRandomizedEncryptionRequired(true)
+                .setUserAuthenticationRequired(true)
+                .setUserAuthenticationValidityDurationSeconds(USER_AUTHENTICATION_DURATION)
+                .setAttestationChallenge(ATTESTATION_CHALLENGE)
+                .setUniqueIdIncluded(true)
+                .setUserAuthenticationValidWhileOnBody(true)
+                .setInvalidatedByBiometricEnrollment(true)
+                .build();
+    }
+
+    void validateSpecValues(KeyGenParameterSpec spec, int uid, String alias) {
+        assertThat(spec.getKeystoreAlias(), is(alias));
+        assertThat(spec.getPurposes(), is(KEY_PURPOSES));
+        assertThat(spec.getUid(), is(uid));
+        assertThat(spec.getKeySize(), is(KEYSIZE));
+        assertThat(spec.getCertificateSubject(), is(SUBJECT));
+        assertThat(spec.getCertificateSerialNumber(), is(SERIAL));
+        assertThat(spec.getCertificateNotBefore(), is(NOT_BEFORE));
+        assertThat(spec.getCertificateNotAfter(), is(NOT_AFTER));
+        assertThat(spec.getKeyValidityStart(), is(KEY_VALIDITY_START));
+        assertThat(spec.getKeyValidityForOriginationEnd(), is(KEY_VALIDITY_FOR_ORIG_END));
+        assertThat(spec.getKeyValidityForConsumptionEnd(), is(KEY_VALIDITY_FOR_CONSUMPTION_END));
+        assertThat(spec.getDigests(), is(new String[] {DIGEST}));
+        assertThat(spec.getEncryptionPaddings(), is(new String[] {ENCRYPTION_PADDING}));
+        assertThat(spec.getSignaturePaddings(), is(new String[] {SIGNATURE_PADDING}));
+        assertThat(spec.getBlockModes(), is(new String[] {BLOCK_MODE}));
+        assertThat(spec.isRandomizedEncryptionRequired(), is(true));
+        assertThat(spec.isUserAuthenticationRequired(), is(true));
+        assertThat(
+                spec.getUserAuthenticationValidityDurationSeconds(),
+                is(USER_AUTHENTICATION_DURATION));
+        assertThat(spec.getAttestationChallenge(), is(ATTESTATION_CHALLENGE));
+        assertThat(spec.isUniqueIdIncluded(), is(true));
+        assertThat(spec.isUserAuthenticationValidWhileOnBody(), is(true));
+        assertThat(spec.isInvalidatedByBiometricEnrollment(), is(true));
+    }
+
+    private Parcel parcelForReading(ParcelableKeyGenParameterSpec spec) {
+        Parcel parcel = Parcel.obtain();
+        spec.writeToParcel(parcel, spec.describeContents());
+
+        parcel.setDataPosition(0);
+        return parcel;
+    }
+
+    @Test
+    public void testParcelingWithAllValues() {
+        ParcelableKeyGenParameterSpec spec =
+            new ParcelableKeyGenParameterSpec(configureDefaultSpec());
+        Parcel parcel = parcelForReading(spec);
+        ParcelableKeyGenParameterSpec fromParcel =
+            ParcelableKeyGenParameterSpec.CREATOR.createFromParcel(parcel);
+        validateSpecValues(fromParcel.getSpec(), UID, ALIAS);
+        assertThat(parcel.dataAvail(), is(0));
+    }
+
+    @Test
+    public void testParcelingWithNullValues() {
+        ParcelableKeyGenParameterSpec spec = new ParcelableKeyGenParameterSpec(
+            new KeyGenParameterSpec.Builder(ALIAS, KEY_PURPOSES).build());
+
+        Parcel parcel = parcelForReading(spec);
+        KeyGenParameterSpec fromParcel = ParcelableKeyGenParameterSpec.CREATOR
+            .createFromParcel(parcel)
+            .getSpec();
+        assertThat(fromParcel.getKeystoreAlias(), is(ALIAS));
+        assertThat(fromParcel.getPurposes(), is(KEY_PURPOSES));
+        assertThat(fromParcel.getCertificateNotBefore(), is(new Date(0L)));
+        assertThat(fromParcel.getCertificateNotAfter(), is(new Date(2461449600000L)));
+        assertThat(parcel.dataAvail(), is(0));
+    }
+
+    @Test
+    public void testParcelingRSAAlgoParameter() {
+        RSAKeyGenParameterSpec rsaSpec =
+                new RSAKeyGenParameterSpec(2048, new BigInteger("5231123"));
+        ParcelableKeyGenParameterSpec spec = new ParcelableKeyGenParameterSpec(
+            new KeyGenParameterSpec.Builder(ALIAS, KEY_PURPOSES)
+            .setAlgorithmParameterSpec(rsaSpec)
+            .build());
+
+        Parcel parcel = parcelForReading(spec);
+        KeyGenParameterSpec fromParcel =
+            ParcelableKeyGenParameterSpec.CREATOR.createFromParcel(parcel).getSpec();
+        RSAKeyGenParameterSpec parcelSpec =
+                (RSAKeyGenParameterSpec) fromParcel.getAlgorithmParameterSpec();
+        // Compare individual fields as RSAKeyGenParameterSpec, on android, does not
+        // implement equals()
+        assertEquals(parcelSpec.getKeysize(), rsaSpec.getKeysize());
+        assertEquals(parcelSpec.getPublicExponent(), rsaSpec.getPublicExponent());
+    }
+
+    @Test
+    public void testParcelingECAlgoParameter() {
+        ECGenParameterSpec ecSpec = new ECGenParameterSpec("P-256");
+        ParcelableKeyGenParameterSpec spec = new ParcelableKeyGenParameterSpec(
+                new KeyGenParameterSpec.Builder(ALIAS, KEY_PURPOSES)
+                        .setAlgorithmParameterSpec(ecSpec)
+                        .build());
+        Parcel parcel = parcelForReading(spec);
+        KeyGenParameterSpec fromParcel =
+            ParcelableKeyGenParameterSpec.CREATOR.createFromParcel(parcel).getSpec();
+        // Compare individual fields as ECGenParameterSpec, on android, does not
+        // implement equals()
+        ECGenParameterSpec parcelSpec = (ECGenParameterSpec) fromParcel.getAlgorithmParameterSpec();
+        assertEquals(parcelSpec.getName(), ecSpec.getName());
+    }
+}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 663083c..4a8c0b46 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -150,6 +150,9 @@
 import android.security.IKeyChainService;
 import android.security.KeyChain;
 import android.security.KeyChain.KeyChainConnection;
+import android.security.keystore.KeyGenParameterSpec;
+import android.security.keystore.ParcelableKeyGenParameterSpec;
+import android.security.KeyStore;
 import android.service.persistentdata.PersistentDataBlockManager;
 import android.telephony.TelephonyManager;
 import android.text.TextUtils;
@@ -5011,6 +5014,54 @@
     }
 
     @Override
+    public boolean generateKeyPair(ComponentName who, String callerPackage, String algorithm,
+            ParcelableKeyGenParameterSpec parcelableKeySpec) {
+        enforceCanManageScope(who, callerPackage, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER,
+                DELEGATION_CERT_INSTALL);
+        final KeyGenParameterSpec keySpec = parcelableKeySpec.getSpec();
+        if (TextUtils.isEmpty(keySpec.getKeystoreAlias())) {
+            throw new IllegalArgumentException("Empty alias provided.");
+        }
+        // As the caller will be granted access to the key, ensure no UID was specified, as
+        // it will not have the desired effect.
+        if (keySpec.getUid() != KeyStore.UID_SELF) {
+            Log.e(LOG_TAG, "Only the caller can be granted access to the generated keypair.");
+            return false;
+        }
+        final int callingUid = mInjector.binderGetCallingUid();
+
+        final UserHandle userHandle = mInjector.binderGetCallingUserHandle();
+        final long id = mInjector.binderClearCallingIdentity();
+        try {
+            try (KeyChainConnection keyChainConnection =
+                    KeyChain.bindAsUser(mContext, userHandle)) {
+                IKeyChainService keyChain = keyChainConnection.getService();
+                final boolean generationResult = keyChain.generateKeyPair(algorithm, parcelableKeySpec);
+                if (!generationResult) {
+                    Log.e(LOG_TAG, "KeyChain failed to generate a keypair.");
+                    return false;
+                }
+
+                // Set a grant for the caller here so that when the client calls
+                // requestPrivateKey, it will be able to get the key from Keystore.
+                // Note the use of the calling  UID, since the request for the private
+                // key will come from the client's process, so the grant has to be for
+                // that UID.
+                keyChain.setGrant(callingUid, keySpec.getKeystoreAlias(), true);
+                return true;
+            }
+        } catch (RemoteException e) {
+            Log.e(LOG_TAG, "KeyChain error while generating a keypair", e);
+        } catch (InterruptedException e) {
+            Log.w(LOG_TAG, "Interrupted while generating keypair", e);
+            Thread.currentThread().interrupt();
+        } finally {
+            mInjector.binderRestoreCallingIdentity(id);
+        }
+        return false;
+    }
+
+    @Override
     public void choosePrivateKeyAlias(final int uid, final Uri uri, final String alias,
             final IBinder response) {
         // Caller UID needs to be trusted, so we restrict this method to SYSTEM_UID callers.