diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index be831d7a2d2b4..e0b1c00223414 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -32,10 +32,17 @@ import android.os.ServiceManager; import android.os.UserHandle; import android.util.Log; +import com.android.org.conscrypt.TrustedCertificateStore; + +import java.io.ByteArrayInputStream; import java.io.IOException; import java.net.InetSocketAddress; import java.net.Proxy; +import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; +import java.security.cert.X509Certificate; import java.util.List; +import java.util.Set; /** * Public interface for managing policies enforced on a device. Most clients @@ -1327,6 +1334,70 @@ public class DevicePolicyManager { return ENCRYPTION_STATUS_UNSUPPORTED; } + /** + * Installs the given certificate as a User CA. + * + * @return false if the certBuffer cannot be parsed or installation is + * interrupted, otherwise true + * @hide + */ + public boolean installCaCert(byte[] certBuffer) { + if (mService != null) { + try { + return mService.installCaCert(certBuffer); + } catch (RemoteException e) { + Log.w(TAG, "Failed talking with device policy service", e); + } + } + return false; + } + + /** + * Uninstalls the given certificate from the list of User CAs, if present. + * + * @hide + */ + public void uninstallCaCert(byte[] certBuffer) { + if (mService != null) { + try { + mService.uninstallCaCert(certBuffer); + } catch (RemoteException e) { + Log.w(TAG, "Failed talking with device policy service", e); + } + } + } + + /** + * Returns whether there are any user-installed CA certificates. + * + * @hide + */ + public boolean hasAnyCaCertsInstalled() { + TrustedCertificateStore certStore = new TrustedCertificateStore(); + Set aliases = certStore.userAliases(); + return aliases != null && !aliases.isEmpty(); + } + + /** + * Returns whether this certificate has been installed as a User CA. + * + * @hide + */ + public boolean hasCaCertInstalled(byte[] certBuffer) { + TrustedCertificateStore certStore = new TrustedCertificateStore(); + String alias; + byte[] pemCert; + try { + CertificateFactory certFactory = CertificateFactory.getInstance("X.509"); + X509Certificate cert = (X509Certificate) certFactory.generateCertificate( + new ByteArrayInputStream(certBuffer)); + return certStore.getCertificateAlias(cert) != null; + } catch (CertificateException ce) { + Log.w(TAG, "Could not parse certificate", ce); + } + return false; + } + /** * Called by an application that is administering the device to disable all cameras * on the device. After setting this, no applications will be able to access any cameras diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl index 9659a91f7098c..172c47c2a94b5 100644 --- a/core/java/android/app/admin/IDevicePolicyManager.aidl +++ b/core/java/android/app/admin/IDevicePolicyManager.aidl @@ -102,4 +102,7 @@ interface IDevicePolicyManager { boolean isDeviceOwner(String packageName); String getDeviceOwner(); String getDeviceOwnerName(); + + boolean installCaCert(in byte[] certBuffer); + void uninstallCaCert(in byte[] certBuffer); } diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 1363e3ca2a5a7..12e33d53ee20d 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -1617,6 +1617,14 @@ android:label="@string/permlab_anyCodecForPlayback" android:description="@string/permdesc_anyCodecForPlayback" /> + + + diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index e497c85c87965..5f5564c62d6de 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -1128,6 +1128,11 @@ Allows the app to use any installed media decoder to decode for playback. + + manage trusted credentials + + Allows the app to install and uninstall CA certificates as trusted credentials. + read/write to resources owned by diag diff --git a/keystore/java/android/security/KeyChain.java b/keystore/java/android/security/KeyChain.java index 328ac5d882ebc..9ea325a498023 100644 --- a/keystore/java/android/security/KeyChain.java +++ b/keystore/java/android/security/KeyChain.java @@ -443,7 +443,10 @@ public final class KeyChain { } @Override public void onServiceDisconnected(ComponentName name) {} }; - boolean isBound = context.bindService(new Intent(IKeyChainService.class.getName()), + Intent intent = new Intent(IKeyChainService.class.getName()); + ComponentName comp = intent.resolveSystemService(context.getPackageManager(), 0); + intent.setComponent(comp); + boolean isBound = context.bindService(intent, keyChainServiceConnection, Context.BIND_AUTO_CREATE); if (!isBound) { diff --git a/services/java/Android.mk b/services/java/Android.mk index 95b28d981b6f3..8c3d0f09197ca 100644 --- a/services/java/Android.mk +++ b/services/java/Android.mk @@ -11,7 +11,7 @@ LOCAL_SRC_FILES := \ LOCAL_MODULE:= services -LOCAL_JAVA_LIBRARIES := android.policy telephony-common +LOCAL_JAVA_LIBRARIES := android.policy conscrypt telephony-common include $(BUILD_JAVA_LIBRARY) diff --git a/services/java/com/android/server/DevicePolicyManagerService.java b/services/java/com/android/server/DevicePolicyManagerService.java index 43f95c3d7615e..7e833965c38a5 100644 --- a/services/java/com/android/server/DevicePolicyManagerService.java +++ b/services/java/com/android/server/DevicePolicyManagerService.java @@ -16,11 +16,14 @@ package com.android.server; +import static android.Manifest.permission.MANAGE_CA_CERTIFICATES; + import com.android.internal.os.storage.ExternalStorageFormatter; import com.android.internal.util.FastXmlSerializer; import com.android.internal.util.JournaledFile; import com.android.internal.util.XmlUtils; import com.android.internal.widget.LockPatternUtils; +import com.android.org.conscrypt.TrustedCertificateStore; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; @@ -49,6 +52,7 @@ import android.content.pm.Signature; import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.ResolveInfo; import android.net.Uri; +import android.os.AsyncTask; import android.os.Binder; import android.os.Bundle; import android.os.Environment; @@ -66,7 +70,12 @@ import android.os.SystemProperties; import android.os.UserHandle; import android.os.UserManager; import android.provider.Settings; +import android.security.Credentials; +import android.security.IKeyChainService; +import android.security.KeyChain; +import android.security.KeyChain.KeyChainConnection; import android.util.AtomicFile; +import android.util.Log; import android.util.PrintWriterPrinter; import android.util.Printer; import android.util.Slog; @@ -75,6 +84,7 @@ import android.util.Xml; import android.view.IWindowManager; import android.view.WindowManagerPolicy; +import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileDescriptor; import java.io.FileInputStream; @@ -82,8 +92,14 @@ import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.PrintWriter; +import java.security.KeyStore.TrustedCertificateEntry; +import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; +import java.security.cert.X509Certificate; import java.text.DateFormat; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; import java.util.Date; import java.util.HashMap; import java.util.List; @@ -1870,6 +1886,76 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { return !"".equals(state); } + public boolean installCaCert(byte[] certBuffer) throws RemoteException { + mContext.enforceCallingOrSelfPermission(MANAGE_CA_CERTIFICATES, null); + KeyChainConnection keyChainConnection = null; + byte[] pemCert; + try { + X509Certificate cert = parseCert(certBuffer); + pemCert = Credentials.convertToPem(cert); + } catch (CertificateException ce) { + Log.e(TAG, "Problem converting cert", ce); + return false; + } catch (IOException ioe) { + Log.e(TAG, "Problem reading cert", ioe); + return false; + } + try { + keyChainConnection = KeyChain.bind(mContext); + try { + keyChainConnection.getService().installCaCertificate(pemCert); + return true; + } finally { + if (keyChainConnection != null) { + keyChainConnection.close(); + keyChainConnection = null; + } + } + } catch (InterruptedException e1) { + Log.w(TAG, "installCaCertsToKeyChain(): ", e1); + Thread.currentThread().interrupt(); + } + return false; + } + + private static X509Certificate parseCert(byte[] certBuffer) + throws CertificateException, IOException { + CertificateFactory certFactory = CertificateFactory.getInstance("X.509"); + return (X509Certificate) certFactory.generateCertificate(new ByteArrayInputStream( + certBuffer)); + } + + public void uninstallCaCert(final byte[] certBuffer) { + mContext.enforceCallingOrSelfPermission(MANAGE_CA_CERTIFICATES, null); + TrustedCertificateStore certStore = new TrustedCertificateStore(); + String alias = null; + try { + X509Certificate cert = parseCert(certBuffer); + alias = certStore.getCertificateAlias(cert); + } catch (CertificateException ce) { + Log.e(TAG, "Problem creating X509Certificate", ce); + return; + } catch (IOException ioe) { + Log.e(TAG, "Problem reading certificate", ioe); + return; + } + try { + KeyChainConnection keyChainConnection = KeyChain.bind(mContext); + IKeyChainService service = keyChainConnection.getService(); + try { + service.deleteCaCertificate(alias); + } catch (RemoteException e) { + Log.e(TAG, "from CaCertUninstaller: ", e); + } finally { + keyChainConnection.close(); + keyChainConnection = null; + } + } catch (InterruptedException ie) { + Log.w(TAG, "CaCertUninstaller: ", ie); + Thread.currentThread().interrupt(); + } + } + void wipeDataLocked(int flags) { // If the SD card is encrypted and non-removable, we have to force a wipe. boolean forceExtWipe = !Environment.isExternalStorageRemovable() && isExtStorageEncrypted();