1 /* 2 * Copyright (c) 2015, 2017, 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.Arrays; 31 import java.util.Collection; 32 import java.util.regex.Pattern; 33 34 /** 35 * The class decomposes standard algorithms into sub-elements. 36 */ 37 public class AlgorithmDecomposer { 38 39 // '(?<!padd)in': match 'in' but not preceded with 'padd'. 40 private static final Pattern PATTERN = 41 Pattern.compile("with|and|(?<!padd)in", Pattern.CASE_INSENSITIVE); 42 decomposeImpl(String algorithm)43 private static Set<String> decomposeImpl(String algorithm) { 44 Set<String> elements = new HashSet<>(); 45 46 // algorithm/mode/padding 47 String[] transTokens = algorithm.split("/"); 48 49 for (String transToken : transTokens) { 50 if (transToken == null || transToken.isEmpty()) { 51 continue; 52 } 53 54 // PBEWith<digest>And<encryption> 55 // PBEWith<prf>And<encryption> 56 // OAEPWith<digest>And<mgf>Padding 57 // <digest>with<encryption> 58 // <digest>with<encryption>and<mgf> 59 // <digest>with<encryption>in<format> 60 String[] tokens = PATTERN.split(transToken); 61 62 for (String token : tokens) { 63 if (token == null || token.isEmpty()) { 64 continue; 65 } 66 67 elements.add(token); 68 } 69 } 70 return elements; 71 } 72 73 /** 74 * Decompose the standard algorithm name into sub-elements. 75 * <p> 76 * For example, we need to decompose "SHA1WithRSA" into "SHA1" and "RSA" 77 * so that we can check the "SHA1" and "RSA" algorithm constraints 78 * separately. 79 * <p> 80 * Please override the method if need to support more name pattern. 81 */ decompose(String algorithm)82 public Set<String> decompose(String algorithm) { 83 if (algorithm == null || algorithm.isEmpty()) { 84 return new HashSet<>(); 85 } 86 87 Set<String> elements = decomposeImpl(algorithm); 88 89 // In Java standard algorithm name specification, for different 90 // purpose, the SHA-1 and SHA-2 algorithm names are different. For 91 // example, for MessageDigest, the standard name is "SHA-256", while 92 // for Signature, the digest algorithm component is "SHA256" for 93 // signature algorithm "SHA256withRSA". So we need to check both 94 // "SHA-256" and "SHA256" to make the right constraint checking. 95 96 // handle special name: SHA-1 and SHA1 97 if (elements.contains("SHA1") && !elements.contains("SHA-1")) { 98 elements.add("SHA-1"); 99 } 100 if (elements.contains("SHA-1") && !elements.contains("SHA1")) { 101 elements.add("SHA1"); 102 } 103 104 // handle special name: SHA-224 and SHA224 105 if (elements.contains("SHA224") && !elements.contains("SHA-224")) { 106 elements.add("SHA-224"); 107 } 108 if (elements.contains("SHA-224") && !elements.contains("SHA224")) { 109 elements.add("SHA224"); 110 } 111 112 // handle special name: SHA-256 and SHA256 113 if (elements.contains("SHA256") && !elements.contains("SHA-256")) { 114 elements.add("SHA-256"); 115 } 116 if (elements.contains("SHA-256") && !elements.contains("SHA256")) { 117 elements.add("SHA256"); 118 } 119 120 // handle special name: SHA-384 and SHA384 121 if (elements.contains("SHA384") && !elements.contains("SHA-384")) { 122 elements.add("SHA-384"); 123 } 124 if (elements.contains("SHA-384") && !elements.contains("SHA384")) { 125 elements.add("SHA384"); 126 } 127 128 // handle special name: SHA-512 and SHA512 129 if (elements.contains("SHA512") && !elements.contains("SHA-512")) { 130 elements.add("SHA-512"); 131 } 132 if (elements.contains("SHA-512") && !elements.contains("SHA512")) { 133 elements.add("SHA512"); 134 } 135 136 return elements; 137 } 138 139 /** 140 * Get aliases of the specified algorithm. 141 * 142 * May support more algorithms in the future. 143 */ getAliases(String algorithm)144 public static Collection<String> getAliases(String algorithm) { 145 String[] aliases; 146 if (algorithm.equalsIgnoreCase("DH") || 147 algorithm.equalsIgnoreCase("DiffieHellman")) { 148 aliases = new String[] {"DH", "DiffieHellman"}; 149 } else { 150 aliases = new String[] {algorithm}; 151 } 152 153 return Arrays.asList(aliases); 154 } 155 hasLoop(Set<String> elements, String find, String replace)156 private static void hasLoop(Set<String> elements, String find, String replace) { 157 if (elements.contains(find)) { 158 if (!elements.contains(replace)) { 159 elements.add(replace); 160 } 161 elements.remove(find); 162 } 163 } 164 165 /* 166 * This decomposes a standard name into sub-elements with a consistent 167 * message digest algorithm name to avoid overly complicated checking. 168 */ decomposeOneHash(String algorithm)169 public static Set<String> decomposeOneHash(String algorithm) { 170 if (algorithm == null || algorithm.isEmpty()) { 171 return new HashSet<>(); 172 } 173 174 Set<String> elements = decomposeImpl(algorithm); 175 176 hasLoop(elements, "SHA-1", "SHA1"); 177 hasLoop(elements, "SHA-224", "SHA224"); 178 hasLoop(elements, "SHA-256", "SHA256"); 179 hasLoop(elements, "SHA-384", "SHA384"); 180 hasLoop(elements, "SHA-512", "SHA512"); 181 182 return elements; 183 } 184 185 /* 186 * The provided message digest algorithm name will return a consistent 187 * naming scheme. 188 */ hashName(String algorithm)189 public static String hashName(String algorithm) { 190 return algorithm.replace("-", ""); 191 } 192 } 193