From ecf0f22e5831832afb48c86abfaa81234c8db619 Mon Sep 17 00:00:00 2001 From: Eran Messeri Date: Mon, 11 Dec 2017 12:32:13 +0000 Subject: DPM: Implement installing certificates for generated keys Add a new method in the DevicePolicyManager to associate certificates (and set the user-visibility) with a given key alias. Conceptually, the new method, setKeyPairCertificate is very similar to installKeyPair, except it does not install a key, only certificates. (The new setKeyPairCertificate, together with generateKeyPair is functionally equivalent to installKeyPair, except the keys are generated in hardware rather than supplied externally). Bug: 63388672 Test: cts-tradefed run commandAndExit cts-dev -a armeabi-v7a -m CtsDevicePolicyManagerTestCases -t com.android.cts.devicepolicy.DeviceOwnerTest#testKeyManagement -l DEBUG Change-Id: Idbfe151f6e5311766decbc1a010bff78dc60249f --- api/current.txt | 1 + .../android/app/admin/DevicePolicyManager.java | 46 ++++++++++++++++++++++ .../android/app/admin/IDevicePolicyManager.aidl | 2 + .../java/android/security/IKeyChainService.aidl | 1 + .../devicepolicy/BaseIDevicePolicyManager.java | 5 +++ .../devicepolicy/DevicePolicyManagerService.java | 27 +++++++++++++ 6 files changed, 82 insertions(+) diff --git a/api/current.txt b/api/current.txt index 1bc38d402cbc..f3eeeddd3655 100644 --- a/api/current.txt +++ b/api/current.txt @@ -6450,6 +6450,7 @@ package android.app.admin { method public void setDeviceOwnerLockScreenInfo(android.content.ComponentName, java.lang.CharSequence); method public void setGlobalSetting(android.content.ComponentName, java.lang.String, java.lang.String); method public void setKeepUninstalledPackages(android.content.ComponentName, java.util.List); + method public boolean setKeyPairCertificate(android.content.ComponentName, java.lang.String, java.util.List, boolean); method public boolean setKeyguardDisabled(android.content.ComponentName, boolean); method public void setKeyguardDisabledFeatures(android.content.ComponentName, int); method public void setLockTaskFeatures(android.content.ComponentName, int); diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 89df421efcc1..86e644400da1 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -4062,6 +4062,52 @@ public class DevicePolicyManager { return null; } + + /** + * Called by a device or profile owner, or delegated certificate installer, to associate + * certificates with a key pair that was generated using {@link #generateKeyPair}, and + * set whether the key is available for the user to choose in the certificate selection + * prompt. + * + * @param admin Which {@link DeviceAdminReceiver} this request is associated with, or + * {@code null} if calling from a delegated certificate installer. + * @param alias The private key alias under which to install the certificate. The {@code alias} + * should denote an existing private key. If a certificate with that alias already + * exists, it will be overwritten. + * @param certs The certificate chain to install. The chain should start with the leaf + * certificate and include the chain of trust in order. This will be returned by + * {@link android.security.KeyChain#getCertificateChain}. + * @param isUserSelectable {@code true} to indicate that a user can select this key via the + * certificate selection prompt, {@code false} to indicate that this key can only be + * granted access by implementing + * {@link android.app.admin.DeviceAdminReceiver#onChoosePrivateKeyAlias}. + * @return {@code true} if the provided {@code alias} exists and the certificates has been + * successfully associated with it, {@code false} otherwise. + * @throws SecurityException if {@code admin} is not {@code null} and not a device or profile + * owner, or {@code admin} is null but the calling application is not a delegated + * certificate installer. + */ + public boolean setKeyPairCertificate(@Nullable ComponentName admin, + @NonNull String alias, @NonNull List certs, boolean isUserSelectable) { + throwIfParentInstance("setKeyPairCertificate"); + try { + final byte[] pemCert = Credentials.convertToPem(certs.get(0)); + byte[] pemChain = null; + if (certs.size() > 1) { + pemChain = Credentials.convertToPem( + certs.subList(1, certs.size()).toArray(new Certificate[0])); + } + return mService.setKeyPairCertificate(admin, mContext.getPackageName(), alias, pemCert, + pemChain, isUserSelectable); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } catch (CertificateException | IOException e) { + Log.w(TAG, "Could not pem-encode certificate", e); + } + return false; + } + + /** * @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 912820818a78..33899dfc8337 100644 --- a/core/java/android/app/admin/IDevicePolicyManager.aidl +++ b/core/java/android/app/admin/IDevicePolicyManager.aidl @@ -171,6 +171,8 @@ interface IDevicePolicyManager { boolean generateKeyPair(in ComponentName who, in String callerPackage, in String algorithm, in ParcelableKeyGenParameterSpec keySpec, out KeymasterCertificateChain attestationChain); + boolean setKeyPairCertificate(in ComponentName who, in String callerPackage, in String alias, + in byte[] certBuffer, in byte[] certChainBuffer, boolean isUserSelectable); void choosePrivateKeyAlias(int uid, in Uri uri, in String alias, IBinder aliasCallback); void setDelegatedScopes(in ComponentName who, in String delegatePackage, in List scopes); diff --git a/keystore/java/android/security/IKeyChainService.aidl b/keystore/java/android/security/IKeyChainService.aidl index eca52cc3e8b6..7c7417dfaaac 100644 --- a/keystore/java/android/security/IKeyChainService.aidl +++ b/keystore/java/android/security/IKeyChainService.aidl @@ -35,6 +35,7 @@ interface IKeyChainService { boolean generateKeyPair(in String algorithm, in ParcelableKeyGenParameterSpec spec); boolean attestKey(in String alias, in byte[] challenge, out KeymasterCertificateChain chain); + boolean setKeyPairCertificate(String alias, in byte[] userCert, in byte[] certChain); // APIs used by CertInstaller and DevicePolicyManager String installCaCertificate(in byte[] caCertificate); diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java index 5b9e3a1e70eb..0a19a23969bb 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java @@ -71,4 +71,9 @@ abstract class BaseIDevicePolicyManager extends IDevicePolicyManager.Stub { public boolean isUsingUnifiedPassword(ComponentName who) { return true; } + + public boolean setKeyPairCertificate(ComponentName who, String callerPackage, String alias, + byte[] cert, byte[] chain, boolean isUserSelectable) { + return false; + } } diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index bead31fc675e..31f950964b94 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -4997,6 +4997,33 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return false; } + @Override + public boolean setKeyPairCertificate(ComponentName who, String callerPackage, String alias, + byte[] cert, byte[] chain, boolean isUserSelectable) { + enforceCanManageScope(who, callerPackage, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER, + DELEGATION_CERT_INSTALL); + + final int callingUid = mInjector.binderGetCallingUid(); + final long id = mInjector.binderClearCallingIdentity(); + try (final KeyChainConnection keyChainConnection = + KeyChain.bindAsUser(mContext, UserHandle.getUserHandleForUid(callingUid))) { + IKeyChainService keyChain = keyChainConnection.getService(); + if (!keyChain.setKeyPairCertificate(alias, cert, chain)) { + return false; + } + keyChain.setUserSelectable(alias, isUserSelectable); + return true; + } catch (InterruptedException e) { + Log.w(LOG_TAG, "Interrupted while setting keypair certificate", e); + Thread.currentThread().interrupt(); + } catch (RemoteException e) { + Log.e(LOG_TAG, "Failed setting keypair certificate", e); + } finally { + mInjector.binderRestoreCallingIdentity(id); + } + return false; + } + @Override public void choosePrivateKeyAlias(final int uid, final Uri uri, final String alias, final IBinder response) { -- cgit v1.2.3-59-g8ed1b