Merge "Add tests for alg name aliases"
diff --git a/luni/src/main/java/org/apache/harmony/security/utils/AlgNameMapper.java b/luni/src/main/java/org/apache/harmony/security/utils/AlgNameMapper.java
index c1d73df..8df02c9 100644
--- a/luni/src/main/java/org/apache/harmony/security/utils/AlgNameMapper.java
+++ b/luni/src/main/java/org/apache/harmony/security/utils/AlgNameMapper.java
@@ -87,14 +87,33 @@
 
     static {
         for (String[] element : knownAlgMappings) {
-            String algUC = element[1].toUpperCase(Locale.US);
-            alg2OidMap.put(algUC, element[0]);
-            oid2AlgMap.put(element[0], algUC);
-            // map upper case alg name to its original name
-            algAliasesMap.put(algUC, element[1]);
+            addMapping(element[0], element[1]);
         }
     }
 
+    /**
+     * Adds a mapping to the internal table. Will overwrite any entry already
+     * present. Visible for testing.
+     */
+    public static void addMapping(String oid, String name) {
+        String nameUC = name.toUpperCase(Locale.ROOT);
+        alg2OidMap.put(nameUC, oid);
+        oid2AlgMap.put(oid, nameUC);
+        // map upper case alg name to its original name
+        algAliasesMap.put(nameUC, name);
+    }
+
+    /**
+     * Removes a mapping from the internal table. Visible for testing.
+     */
+    public static void removeMapping(String oid, String name) {
+        String nameUC = name.toUpperCase(Locale.ROOT);
+        alg2OidMap.remove(nameUC);
+        oid2AlgMap.remove(oid);
+        // map upper case alg name to its original name
+        algAliasesMap.remove(nameUC);
+    }
+
     // No instances
     private AlgNameMapper() {
     }
@@ -126,7 +145,7 @@
         checkCacheVersion();
 
         // alg2OidMap map contains upper case keys
-        String result = alg2OidMap.get(algName.toUpperCase(Locale.US));
+        String result = alg2OidMap.get(algName.toUpperCase(Locale.ROOT));
         if (result != null) {
             return result;
         }
@@ -144,7 +163,7 @@
      * Returns algName for OID
      *
      * @param oid OID to be mapped
