1 /*
2  * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.  Oracle designates this
8  * particular file as subject to the "Classpath" exception as provided
9  * by Oracle in the LICENSE file that accompanied this code.
10  *
11  * This code is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14  * version 2 for more details (a copy is included in the LICENSE file that
15  * accompanied this code).
16  *
17  * You should have received a copy of the GNU General Public License version
18  * 2 along with this work; if not, write to the Free Software Foundation,
19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20  *
21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22  * or visit www.oracle.com if you need additional information or have any
23  * questions.
24  */
25 
26 package sun.security.util;
27 
28 import java.util.HashSet;
29 import java.util.Set;
30 import java.util.regex.Pattern;
31 
32 /**
33  * The class decomposes standard algorithms into sub-elements.
34  */
35 public class AlgorithmDecomposer {
36 
37     private static final Pattern transPattern = Pattern.compile("/");
38     private static final Pattern pattern =
39                     Pattern.compile("with|and", Pattern.CASE_INSENSITIVE);
40 
decomposeImpl(String algorithm)41     private static Set<String> decomposeImpl(String algorithm) {
42 
43         // algorithm/mode/padding
44         String[] transTockens = transPattern.split(algorithm);
45 
46         Set<String> elements = new HashSet<>();
47         for (String transTocken : transTockens) {
48             if (transTocken == null || transTocken.length() == 0) {
49                 continue;
50             }
51 
52             // PBEWith<digest>And<encryption>
53             // PBEWith<prf>And<encryption>
54             // OAEPWith<digest>And<mgf>Padding
55             // <digest>with<encryption>
56             // <digest>with<encryption>and<mgf>
57             String[] tokens = pattern.split(transTocken);
58 
59             for (String token : tokens) {
60                 if (token == null || token.length() == 0) {
61                     continue;
62                 }
63 
64                 elements.add(token);
65             }
66         }
67         return elements;
68     }
69 
70     /**
71      * Decompose the standard algorithm name into sub-elements.
72      * <p>
73      * For example, we need to decompose "SHA1WithRSA" into "SHA1" and "RSA"
74      * so that we can check the "SHA1" and "RSA" algorithm constraints
75      * separately.
76      * <p>
77      * Please override the method if need to support more name pattern.
78      */
decompose(String algorithm)79     public Set<String> decompose(String algorithm) {
80         if (algorithm == null || algorithm.length() == 0) {
81             return new HashSet<>();
82         }
83 
84         Set<String> elements = decomposeImpl(algorithm);
85 
86         // In Java standard algorithm name specification, for different
87         // purpose, the SHA-1 and SHA-2 algorithm names are different. For
88         // example, for MessageDigest, the standard name is "SHA-256", while
89         // for Signature, the digest algorithm component is "SHA256" for
90         // signature algorithm "SHA256withRSA". So we need to check both
91         // "SHA-256" and "SHA256" to make the right constraint checking.
92 
93         // handle special name: SHA-1 and SHA1
94         if (elements.contains("SHA1") && !elements.contains("SHA-1")) {
95             elements.add("SHA-1");
96         }
97         if (elements.contains("SHA-1") && !elements.contains("SHA1")) {
98             elements.add("SHA1");
99         }
100 
101         // handle special name: SHA-224 and SHA224
102         if (elements.contains("SHA224") && !elements.contains("SHA-224")) {
103             elements.add("SHA-224");
104         }
105         if (elements.contains("SHA-224") && !elements.contains("SHA224")) {
106             elements.add("SHA224");
107         }
108 
109         // handle special name: SHA-256 and SHA256
110         if (elements.contains("SHA256") && !elements.contains("SHA-256")) {
111             elements.add("SHA-256");
112         }
113         if (elements.contains("SHA-256") && !elements.contains("SHA256")) {
114             elements.add("SHA256");
115         }
116 
117         // handle special name: SHA-384 and SHA384
118         if (elements.contains("SHA384") && !elements.contains("SHA-384")) {
119             elements.add("SHA-384");
120         }
121         if (elements.contains("SHA-384") && !elements.contains("SHA384")) {
122             elements.add("SHA384");
123         }
124 
125         // handle special name: SHA-512 and SHA512
126         if (elements.contains("SHA512") && !elements.contains("SHA-512")) {
127             elements.add("SHA-512");
128         }
129         if (elements.contains("SHA-512") && !elements.contains("SHA512")) {
130             elements.add("SHA512");
131         }
132 
133         return elements;
134     }
135 
hasLoop(Set<String> elements, String find, String replace)136     private static void hasLoop(Set<String> elements, String find, String replace) {
137         if (elements.contains(find)) {
138             if (!elements.contains(replace)) {
139                 elements.add(replace);
140 }
141             elements.remove(find);
142         }
143     }
144 
145     /*
146      * This decomposes a standard name into sub-elements with a consistent
147      * message digest algorithm name to avoid overly complicated checking.
148      */
decomposeOneHash(String algorithm)149     public static Set<String> decomposeOneHash(String algorithm) {
150         if (algorithm == null || algorithm.length() == 0) {
151             return new HashSet<>();
152         }
153 
154         Set<String> elements = decomposeImpl(algorithm);
155 
156         hasLoop(elements, "SHA-1", "SHA1");
157         hasLoop(elements, "SHA-224", "SHA224");
158         hasLoop(elements, "SHA-256", "SHA256");
159         hasLoop(elements, "SHA-384", "SHA384");
160         hasLoop(elements, "SHA-512", "SHA512");
161 
162         return elements;
163     }
164 
165     /*
166      * The provided message digest algorithm name will return a consistent
167      * naming scheme.
168      */
hashName(String algorithm)169     public static String hashName(String algorithm) {
170         return algorithm.replace("-", "");
171     }
172 }
173