8282 */
8383@ Internal
8484public class TrustedCertificateStore implements ConscryptCertStore {
85+ private static final String PREFIX_MANAGED = "managed:" ;
8586 private static String PREFIX_SYSTEM = "system:" ;
8687 private static final String PREFIX_USER = "user:" ;
8788
89+ public static final boolean isManaged (String alias ) {
90+ return alias .startsWith (PREFIX_MANAGED );
91+ }
8892 public static final boolean isSystem (String alias ) {
8993 return alias .startsWith (PREFIX_SYSTEM );
9094 }
@@ -93,6 +97,7 @@ public static final boolean isUser(String alias) {
9397 }
9498
9599 private static class PreloadHolder {
100+ private static File defaultCaCertsManagedDir ;
96101 private static File defaultCaCertsSystemDir ;
97102 private static File defaultCaCertsAddedDir ;
98103 private static File defaultCaCertsDeletedDir ;
@@ -101,6 +106,7 @@ private static class PreloadHolder {
101106 String ANDROID_ROOT = System .getenv ("ANDROID_ROOT" );
102107 String ANDROID_DATA = System .getenv ("ANDROID_DATA" );
103108 File updatableDir = new File ("/apex/com.android.conscrypt/cacerts" );
109+ defaultCaCertsManagedDir = new File (ANDROID_DATA + "/misc/cacerts_managed" );
104110 if (shouldUseApex (updatableDir )) {
105111 defaultCaCertsSystemDir = updatableDir ;
106112 } else {
@@ -147,20 +153,23 @@ public static void setDefaultUserDirectory(File root) {
147153 PreloadHolder .defaultCaCertsDeletedDir = new File (root , "cacerts-removed" );
148154 }
149155
156+ private final File managedDir ;
150157 private final File systemDir ;
151158 private final File addedDir ;
152159 private final File deletedDir ;
153160
154161 public TrustedCertificateStore () {
155- this (PreloadHolder .defaultCaCertsSystemDir , PreloadHolder .defaultCaCertsAddedDir ,
156- PreloadHolder .defaultCaCertsDeletedDir );
162+ this (PreloadHolder .defaultCaCertsManagedDir , PreloadHolder .defaultCaCertsSystemDir ,
163+ PreloadHolder .defaultCaCertsAddedDir , PreloadHolder . defaultCaCertsDeletedDir );
157164 }
158165
159166 public TrustedCertificateStore (File baseDir ) {
160- this (baseDir , PreloadHolder .defaultCaCertsAddedDir , PreloadHolder .defaultCaCertsDeletedDir );
167+ this (PreloadHolder .defaultCaCertsManagedDir , baseDir , PreloadHolder .defaultCaCertsAddedDir ,
168+ PreloadHolder .defaultCaCertsDeletedDir );
161169 }
162170
163- public TrustedCertificateStore (File systemDir , File addedDir , File deletedDir ) {
171+ public TrustedCertificateStore (File managedDir , File systemDir , File addedDir , File deletedDir ) {
172+ this .managedDir = managedDir ;
164173 this .systemDir = systemDir ;
165174 this .addedDir = addedDir ;
166175 this .deletedDir = deletedDir ;
@@ -173,7 +182,7 @@ public Certificate getCertificate(String alias) {
173182 public Certificate getCertificate (String alias , boolean includeDeletedSystem ) {
174183
175184 File file = fileForAlias (alias );
176- if (file == null || (isUser (alias ) && isTombstone (file ))) {
185+ if (file == null || (isUser (alias ) && isTombstone (file )) || ( isManaged ( alias ) && isTombstone ( file )) ) {
177186 return null ;
178187 }
179188 X509Certificate cert = readCertificate (file );
@@ -193,6 +202,8 @@ private File fileForAlias(String alias) {
193202 File file ;
194203 if (isSystem (alias )) {
195204 file = new File (systemDir , alias .substring (PREFIX_SYSTEM .length ()));
205+ } else if (isManaged (alias )) {
206+ file = new File (managedDir , alias .substring (PREFIX_MANAGED .length ()));
196207 } else if (isUser (alias )) {
197208 file = new File (addedDir , alias .substring (PREFIX_USER .length ()));
198209 } else {
@@ -268,6 +279,7 @@ public Date getCreationDate(String alias) {
268279
269280 public Set <String > aliases () {
270281 Set <String > result = new HashSet <String >();
282+ addAliases (result , PREFIX_MANAGED , managedDir );
271283 addAliases (result , PREFIX_USER , addedDir );
272284 addAliases (result , PREFIX_SYSTEM , systemDir );
273285 return result ;
@@ -292,6 +304,21 @@ private void addAliases(Set<String> result, String prefix, File dir) {
292304 }
293305 }
294306
307+ public Set <String > allManagedAliases () {
308+ Set <String > result = new HashSet <String >();
309+ String [] files = managedDir .list ();
310+ if (files == null ) {
311+ return result ;
312+ }
313+ for (String filename : files ) {
314+ String alias = PREFIX_MANAGED + filename ;
315+ if (containsAlias (alias , true )) {
316+ result .add (alias );
317+ }
318+ }
319+ return result ;
320+ }
321+
295322 public Set <String > allSystemAliases () {
296323 Set <String > result = new HashSet <String >();
297324 String [] files = systemDir .list ();
@@ -324,6 +351,10 @@ public String getCertificateAlias(Certificate c, boolean includeDeletedSystem) {
324351 return null ;
325352 }
326353 X509Certificate x = (X509Certificate ) c ;
354+ File managed = getCertificateFile (managedDir , x );
355+ if (managed .exists ()) {
356+ return PREFIX_MANAGED + managed .getName ();
357+ }
327358 File user = getCertificateFile (addedDir , x );
328359 if (user .exists ()) {
329360 return PREFIX_USER + user .getName ();
@@ -338,6 +369,14 @@ public String getCertificateAlias(Certificate c, boolean includeDeletedSystem) {
338369 return null ;
339370 }
340371
372+ /**
373+ * Returns true to indicate that the certificate was added by the
374+ * device owner, false otherwise.
375+ */
376+ public boolean isManagedCertificate (X509Certificate cert ) {
377+ return getCertificateFile (managedDir , cert ).exists ();
378+ }
379+
341380 /**
342381 * Returns true to indicate that the certificate was added by the
343382 * user, false otherwise.
@@ -383,6 +422,13 @@ public boolean match(X509Certificate ca) {
383422 return ca .getPublicKey ().equals (c .getPublicKey ());
384423 }
385424 };
425+ X509Certificate managed = findCert (managedDir ,
426+ c .getSubjectX500Principal (),
427+ selector ,
428+ X509Certificate .class );
429+ if (managed != null ) {
430+ return managed ;
431+ }
386432 X509Certificate user = findCert (addedDir ,
387433 c .getSubjectX500Principal (),
388434 selector ,
@@ -419,6 +465,10 @@ public boolean match(X509Certificate ca) {
419465 }
420466 };
421467 X500Principal issuer = c .getIssuerX500Principal ();
468+ X509Certificate managed = findCert (managedDir , issuer , selector , X509Certificate .class );
469+ if (managed != null ) {
470+ return managed ;
471+ }
422472 X509Certificate user = findCert (addedDir , issuer , selector , X509Certificate .class );
423473 if (user != null ) {
424474 return user ;
@@ -445,6 +495,10 @@ public boolean match(X509Certificate ca) {
445495 }
446496 };
447497 X500Principal issuer = c .getIssuerX500Principal ();
498+ Set <X509Certificate > managedCerts = findCertSet (managedDir , issuer , selector );
499+ if (managedCerts != null ) {
500+ issuers = managedCerts ;
501+ }
448502 Set <X509Certificate > userAddedCerts = findCertSet (addedDir , issuer , selector );
449503 if (userAddedCerts != null ) {
450504 issuers = userAddedCerts ;
@@ -603,13 +657,21 @@ private File file(File dir, String hash, int index) {
603657 return new File (dir , hash + '.' + index );
604658 }
605659
660+ /**
661+ * @deprecated Use {@link #installCertificate(boolean[], java.security.cert.X509Certificate)} instead.
662+ */
663+ @ Deprecated
664+ public void installCertificate (X509Certificate cert ) throws IOException , CertificateException {
665+ installCertificate (false , cert );
666+ }
667+
606668 /**
607669 * This non-{@code KeyStoreSpi} public interface is used by the
608670 * {@code KeyChainService} to install new CA certificates. It
609671 * silently ignores the certificate if it already exists in the
610672 * store.
611673 */
612- public void installCertificate (X509Certificate cert ) throws IOException , CertificateException {
674+ public void installCertificate (boolean isManaged , X509Certificate cert ) throws IOException , CertificateException {
613675 if (cert == null ) {
614676 throw new NullPointerException ("cert == null" );
615677 }
@@ -628,6 +690,13 @@ public void installCertificate(X509Certificate cert) throws IOException, Certifi
628690 // return taking no further action.
629691 return ;
630692 }
693+ File managed = getCertificateFile (managedDir , cert );
694+ if (isManaged ) {
695+ if (!managed .exists ()) {
696+ writeCertificate (managed , cert );
697+ }
698+ return ;
699+ }
631700 File user = getCertificateFile (addedDir , cert );
632701 if (user .exists ()) {
633702 // we have an already installed user cert, bail.
@@ -667,7 +736,7 @@ public void deleteCertificateEntry(String alias) throws IOException, Certificate
667736 writeCertificate (deleted , cert );
668737 return ;
669738 }
670- if (isUser (alias )) {
739+ if (isUser (alias ) || isManaged ( alias ) ) {
671740 // truncate the file to make a tombstone by opening and closing.
672741 // we need ensure that we don't leave a gap before a valid cert.
673742 new FileOutputStream (file ).close ();
@@ -678,22 +747,32 @@ public void deleteCertificateEntry(String alias) throws IOException, Certificate
678747 }
679748
680749 private void removeUnnecessaryTombstones (String alias ) throws IOException {
681- if (!isUser (alias )) {
750+ if (!isUser (alias ) && ! isManaged ( alias ) ) {
682751 throw new AssertionError (alias );
683752 }
684753 int dotIndex = alias .lastIndexOf ('.' );
685754 if (dotIndex == -1 ) {
686755 throw new AssertionError (alias );
687756 }
688757
689- String hash = alias .substring (PREFIX_USER .length (), dotIndex );
758+ File dir = null ;
759+ String hash = null ;
760+ if (isUser (alias )) {
761+ dir = addedDir ;
762+ hash = alias .substring (PREFIX_USER .length (), dotIndex );
763+ } else if (isManaged (alias )) {
764+ dir = managedDir ;
765+ hash = alias .substring (PREFIX_MANAGED .length (), dotIndex );
766+ } else {
767+ throw new AssertionError (alias );
768+ }
690769 int lastTombstoneIndex = Integer .parseInt (alias .substring (dotIndex + 1 ));
691770
692- if (file (addedDir , hash , lastTombstoneIndex + 1 ).exists ()) {
771+ if (file (dir , hash , lastTombstoneIndex + 1 ).exists ()) {
693772 return ;
694773 }
695774 while (lastTombstoneIndex >= 0 ) {
696- File file = file (addedDir , hash , lastTombstoneIndex );
775+ File file = file (dir , hash , lastTombstoneIndex );
697776 if (!isTombstone (file )) {
698777 break ;
699778 }
0 commit comments