Merge "Add DevicePolicyManager API to install a client cert chain." into nyc-dev
This commit is contained in:
@@ -5893,7 +5893,7 @@ package android.app.admin {
|
||||
method public boolean hasGrantedPolicy(android.content.ComponentName, int);
|
||||
method public boolean installCaCert(android.content.ComponentName, byte[]);
|
||||
method public boolean installKeyPair(android.content.ComponentName, java.security.PrivateKey, java.security.cert.Certificate, java.lang.String);
|
||||
method public boolean installKeyPair(android.content.ComponentName, java.security.PrivateKey, java.security.cert.Certificate, java.lang.String, boolean);
|
||||
method public boolean installKeyPair(android.content.ComponentName, java.security.PrivateKey, java.security.cert.Certificate[], java.lang.String, boolean);
|
||||
method public boolean isActivePasswordSufficient();
|
||||
method public boolean isAdminActive(android.content.ComponentName);
|
||||
method public boolean isApplicationHidden(android.content.ComponentName, java.lang.String);
|
||||
|
||||
@@ -6039,7 +6039,7 @@ package android.app.admin {
|
||||
method public boolean hasGrantedPolicy(android.content.ComponentName, int);
|
||||
method public boolean installCaCert(android.content.ComponentName, byte[]);
|
||||
method public boolean installKeyPair(android.content.ComponentName, java.security.PrivateKey, java.security.cert.Certificate, java.lang.String);
|
||||
method public boolean installKeyPair(android.content.ComponentName, java.security.PrivateKey, java.security.cert.Certificate, java.lang.String, boolean);
|
||||
method public boolean installKeyPair(android.content.ComponentName, java.security.PrivateKey, java.security.cert.Certificate[], java.lang.String, boolean);
|
||||
method public boolean isActivePasswordSufficient();
|
||||
method public boolean isAdminActive(android.content.ComponentName);
|
||||
method public boolean isApplicationHidden(android.content.ComponentName, java.lang.String);
|
||||
|
||||
@@ -5897,7 +5897,7 @@ package android.app.admin {
|
||||
method public boolean hasGrantedPolicy(android.content.ComponentName, int);
|
||||
method public boolean installCaCert(android.content.ComponentName, byte[]);
|
||||
method public boolean installKeyPair(android.content.ComponentName, java.security.PrivateKey, java.security.cert.Certificate, java.lang.String);
|
||||
method public boolean installKeyPair(android.content.ComponentName, java.security.PrivateKey, java.security.cert.Certificate, java.lang.String, boolean);
|
||||
method public boolean installKeyPair(android.content.ComponentName, java.security.PrivateKey, java.security.cert.Certificate[], java.lang.String, boolean);
|
||||
method public boolean isActivePasswordSufficient();
|
||||
method public boolean isAdminActive(android.content.ComponentName);
|
||||
method public boolean isApplicationHidden(android.content.ComponentName, java.lang.String);
|
||||
|
||||
@@ -71,6 +71,7 @@ import java.security.cert.X509Certificate;
|
||||
import java.security.spec.InvalidKeySpecException;
|
||||
import java.security.spec.PKCS8EncodedKeySpec;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
@@ -2782,7 +2783,7 @@ public class DevicePolicyManager {
|
||||
* compromised, certificates it had already installed will be protected.
|
||||
*
|
||||
* <p>If the installer must have access to the credentials, call
|
||||
* {@link #installKeyPair(ComponentName, PrivateKey, Certificate, String, boolean)} instead.
|
||||
* {@link #installKeyPair(ComponentName, PrivateKey, Certificate[], String, boolean)} instead.
|
||||
*
|
||||
* @param admin Which {@link DeviceAdminReceiver} this request is associated with, or
|
||||
* {@code null} if calling from a delegated certificate installer.
|
||||
@@ -2796,13 +2797,14 @@ public class DevicePolicyManager {
|
||||
*/
|
||||
public boolean installKeyPair(@Nullable ComponentName admin, @NonNull PrivateKey privKey,
|
||||
@NonNull Certificate cert, @NonNull String alias) {
|
||||
return installKeyPair(admin, privKey, cert, alias, false);
|
||||
return installKeyPair(admin, privKey, new Certificate[] {cert}, alias, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by a device or profile owner, or delegated certificate installer, to install a
|
||||
* certificate and corresponding private key. All apps within the profile will be able to access
|
||||
* the certificate and use the private key, given direct user approval.
|
||||
* certificate chain and corresponding private key for the leaf certificate. All apps within the
|
||||
* profile will be able to access the certificate chain and use the private key, given direct
|
||||
* user approval.
|
||||
*
|
||||
* <p>The caller of this API may grant itself access to the certificate and private key
|
||||
* immediately, without user approval. It is a best practice not to request this unless strictly
|
||||
@@ -2811,7 +2813,9 @@ public class DevicePolicyManager {
|
||||
* @param admin Which {@link DeviceAdminReceiver} this request is associated with, or
|
||||
* {@code null} if calling from a delegated certificate installer.
|
||||
* @param privKey The private key to install.
|
||||
* @param cert The certificate to install.
|
||||
* @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 alias The private key alias under which to install the certificate. If a certificate
|
||||
* with that alias already exists, it will be overwritten.
|
||||
* @param requestAccess {@code true} to request that the calling app be granted access to the
|
||||
@@ -2820,14 +2824,20 @@ public class DevicePolicyManager {
|
||||
* @return {@code true} if the keys were installed, {@code false} otherwise.
|
||||
* @throws SecurityException if {@code admin} is not {@code null} and not a device or profile
|
||||
* owner.
|
||||
* @see android.security.KeyChain#getCertificateChain
|
||||
*/
|
||||
public boolean installKeyPair(@Nullable ComponentName admin, @NonNull PrivateKey privKey,
|
||||
@NonNull Certificate cert, @NonNull String alias, boolean requestAccess) {
|
||||
@NonNull Certificate[] certs, @NonNull String alias, boolean requestAccess) {
|
||||
try {
|
||||
final byte[] pemCert = Credentials.convertToPem(cert);
|
||||
final byte[] pemCert = Credentials.convertToPem(certs[0]);
|
||||
byte[] pemChain = null;
|
||||
if (certs.length > 1) {
|
||||
pemChain = Credentials.convertToPem(Arrays.copyOfRange(certs, 1, certs.length));
|
||||
}
|
||||
final byte[] pkcs8Key = KeyFactory.getInstance(privKey.getAlgorithm())
|
||||
.getKeySpec(privKey, PKCS8EncodedKeySpec.class).getEncoded();
|
||||
return mService.installKeyPair(admin, pkcs8Key, pemCert, alias, requestAccess);
|
||||
return mService.installKeyPair(admin, pkcs8Key, pemCert, pemChain, alias,
|
||||
requestAccess);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
} catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
|
||||
|
||||
@@ -146,7 +146,7 @@ interface IDevicePolicyManager {
|
||||
void enforceCanManageCaCerts(in ComponentName admin);
|
||||
|
||||
boolean installKeyPair(in ComponentName who, in byte[] privKeyBuffer, in byte[] certBuffer,
|
||||
String alias, boolean requestAccess);
|
||||
in byte[] certChainBuffer, String alias, boolean requestAccess);
|
||||
boolean removeKeyPair(in ComponentName who, String alias);
|
||||
void choosePrivateKeyAlias(int uid, in Uri uri, in String alias, IBinder aliasCallback);
|
||||
|
||||
|
||||
@@ -27,12 +27,13 @@ interface IKeyChainService {
|
||||
// APIs used by KeyChain
|
||||
String requestPrivateKey(String alias);
|
||||
byte[] getCertificate(String alias);
|
||||
byte[] getCaCertificates(String alias);
|
||||
|
||||
// APIs used by CertInstaller
|
||||
void installCaCertificate(in byte[] caCertificate);
|
||||
|
||||
// APIs used by DevicePolicyManager
|
||||
boolean installKeyPair(in byte[] privateKey, in byte[] userCert, String alias);
|
||||
boolean installKeyPair(in byte[] privateKey, in byte[] userCert, in byte[] certChain, String alias);
|
||||
boolean removeKeyPair(String alias);
|
||||
|
||||
// APIs used by Settings
|
||||
|
||||
@@ -42,6 +42,8 @@ import java.security.cert.Certificate;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.security.cert.CertificateFactory;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
@@ -389,7 +391,12 @@ public final class KeyChain {
|
||||
|
||||
/**
|
||||
* Returns the {@code X509Certificate} chain for the requested
|
||||
* alias, or null if no there is no result.
|
||||
* alias, or null if there is no result.
|
||||
* <p>
|
||||
* <strong>Note:</strong> If a certificate chain was explicitly specified when the alias was
|
||||
* installed, this method will return that chain. If only the client certificate was specified
|
||||
* at the installation time, this method will try to build a certificate chain using all
|
||||
* available trust anchors (preinstalled and user-added).
|
||||
*
|
||||
* <p> This method may block while waiting for a connection to another process, and must never
|
||||
* be called from the main thread.
|
||||
@@ -413,11 +420,31 @@ public final class KeyChain {
|
||||
if (certificateBytes == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
TrustedCertificateStore store = new TrustedCertificateStore();
|
||||
List<X509Certificate> chain = store
|
||||
.getCertificateChain(toCertificate(certificateBytes));
|
||||
return chain.toArray(new X509Certificate[chain.size()]);
|
||||
X509Certificate leafCert = toCertificate(certificateBytes);
|
||||
final byte[] certChainBytes = keyChainService.getCaCertificates(alias);
|
||||
// If the keypair is installed with a certificate chain by either
|
||||
// DevicePolicyManager.installKeyPair or CertInstaller, return that chain.
|
||||
if (certChainBytes != null && certChainBytes.length != 0) {
|
||||
Collection<X509Certificate> chain = toCertificates(certChainBytes);
|
||||
ArrayList<X509Certificate> fullChain = new ArrayList<>(chain.size() + 1);
|
||||
fullChain.add(leafCert);
|
||||
fullChain.addAll(chain);
|
||||
return fullChain.toArray(new X509Certificate[fullChain.size()]);
|
||||
} else {
|
||||
// If there isn't a certificate chain, either due to a pre-existing keypair
|
||||
// installed before N, or no chain is explicitly installed under the new logic,
|
||||
// fall back to old behavior of constructing the chain from trusted credentials.
|
||||
//
|
||||
// This logic exists to maintain old behaviour for already installed keypair, at
|
||||
// the cost of potentially returning extra certificate chain for new clients who
|
||||
// explicitly installed only the client certificate without a chain. The latter
|
||||
// case is actually no different from pre-N behaviour of getCertificateChain(),
|
||||
// in that sense this change introduces no regression. Besides the returned chain
|
||||
// is still valid so the consumer of the chain should have no problem verifying it.
|
||||
TrustedCertificateStore store = new TrustedCertificateStore();
|
||||
List<X509Certificate> chain = store.getCertificateChain(leafCert);
|
||||
return chain.toArray(new X509Certificate[chain.size()]);
|
||||
}
|
||||
} catch (CertificateException e) {
|
||||
throw new KeyChainException(e);
|
||||
} catch (RemoteException e) {
|
||||
@@ -486,6 +513,21 @@ public final class KeyChain {
|
||||
}
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
@NonNull
|
||||
public static Collection<X509Certificate> toCertificates(@NonNull byte[] bytes) {
|
||||
if (bytes == null) {
|
||||
throw new IllegalArgumentException("bytes == null");
|
||||
}
|
||||
try {
|
||||
CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
|
||||
return (Collection<X509Certificate>) certFactory.generateCertificates(
|
||||
new ByteArrayInputStream(bytes));
|
||||
} catch (CertificateException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide for reuse by CertInstaller and Settings.
|
||||
* @see KeyChain#bind
|
||||
|
||||
@@ -4138,8 +4138,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean installKeyPair(ComponentName who, byte[] privKey, byte[] cert, String alias,
|
||||
boolean requestAccess) {
|
||||
public boolean installKeyPair(ComponentName who, byte[] privKey, byte[] cert, byte[] chain,
|
||||
String alias, boolean requestAccess) {
|
||||
enforceCanManageInstalledKeys(who);
|
||||
|
||||
final int callingUid = mInjector.binderGetCallingUid();
|
||||
@@ -4149,7 +4149,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
|
||||
KeyChain.bindAsUser(mContext, UserHandle.getUserHandleForUid(callingUid));
|
||||
try {
|
||||
IKeyChainService keyChain = keyChainConnection.getService();
|
||||
if (!keyChain.installKeyPair(privKey, cert, alias)) {
|
||||
if (!keyChain.installKeyPair(privKey, cert, chain, alias)) {
|
||||
return false;
|
||||
}
|
||||
if (requestAccess) {
|
||||
|
||||
Reference in New Issue
Block a user