-     * @return algorithm name
+     * @return algorithm name or {@code null} if not found.
      */
     public static String map2AlgName(String oid) {
         checkCacheVersion();
@@ -172,7 +191,7 @@
      * @return algorithm name
      */
     public static String getStandardName(String algName) {
-        return algAliasesMap.get(algName.toUpperCase(Locale.US));
+        return algAliasesMap.get(algName.toUpperCase(Locale.ROOT));
     }
 
     // Searches given provider for mappings like
@@ -185,11 +204,11 @@
         for (String service : serviceName) {
             String keyPrfix2find = "Alg.Alias." + service + ".";
             for (Entry<Object, Object> me : entrySet) {
-                String key = (String)me.getKey();
+                String key = (String) me.getKey();
                 if (key.startsWith(keyPrfix2find)) {
                     String alias = key.substring(keyPrfix2find.length());
-                    String alg = (String)me.getValue();
-                    String algUC = alg.toUpperCase(Locale.US);
+                    String alg = (String) me.getValue();
+                    String algUC = alg.toUpperCase(Locale.ROOT);
                     if (isOID(alias)) {
                         if (alias.startsWith("OID.")) {
                             alias = alias.substring(4);
@@ -207,9 +226,9 @@
                             // map upper case alg name to its original name
                             algAliasesMap.put(algUC, alg);
                         }
-                           // Do not override known standard names
-                    } else if (!algAliasesMap.containsKey(alias.toUpperCase(Locale.US))) {
-                        algAliasesMap.put(alias.toUpperCase(Locale.US), alg);
+                    // Do not override known standard names
+                    } else if (!algAliasesMap.containsKey(alias.toUpperCase(Locale.ROOT))) {
+                        algAliasesMap.put(alias.toUpperCase(Locale.ROOT), alg);
                     }
                 }
             }
diff --git a/luni/src/test/java/libcore/java/security/cert/FakeOidProvider.java b/luni/src/test/java/libcore/java/security/cert/FakeOidProvider.java
new file mode 100644
index 0000000..3e9183a
--- /dev/null
+++ b/luni/src/test/java/libcore/java/security/cert/FakeOidProvider.java
@@ -0,0 +1,69 @@
+package libcore.java.security.cert;
+
+import java.security.InvalidKeyException;
+import java.security.InvalidParameterException;
+import java.security.PrivateKey;
+import java.security.Provider;
+import java.security.PublicKey;
+import java.security.SignatureException;
+import java.security.SignatureSpi;
+
+public class FakeOidProvider extends Provider {
+    /**
+     * Used for testing some effects of algorithm OID mapping. We have to be
+     * slightly careful of the OID we pick here: the first number has to be 0,
+     * 1, or 2, and the second number has to be less than 39.
+     */
+    public static final String SIGALG_OID = "1.2.34359737229.1.1.5";
+
+    /**
+     * Used for testing some effects of algorithm OID mapping.
+     */
+    public static final String SIGALG_OID_NAME = "FAKEwithFAKE";
+
+    public static final String PROVIDER_NAME = "FakeOidProvider";
+
+    protected FakeOidProvider() {
+        super(PROVIDER_NAME, 1.0, "Fake OID Provider for Tests");
+
+        put("Signature." + SIGALG_OID, FakeOidSignature.class.getName());
+    }
+
+    public static class FakeOidSignature extends SignatureSpi {
+        @Override
+        protected void engineInitVerify(PublicKey publicKey) throws InvalidKeyException {
+        }
+
+        @Override
+        protected void engineInitSign(PrivateKey privateKey) throws InvalidKeyException {
+        }
+
+        @Override
+        protected void engineUpdate(byte b) throws SignatureException {
+        }
+
+        @Override
+        protected void engineUpdate(byte[] b, int off, int len) throws SignatureException {
+        }
+
+        @Override
+        protected byte[] engineSign() throws SignatureException {
+            return null;
+        }
+
+        @Override
+        protected boolean engineVerify(byte[] sigBytes) throws SignatureException {
+            return true;
+        }
+
+        @Override
+        protected void engineSetParameter(String param, Object value)
+                throws InvalidParameterException {
+        }
+
+        @Override
+        protected Object engineGetParameter(String param) throws InvalidParameterException {
+            return null;
+        }
+    }
+}
diff --git a/luni/src/test/java/libcore/java/security/cert/X509CertificateTest.java b/luni/src/test/java/libcore/java/security/cert/X509CertificateTest.java
index c35f8e6..5ddbed3 100644
--- a/luni/src/test/java/libcore/java/security/cert/X509CertificateTest.java
+++ b/luni/src/test/java/libcore/java/security/cert/X509CertificateTest.java
@@ -16,6 +16,8 @@
 
 package libcore.java.security.cert;
 
+import org.apache.harmony.security.utils.AlgNameMapper;
+
 import tests.support.resource.Support_Resources;
 
 import java.io.BufferedInputStream;
@@ -294,6 +296,8 @@
                 generateCertificates_PKCS7_PEM_TrailingData(f);
                 generateCertificates_PKCS7_DER_TrailingData(f);
                 test_Serialization(f);
+                test_UnknownUnmappedKeyOID(f);
+                test_UnknownMappedKeyOID(f);
             } catch (Throwable e) {
                 out.append("Error encountered checking " + p.getName() + "\n");
                 e.printStackTrace(out);
@@ -1174,7 +1178,7 @@
             Collection<? extends X509Certificate> certs = (Collection<? extends X509Certificate>)
                     f.generateCertificates(bais);
             if (StandardNames.IS_RI) {
-                fail("RI fails on this test.");
+                return;
             }
         } catch (CertificateParsingException e) {
             if (StandardNames.IS_RI) {
@@ -1204,7 +1208,7 @@
             Collection<? extends X509Certificate> certs = (Collection<? extends X509Certificate>)
                     f.generateCertificates(bais);
             if (StandardNames.IS_RI) {
-                fail("RI fails on this test.");
+                return;
             }
         } catch (CertificateParsingException e) {
             if (StandardNames.IS_RI) {
@@ -1249,12 +1253,7 @@
         Collection<? extends X509Certificate> certs = (Collection<? extends X509Certificate>)
                 f.generateCertificates(bais);
 
-        // RI is broken
-        if (StandardNames.IS_RI) {
-            assertEquals(0, bais.available());
-        } else {
-            assertEquals(4096, bais.available());
-        }
+        assertEquals(4096, bais.available());
     }
 
     private void test_Serialization(CertificateFactory f) throws Exception {
@@ -1284,6 +1283,89 @@
         }
     }
 
+    private void test_UnknownUnmappedKeyOID(CertificateFactory f) throws Exception {
+        byte[] certBytes = generateFakeOidCertificate();
+
+        {
+            X509Certificate cert = (X509Certificate) f
+                    .generateCertificate(new ByteArrayInputStream(certBytes));
+            assertEquals(FakeOidProvider.SIGALG_OID, cert.getSigAlgOID());
+            assertEquals(FakeOidProvider.SIGALG_OID, cert.getSigAlgName());
+        }
+    }
+
+    private void test_UnknownMappedKeyOID(CertificateFactory f) throws Exception {
+        AlgNameMapper.addMapping(FakeOidProvider.SIGALG_OID, FakeOidProvider.SIGALG_OID_NAME);
+
+        Security.addProvider(new FakeOidProvider());
+        try {
+            byte[] certBytes = generateFakeOidCertificate();
+
+            // Make sure the certificate is outputting something.
+            X509Certificate cert = (X509Certificate) f
+                    .generateCertificate(new ByteArrayInputStream(certBytes));
+            assertEquals(FakeOidProvider.SIGALG_OID, cert.getSigAlgOID());
+            if ("AndroidOpenSSL".equals(f.getProvider().getName())) {
+                // AndroidOpenSSL provider has a connection to AlgNameMapper, so
+                // we expect it to get our special name.
+                assertEquals(FakeOidProvider.SIGALG_OID_NAME, cert.getSigAlgName());
+            } else {
+                assertNotNull(cert.getSigAlgName());
+            }
+
+            cert.verify(cert.getPublicKey());
+        } finally {
+            AlgNameMapper
+                    .removeMapping(FakeOidProvider.SIGALG_OID, FakeOidProvider.SIGALG_OID_NAME);
+            Security.removeProvider(FakeOidProvider.PROVIDER_NAME);
+        }
+    }
+
+    private byte[] generateFakeOidCertificate() throws IOException {
+        byte[] certBytes;
+
+        // Read in the original cert.
+        {
+            InputStream is = null;
+            try {
+                is = Support_Resources.getStream(CERT_RSA);
+
+                ByteArrayOutputStream baos = new ByteArrayOutputStream();
+                byte[] buffer = new byte[2048];
+                int numRead;
+                while ((numRead = is.read(buffer, 0, buffer.length)) != -1) {
+                    baos.write(buffer, 0, numRead);
+                }
+                certBytes = baos.toByteArray();
+            } finally {
+                if (is != null) {
+                    try {
+                        is.close();
+                    } catch (IOException ignored) {
+                    }
+                }
+            }
+        }
+
+        // Fix the OID for the certificate.
+        {
+            int numFixed = 0;
+            for (int i = 0; i < certBytes.length - 5; i++) {
+                if (certBytes[i] == (byte) 0x2A && certBytes[i + 1] == (byte) 0x86
+                        && certBytes[i + 2] == (byte) 0x48 && certBytes[i + 3] == (byte) 0x86
+                        && certBytes[i + 4] == (byte) 0xF7) {
+                    certBytes[i + 1] = (byte) 0xFF;
+                    certBytes[i + 2] = (byte) 0xFF;
+                    certBytes[i + 3] = (byte) 0xFF;
+                    i += 4;
+                    numFixed++;
+                }
+            }
+            assertEquals(3, numFixed);
+        }
+        return certBytes;
+    }
+
     @Override
     protected void setUp() throws Exception {
         super.setUp();