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