Merge "Require actual certificates for testNoAddedCertificates."
diff --git a/tests/tests/security/src/android/security/cts/CertificateTest.java b/tests/tests/security/src/android/security/cts/CertificateTest.java
index e6e2a2b..3db7aca 100644
--- a/tests/tests/security/src/android/security/cts/CertificateTest.java
+++ b/tests/tests/security/src/android/security/cts/CertificateTest.java
@@ -16,9 +16,10 @@
 
 package android.security.cts;
 
-import android.test.AndroidTestCase;
+import android.content.res.AssetManager;
+import android.test.InstrumentationTestCase;
 
-import java.io.FileInputStream;
+import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
 import java.security.KeyStore;
@@ -27,6 +28,7 @@
 import java.security.NoSuchAlgorithmException;
 import java.security.cert.CertificateEncodingException;
 import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
 import java.security.cert.X509Certificate;
 import java.util.Arrays;
 import java.util.Collections;
@@ -34,7 +36,7 @@
 import java.util.List;
 import java.util.Set;
 
-public class CertificateTest extends AndroidTestCase {
+public class CertificateTest extends InstrumentationTestCase {
 
     public void testNoRemovedCertificates() throws Exception {
         Set<String> expectedCertificates = new HashSet<String>(
@@ -45,16 +47,58 @@
     }
 
     /**
-     * {@see OEMCertificateWhitelist#OEM_CERTIFICATE_WHITELIST} for more information on this test.
+     * If you fail CTS as a result of adding a root CA that is not part of the Android root CA
+     * store, please see the following.
+     *
+     * First, this test exists because adding untrustworthy root CAs to a device has a very
+     * significant security impact. In the worst case, adding a rogue CA can permanently compromise
+     * the confidentiality and integrity of your users' network traffic. Because of this risk,
+     * adding new certificates should be done sparingly and as a last resort -- never as a first
+     * response or short term fix. Before attempting to modify this test, please consider whether
+     * adding a new certificate authority is in your users' best interests.
+     *
+     * Second, because the addition of a new root CA by an OEM can have such dire consequences for
+     * so many people it is imperative that it be done transparently and in the open. Any request to
+     * modify the certificate list used by this test must have a corresponding change in AOSP
+     * (one certificate per change) authored by the OEM in question and including:
+     *
+     *     - the certificate in question:
+     *       - The certificate must be in a file under
+     *         cts/tests/tests/security/assets/oem_cacerts, in PEM (Privacy-enhanced Electronic
+     *         Mail) format, with the textual representation of the certificate following the PEM
+     *         section.
+     *       - The file name must be in the format of <hash>.<n> where "hash" is the subject hash
+     *         produced by:
+     *           openssl x509 -in cert_file -subject_hash -noout
+     *         and the "n" is a unique integer identifier starting at 0 to deal with collisions.
+     *         See OpenSSL's c_rehash manpage for details.
+     *       - cts/tests/tests/security/tools/format_cert.sh helps meet the above requirements.
+     *
+     *     - information about who created and maintains both the certificate and the corresponding
+     *       keypair.
+     *
+     *     - information about what the certificate is to be used for and why the certificate is
+     *       appropriate for inclusion.
+     *
+     *     - a statement from the OEM indicating that they have sufficient confidence in the
+     *       security of the key, the security practices of the issuer, and the validity of the
+     *       intended use that they believe adding the certificate is not detrimental to the
+     *       security of the user.
+     *
+     * Finally, please note that this is not the usual process for adding root CAs to Android. If
+     * you have a certificate that you believe should be present on all Android devices, please file
+     * a public bug at https://code.google.com/p/android/issues/entry or http://b.android.com to
+     * seek resolution.
+     *
+     * For questions, comments, and code reviews please contact security@android.com.
      */
     public void testNoAddedCertificates() throws Exception {
-        Set<String> oemCertificateWhitelist = new HashSet<String>(
-                Arrays.asList(OEMCertificateWhitelist.OEM_CERTIFICATE_WHITELIST));
+        Set<String> oemWhitelistedCertificates = getOemWhitelistedCertificates();
         Set<String> expectedCertificates = new HashSet<String>(
                 Arrays.asList(CertificateData.CERTIFICATE_DATA));
         Set<String> deviceCertificates = getDeviceCertificates();
         deviceCertificates.removeAll(expectedCertificates);
-        deviceCertificates.removeAll(oemCertificateWhitelist);
+        deviceCertificates.removeAll(oemWhitelistedCertificates);
         assertEquals("Unknown CA certificates", Collections.EMPTY_SET, deviceCertificates);
     }
 
@@ -88,6 +132,30 @@
         return certificates;
     }
 
+    private static final String ASSETS_DIR_OEM_CERTS = "oem_cacerts";
+
+    private Set<String> getOemWhitelistedCertificates() throws Exception {
+        Set<String> certificates = new HashSet<String>();
+        CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
+        AssetManager assetManager = getInstrumentation().getContext().getAssets();
+        for (String path : assetManager.list(ASSETS_DIR_OEM_CERTS)) {
+            File certAssetFile = new File(ASSETS_DIR_OEM_CERTS, path);
+            InputStream in = null;
+            try {
+                in = assetManager.open(certAssetFile.toString());
+                X509Certificate certificate = (X509Certificate) certFactory.generateCertificate(in);
+                certificates.add(getFingerprint(certificate));
+            } catch (Exception e) {
+                throw new Exception("Failed to load certificate from asset: " + certAssetFile, e);
+            } finally {
+                if (in != null) {
+                    in.close();
+                }
+            }
+        }
+        return certificates;
+    }
+
     private String getFingerprint(X509Certificate certificate) throws CertificateEncodingException,
             NoSuchAlgorithmException {
         MessageDigest messageDigest = MessageDigest.getInstance("SHA1");
diff --git a/tests/tests/security/src/android/security/cts/OEMCertificateWhitelist.java b/tests/tests/security/src/android/security/cts/OEMCertificateWhitelist.java
deleted file mode 100644
index 024c15f..0000000
--- a/tests/tests/security/src/android/security/cts/OEMCertificateWhitelist.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.security.cts;
-
-class OEMCertificateWhitelist {
-
-  /**
-   * If you fail CTS as a result of adding a root CA that is not part
-   * of the Android root CA store, please see the following.
-   *
-   * First, this test exists because adding untrustworthy root CAs
-   * to a device has a very significant security impact. In the worst
-   * case, adding a rogue CA to this list can permanently compromise
-   * the confidentiality and integrity of your users' network traffic.
-   * Because of this risk, adding new certificates should be done
-   * sparingly and as a last resort- never as a first response or
-   * short term fix. Before attempting to modify this test, please
-   * consider whether adding a new certificate authority is in your
-   * users' best interests.
-   *
-   * Second, because the addition of a new root CA by an OEM can have
-   * such dire consequences for so many people it is imperative that
-   * it be done transparently and in the open. Any request to modify
-   * this list must have a corresponding change in AOSP authored by
-   * the OEM in question and including:
-   *
-   *     - the certificate in question.
-   *
-   *     - information about who created and maintains
-   *       both the certificate and the corresponding keypair.
-   *
-   *     - information about what the certificate is to be used
-   *       for and why the certificate is appropriate for inclusion.
-   *
-   *     - a statement from the OEM indicating that they have
-   *       sufficient confidence in the security of the key, the
-   *       security practices of the issuer, and the validity
-   *       of the intended use that they believe adding the
-   *       certificate is not detrimental to the security of the
-   *       user.
-   *
-   * Finally, please note that this is not the usual process for
-   * adding root CAs to Android. If you have a certificate that you
-   * believe should be present on all Android devices, please file a
-   * public bug at https://code.google.com/p/android/issues/entry or
-   * http://b.android.com to seek resolution.
-   *
-   * For questions, comments, and code reviews please contact
-   * security@android.com.
-   */
-  static final String[] OEM_CERTIFICATE_WHITELIST = {};
-
-}
diff --git a/tests/tests/security/tools/format_cert.sh b/tests/tests/security/tools/format_cert.sh
new file mode 100755
index 0000000..94407a0
--- /dev/null
+++ b/tests/tests/security/tools/format_cert.sh
@@ -0,0 +1,55 @@
+#!/bin/sh
+
+# Outputs the provided certificate (PEM or DER) in a format used by CTS tests.
+# The format is PEM block, followed by the textual representation of the
+# certificate, followed by the SHA-1 fingerprint.
+
+# OpenSSL binary built from this Android source
+OPENSSL="$ANDROID_HOST_OUT/bin/openssl"
+if [ "$ANDROID_HOST_OUT" == "" ]; then
+  echo "Android build environment not set up"
+  echo
+  echo "Run the following from the root of the Android source tree:"
+  echo "  . build/envsetup.sh && lunch"
+  exit 1
+fi
+if [ ! -f "$OPENSSL" ]; then
+  echo "openssl binary not found"
+  echo
+  echo "Run 'mmm external/openssl' or 'make openssl' from the root of the" \
+      "Android source tree to build it."
+  exit 1
+fi
+
+# Input file containing the certificate in PEM or DER format
+in_file="$1"
+
+# Output file. If not specified, the file will be named <hash>.0 where "hash"
+# is the certificate's subject hash produced by:
+#   openssl x509 -in cert_file -subject_hash -noout
+out_file="$2"
+
+# Detect whether the input file is PEM or DER.
+in_form="pem"
+subject_hash=$("$OPENSSL" x509 -in "$in_file" -inform $in_form -subject_hash \
+    -noout 2>/dev/null)
+if [ "$?" != "0" ]; then
+  in_form="der"
+  subject_hash=$("$OPENSSL" x509 -in "$in_file" -inform $in_form -subject_hash \
+      -noout)
+  if [ "$?" != "0" ]; then
+    echo "Certificate file format is neither PEM nor DER"
+    exit 1
+  fi
+fi
+
+# Name the output file <hash>.0 if the name is not specified explicitly.
+if [ "$out_file" == "" ]; then
+  out_file="$subject_hash.0"
+  echo "Auto-generated output file name: $out_file"
+fi
+
+# Output the certificate in the target format
+"$OPENSSL" x509 -in "$in_file" -inform $in_form -outform pem > "$out_file" && \
+"$OPENSSL" x509 -in "$in_file" -inform $in_form -noout -text -fingerprint \
+    >> "$out_file"