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 hasGrantedPolicy(android.content.ComponentName, int);
|
||||||
method public boolean installCaCert(android.content.ComponentName, byte[]);
|
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);
|
||||||
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 isActivePasswordSufficient();
|
||||||
method public boolean isAdminActive(android.content.ComponentName);
|
method public boolean isAdminActive(android.content.ComponentName);
|
||||||
method public boolean isApplicationHidden(android.content.ComponentName, java.lang.String);
|
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 hasGrantedPolicy(android.content.ComponentName, int);
|
||||||
method public boolean installCaCert(android.content.ComponentName, byte[]);
|
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);
|
||||||
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 isActivePasswordSufficient();
|
||||||
method public boolean isAdminActive(android.content.ComponentName);
|
method public boolean isAdminActive(android.content.ComponentName);
|
||||||
method public boolean isApplicationHidden(android.content.ComponentName, java.lang.String);
|
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 hasGrantedPolicy(android.content.ComponentName, int);
|
||||||
method public boolean installCaCert(android.content.ComponentName, byte[]);
|
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);
|
||||||
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 isActivePasswordSufficient();
|
||||||
method public boolean isAdminActive(android.content.ComponentName);
|
method public boolean isAdminActive(android.content.ComponentName);
|
||||||
method public boolean isApplicationHidden(android.content.ComponentName, java.lang.String);
|
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.InvalidKeySpecException;
|
||||||
import java.security.spec.PKCS8EncodedKeySpec;
|
import java.security.spec.PKCS8EncodedKeySpec;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
@@ -2782,7 +2783,7 @@ public class DevicePolicyManager {
|
|||||||
* compromised, certificates it had already installed will be protected.
|
* compromised, certificates it had already installed will be protected.
|
||||||
*
|
*
|
||||||
* <p>If the installer must have access to the credentials, call
|
* <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
|
* @param admin Which {@link DeviceAdminReceiver} this request is associated with, or
|
||||||
* {@code null} if calling from a delegated certificate installer.
|
* {@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,
|
public boolean installKeyPair(@Nullable ComponentName admin, @NonNull PrivateKey privKey,
|
||||||
@NonNull Certificate cert, @NonNull String alias) {
|
@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
|
* 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
|
* certificate chain and corresponding private key for the leaf certificate. All apps within the
|
||||||
* the certificate and use the private key, given direct user approval.
|
* 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
|
* <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
|
* 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
|
* @param admin Which {@link DeviceAdminReceiver} this request is associated with, or
|
||||||
* {@code null} if calling from a delegated certificate installer.
|
* {@code null} if calling from a delegated certificate installer.
|
||||||
* @param privKey The private key to install.
|
* @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
|
* @param alias The private key alias under which to install the certificate. If a certificate
|
||||||
* with that alias already exists, it will be overwritten.
|
* with that alias already exists, it will be overwritten.
|
||||||
* @param requestAccess {@code true} to request that the calling app be granted access to the
|
* @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.
|
* @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
|
* @throws SecurityException if {@code admin} is not {@code null} and not a device or profile
|
||||||
* owner.
|
* owner.
|
||||||
|
* @see android.security.KeyChain#getCertificateChain
|
||||||
*/
|
*/
|
||||||
public boolean installKeyPair(@Nullable ComponentName admin, @NonNull PrivateKey privKey,
|
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 {
|
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())
|
final byte[] pkcs8Key = KeyFactory.getInstance(privKey.getAlgorithm())
|
||||||
.getKeySpec(privKey, PKCS8EncodedKeySpec.class).getEncoded();
|
.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) {
|
} catch (RemoteException e) {
|
||||||
throw e.rethrowFromSystemServer();
|
throw e.rethrowFromSystemServer();
|
||||||
} catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
|
} catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
|
||||||
|
|||||||
@@ -146,7 +146,7 @@ interface IDevicePolicyManager {
|
|||||||
void enforceCanManageCaCerts(in ComponentName admin);
|
void enforceCanManageCaCerts(in ComponentName admin);
|
||||||
|
|
||||||
boolean installKeyPair(in ComponentName who, in byte[] privKeyBuffer, in byte[] certBuffer,
|
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);
|
boolean removeKeyPair(in ComponentName who, String alias);
|
||||||
void choosePrivateKeyAlias(int uid, in Uri uri, in String alias, IBinder aliasCallback);
|
void choosePrivateKeyAlias(int uid, in Uri uri, in String alias, IBinder aliasCallback);
|
||||||
|
|
||||||
|
|||||||
@@ -27,12 +27,13 @@ interface IKeyChainService {
|
|||||||
// APIs used by KeyChain
|
// APIs used by KeyChain
|
||||||
String requestPrivateKey(String alias);
|
String requestPrivateKey(String alias);
|
||||||
byte[] getCertificate(String alias);
|
byte[] getCertificate(String alias);
|
||||||
|
byte[] getCaCertificates(String alias);
|
||||||
|
|
||||||
// APIs used by CertInstaller
|
// APIs used by CertInstaller
|
||||||
void installCaCertificate(in byte[] caCertificate);
|
void installCaCertificate(in byte[] caCertificate);
|
||||||
|
|
||||||
// APIs used by DevicePolicyManager
|
// 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);
|
boolean removeKeyPair(String alias);
|
||||||
|
|
||||||
// APIs used by Settings
|
// APIs used by Settings
|
||||||
|
|||||||
@@ -42,6 +42,8 @@ import java.security.cert.Certificate;
|
|||||||
import java.security.cert.CertificateException;
|
import java.security.cert.CertificateException;
|
||||||
import java.security.cert.CertificateFactory;
|
import java.security.cert.CertificateFactory;
|
||||||
import java.security.cert.X509Certificate;
|
import java.security.cert.X509Certificate;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.concurrent.BlockingQueue;
|
import java.util.concurrent.BlockingQueue;
|
||||||
@@ -389,7 +391,12 @@ public final class KeyChain {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the {@code X509Certificate} chain for the requested
|
* 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
|
* <p> This method may block while waiting for a connection to another process, and must never
|
||||||
* be called from the main thread.
|
* be called from the main thread.
|
||||||
@@ -413,11 +420,31 @@ public final class KeyChain {
|
|||||||
if (certificateBytes == null) {
|
if (certificateBytes == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
X509Certificate leafCert = toCertificate(certificateBytes);
|
||||||
TrustedCertificateStore store = new TrustedCertificateStore();
|
final byte[] certChainBytes = keyChainService.getCaCertificates(alias);
|
||||||
List<X509Certificate> chain = store
|
// If the keypair is installed with a certificate chain by either
|
||||||
.getCertificateChain(toCertificate(certificateBytes));
|
// DevicePolicyManager.installKeyPair or CertInstaller, return that chain.
|
||||||
return chain.toArray(new X509Certificate[chain.size()]);
|
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) {
|
} catch (CertificateException e) {
|
||||||
throw new KeyChainException(e);
|
throw new KeyChainException(e);
|
||||||
} catch (RemoteException 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.
|
* @hide for reuse by CertInstaller and Settings.
|
||||||
* @see KeyChain#bind
|
* @see KeyChain#bind
|
||||||
|
|||||||
@@ -4138,8 +4138,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean installKeyPair(ComponentName who, byte[] privKey, byte[] cert, String alias,
|
public boolean installKeyPair(ComponentName who, byte[] privKey, byte[] cert, byte[] chain,
|
||||||
boolean requestAccess) {
|
String alias, boolean requestAccess) {
|
||||||
enforceCanManageInstalledKeys(who);
|
enforceCanManageInstalledKeys(who);
|
||||||
|
|
||||||
final int callingUid = mInjector.binderGetCallingUid();
|
final int callingUid = mInjector.binderGetCallingUid();
|
||||||
@@ -4149,7 +4149,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
|
|||||||
KeyChain.bindAsUser(mContext, UserHandle.getUserHandleForUid(callingUid));
|
KeyChain.bindAsUser(mContext, UserHandle.getUserHandleForUid(callingUid));
|
||||||
try {
|
try {
|
||||||
IKeyChainService keyChain = keyChainConnection.getService();
|
IKeyChainService keyChain = keyChainConnection.getService();
|
||||||
if (!keyChain.installKeyPair(privKey, cert, alias)) {
|
if (!keyChain.installKeyPair(privKey, cert, chain, alias)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (requestAccess) {
|
if (requestAccess) {
|
||||||
|
|||||||
Reference in New Issue
Block a user