1/*
2 *  Licensed to the Apache Software Foundation (ASF) under one or more
3 *  contributor license agreements.  See the NOTICE file distributed with
4 *  this work for additional information regarding copyright ownership.
5 *  The ASF licenses this file to You under the Apache License, Version 2.0
6 *  (the "License"); you may not use this file except in compliance with
7 *  the License.  You may obtain a copy of the License at
8 *
9 *     http://www.apache.org/licenses/LICENSE-2.0
10 *
11 *  Unless required by applicable law or agreed to in writing, software
12 *  distributed under the License is distributed on an "AS IS" BASIS,
13 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 *  See the License for the specific language governing permissions and
15 *  limitations under the License.
16 */
17
18/**
19* @author Vladimir N. Molotkov
20* @version $Revision$
21*/
22
23package org.apache.harmony.security.utils;
24
25import java.security.Provider;
26import java.security.Security;
27import java.util.HashMap;
28import java.util.Map;
29import java.util.Set;
30import java.util.Map.Entry;
31
32import org.apache.harmony.security.Util;
33import org.apache.harmony.security.asn1.ObjectIdentifier;
34
35/**
36 * Provides Algorithm Name to OID and OID to Algorithm Name mappings. Some known
37 * mappings are hardcoded. Tries to obtain additional mappings from installed
38 * providers during initialization.
39 */
40public class AlgNameMapper {
41
42    // Will search OID mappings for these services
43    private static final String[] serviceName = {
44            "Cipher", //$NON-NLS-1$
45            "AlgorithmParameters", //$NON-NLS-1$
46            "Signature" //$NON-NLS-1$
47    };
48
49    // These mappings CAN NOT be overridden
50    // by the ones from available providers
51    // during maps initialization
52    // (source: http://asn1.elibel.tm.fr):
53    private static final String[][] knownAlgMappings = {
54        {"1.2.840.10040.4.1",       "DSA"}, //$NON-NLS-1$ //$NON-NLS-2$
55        {"1.2.840.10040.4.3",       "SHA1withDSA"}, //$NON-NLS-1$ //$NON-NLS-2$
56        {"1.2.840.113549.1.1.1",    "RSA"}, //$NON-NLS-1$ //$NON-NLS-2$
57        // BEGIN android-removed
58        // Dropping MD2
59        // {"1.2.840.113549.1.1.2",    "MD2withRSA"}, //$NON-NLS-1$ //$NON-NLS-2$
60        // END android-removed
61        {"1.2.840.113549.1.1.4",    "MD5withRSA"}, //$NON-NLS-1$ //$NON-NLS-2$
62        {"1.2.840.113549.1.1.5",    "SHA1withRSA"}, //$NON-NLS-1$ //$NON-NLS-2$
63        {"1.2.840.113549.1.3.1",    "DiffieHellman"}, //$NON-NLS-1$ //$NON-NLS-2$
64        {"1.2.840.113549.1.5.3",    "pbeWithMD5AndDES-CBC"}, //$NON-NLS-1$ //$NON-NLS-2$
65        {"1.2.840.113549.1.12.1.3", "pbeWithSHAAnd3-KeyTripleDES-CBC"}, //$NON-NLS-1$ //$NON-NLS-2$
66        {"1.2.840.113549.1.12.1.6", "pbeWithSHAAnd40BitRC2-CBC"}, //$NON-NLS-1$ //$NON-NLS-2$
67        {"1.2.840.113549.3.2",      "RC2-CBC"}, //$NON-NLS-1$ //$NON-NLS-2$
68        {"1.2.840.113549.3.3",      "RC2-EBC"}, //$NON-NLS-1$ //$NON-NLS-2$
69        {"1.2.840.113549.3.4",      "RC4"}, //$NON-NLS-1$ //$NON-NLS-2$
70        {"1.2.840.113549.3.5",      "RC4WithMAC"}, //$NON-NLS-1$ //$NON-NLS-2$
71        {"1.2.840.113549.3.6",      "DESx-CBC"}, //$NON-NLS-1$ //$NON-NLS-2$
72        {"1.2.840.113549.3.7",      "TripleDES-CBC"}, //$NON-NLS-1$ //$NON-NLS-2$
73        {"1.2.840.113549.3.8",      "rc5CBC"}, //$NON-NLS-1$ //$NON-NLS-2$
74        {"1.2.840.113549.3.9",      "RC5-CBC"}, //$NON-NLS-1$ //$NON-NLS-2$
75        {"1.2.840.113549.3.10",     "DESCDMF"}, //$NON-NLS-1$ //$NON-NLS-2$
76        {"2.23.42.9.11.4.1",        "ECDSA"}, //$NON-NLS-1$ //$NON-NLS-2$
77    };
78    // Maps alg name to OID
79    private static final Map<String, String> alg2OidMap = new HashMap<String, String>();
80    // Maps OID to alg name
81    private static final Map<String, String> oid2AlgMap = new HashMap<String, String>();
82    // Maps aliases to alg names
83    private static final Map<String, String> algAliasesMap = new HashMap<String, String>();
84
85    static {
86        for (String[] element : knownAlgMappings) {
87            String algUC = Util.toUpperCase(element[1]);
88            alg2OidMap.put(algUC, element[0]);
89            oid2AlgMap.put(element[0], algUC);
90            // map upper case alg name to its original name
91            algAliasesMap.put(algUC, element[1]);
92        }
93        //
94        // Now search providers for mappings like
95        // Alg.Alias.<service>.<OID-INTS-DOT-SEPARATED>=<alg-name>
96        //  or
97        // Alg.Alias.<service>.OID.<OID-INTS-DOT-SEPARATED>=<alg-name>
98        //
99        Provider[] pl = Security.getProviders();
100        for (Provider element : pl) {
101            selectEntries(element);
102        }
103    }
104
105    // No instances
106    private AlgNameMapper() {
107    }
108
109    /**
110     * Returns OID for algName
111     *
112     * @param algName algorithm name to be mapped
113     * @return OID as String
114     */
115    public static String map2OID(String algName) {
116        // alg2OidMap map contains upper case keys
117        return alg2OidMap.get(Util.toUpperCase(algName));
118    }
119
120    /**
121     * Returns algName for OID
122     *
123     * @param oid OID to be mapped
124     * @return algorithm name
125     */
126    public static String map2AlgName(String oid) {
127        // oid2AlgMap map contains upper case values
128        String algUC = oid2AlgMap.get(oid);
129        // if not null there is always map UC->Orig
130        return algUC == null ? null : algAliasesMap.get(algUC);
131    }
132
133    /**
134     * Returns Algorithm name for given algorithm alias
135     *
136     * @param algName - alias
137     * @return algorithm name
138     */
139    public static String getStandardName(String algName) {
140        return algAliasesMap.get(Util.toUpperCase(algName));
141    }
142
143    // Searches given provider for mappings like
144    // Alg.Alias.<service>.<OID-INTS-DOT-SEPARATED>=<alg-name>
145    //  or
146    // Alg.Alias.<service>.OID.<OID-INTS-DOT-SEPARATED>=<alg-name>
147    // Puts mappings found into appropriate internal maps
148    private static void selectEntries(Provider p) {
149        Set<Map.Entry<Object, Object>> entrySet = p.entrySet();
150        for (String service : serviceName) {
151            String keyPrfix2find = "Alg.Alias." + service + ".";  //$NON-NLS-1$ //$NON-NLS-2$
152            for (Entry<Object, Object> me : entrySet) {
153                String key = (String)me.getKey();
154                if (key.startsWith(keyPrfix2find)) {
155                    String alias = key.substring(keyPrfix2find.length());
156                    String alg = (String)me.getValue();
157                    String algUC = Util.toUpperCase(alg);
158                    if (isOID(alias)) {
159                        if (alias.startsWith("OID.")) { //$NON-NLS-1$
160                            alias = alias.substring(4);
161                        }
162                        // Do not overwrite already known mappings
163                        boolean oid2AlgContains = oid2AlgMap.containsKey(alias);
164                        boolean alg2OidContains = alg2OidMap.containsKey(algUC);
165                        if (!oid2AlgContains || !alg2OidContains) {
166                            if (!oid2AlgContains) {
167                                oid2AlgMap.put(alias, algUC);
168                            }
169                            if (!alg2OidContains) {
170                                alg2OidMap.put(algUC, alias);
171                            }
172                            // map upper case alg name to its original name
173                            algAliasesMap.put(algUC, alg);
174                        }
175                           // Do not override known standard names
176                    } else if (!algAliasesMap.containsKey(Util.toUpperCase(alias))) {
177                        algAliasesMap.put(Util.toUpperCase(alias), alg);
178                    }
179                }
180            }
181        }
182    }
183
184    /**
185     * Checks if parameter represents OID
186     *
187     * @param alias alias to be checked
188     * @return 'true' if parameter represents OID
189     */
190    public static boolean isOID(String alias) {
191        // BEGIN android-changed
192        return ObjectIdentifier.isOID(normalize(alias));
193        // END android-changed
194    }
195
196    /**
197     * Removes leading "OID." from oid String passed
198     *
199     * @param oid string that may contain leading "OID."
200     * @return string passed without leading "OID."
201     */
202    public static String normalize(String oid) {
203        return oid.startsWith("OID.") //$NON-NLS-1$
204            ? oid.substring(4)
205            : oid;
206    }
207
208    /**
209     * Present all internal maps as formatted string
210     * @return Internal maps String representation
211     */
212    public static String dump() {
213        StringBuilder sb = new StringBuilder("alg2OidMap: "); //$NON-NLS-1$
214        sb.append(alg2OidMap);
215        sb.append("\noid2AlgMap: "); //$NON-NLS-1$
216        sb.append(oid2AlgMap);
217        sb.append("\nalgAliasesMap: "); //$NON-NLS-1$
218        sb.append(algAliasesMap);
219        return sb.toString();
220    }
221}
222