1 /* 2 * Copyright (c) 2010, 2015, 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.security.CryptoPrimitive; 29 import java.security.AlgorithmParameters; 30 import java.security.Key; 31 import java.util.Locale; 32 import java.util.Set; 33 import java.util.Collections; 34 import java.util.HashSet; 35 import java.util.Map; 36 import java.util.HashMap; 37 import java.util.regex.Pattern; 38 import java.util.regex.Matcher; 39 40 /** 41 * Algorithm constraints for disabled algorithms property 42 * 43 * See the "jdk.certpath.disabledAlgorithms" specification in java.security 44 * for the syntax of the disabled algorithm string. 45 */ 46 public class DisabledAlgorithmConstraints extends AbstractAlgorithmConstraints { 47 48 // the known security property, jdk.certpath.disabledAlgorithms 49 public static final String PROPERTY_CERTPATH_DISABLED_ALGS = 50 "jdk.certpath.disabledAlgorithms"; 51 52 // the known security property, jdk.tls.disabledAlgorithms 53 public static final String PROPERTY_TLS_DISABLED_ALGS = 54 "jdk.tls.disabledAlgorithms"; 55 56 private static final Map<String, String[]> disabledAlgorithmsMap = 57 new HashMap<>(); 58 private static final Map<String, KeySizeConstraints> keySizeConstraintsMap = 59 new HashMap<>(); 60 61 private final String[] disabledAlgorithms; 62 private final KeySizeConstraints keySizeConstraints; 63 64 /** 65 * Initialize algorithm constraints with the specified security property. 66 * 67 * @param propertyName the security property name that define the disabled 68 * algorithm constraints 69 */ 70 public DisabledAlgorithmConstraints(String propertyName) { 71 this(propertyName, new AlgorithmDecomposer()); 72 } 73 74 public DisabledAlgorithmConstraints(String propertyName, 75 AlgorithmDecomposer decomposer) { 76 super(decomposer); 77 disabledAlgorithms = getAlgorithms(disabledAlgorithmsMap, propertyName); 78 keySizeConstraints = getKeySizeConstraints(disabledAlgorithms, 79 propertyName); 80 } 81 82 @Override 83 public final boolean permits(Set<CryptoPrimitive> primitives, 84 String algorithm, AlgorithmParameters parameters) { 85 86 if (primitives == null || primitives.isEmpty()) { 87 throw new IllegalArgumentException( 88 "No cryptographic primitive specified"); 89 } 90 91 return checkAlgorithm(disabledAlgorithms, algorithm, decomposer); 92 } 93 94 @Override 95 public final boolean permits(Set<CryptoPrimitive> primitives, Key key) { 96 return checkConstraints(primitives, "", key, null); 97 } 98 99 @Override 100 public final boolean permits(Set<CryptoPrimitive> primitives, 101 String algorithm, Key key, AlgorithmParameters parameters) { 102 103 if (algorithm == null || algorithm.length() == 0) { 104 throw new IllegalArgumentException("No algorithm name specified"); 105 } 106 107 return checkConstraints(primitives, algorithm, key, parameters); 108 } 109 110 // Check algorithm constraints 111 private boolean checkConstraints(Set<CryptoPrimitive> primitives, 112 String algorithm, Key key, AlgorithmParameters parameters) { 113 114 // check the key parameter, it cannot be null. 115 if (key == null) { 116 throw new IllegalArgumentException("The key cannot be null"); 117 } 118 119 // check the target algorithm 120 if (algorithm != null && algorithm.length() != 0) { 121 if (!permits(primitives, algorithm, parameters)) { 122 return false; 123 } 124 } 125 126 // check the key algorithm 127 if (!permits(primitives, key.getAlgorithm(), null)) { 128 return false; 129 } 130 131 // check the key constraints 132 if (keySizeConstraints.disables(key)) { 133 return false; 134 } 135 136 return true; 137 } 138 139 private static KeySizeConstraints getKeySizeConstraints( 140 String[] disabledAlgorithms, String propertyName) { 141 synchronized (keySizeConstraintsMap) { 142 if(!keySizeConstraintsMap.containsKey(propertyName)) { 143 // map the key constraints 144 KeySizeConstraints keySizeConstraints = 145 new KeySizeConstraints(disabledAlgorithms); 146 keySizeConstraintsMap.put(propertyName, keySizeConstraints); 147 } 148 149 return keySizeConstraintsMap.get(propertyName); 150 } 151 } 152 153 /** 154 * key constraints 155 */ 156 private static class KeySizeConstraints { 157 private static final Pattern pattern = Pattern.compile( 158 "(\\S+)\\s+keySize\\s*(<=|<|==|!=|>|>=)\\s*(\\d+)"); 159 160 private Map<String, Set<KeySizeConstraint>> constraintsMap = 161 Collections.synchronizedMap( 162 new HashMap<String, Set<KeySizeConstraint>>()); 163 164 public KeySizeConstraints(String[] restrictions) { 165 for (String restriction : restrictions) { 166 if (restriction == null || restriction.isEmpty()) { 167 continue; 168 } 169 170 Matcher matcher = pattern.matcher(restriction); 171 if (matcher.matches()) { 172 String algorithm = matcher.group(1); 173 174 KeySizeConstraint.Operator operator = 175 KeySizeConstraint.Operator.of(matcher.group(2)); 176 int length = Integer.parseInt(matcher.group(3)); 177 178 algorithm = algorithm.toLowerCase(Locale.ENGLISH); 179 180 synchronized (constraintsMap) { 181 if (!constraintsMap.containsKey(algorithm)) { 182 constraintsMap.put(algorithm, 183 new HashSet<KeySizeConstraint>()); 184 } 185 186 Set<KeySizeConstraint> constraintSet = 187 constraintsMap.get(algorithm); 188 KeySizeConstraint constraint = 189 new KeySizeConstraint(operator, length); 190 constraintSet.add(constraint); 191 } 192 } 193 } 194 } 195 196 // Does this KeySizeConstraints disable the specified key? 197 public boolean disables(Key key) { 198 String algorithm = key.getAlgorithm().toLowerCase(Locale.ENGLISH); 199 synchronized (constraintsMap) { 200 if (constraintsMap.containsKey(algorithm)) { 201 Set<KeySizeConstraint> constraintSet = 202 constraintsMap.get(algorithm); 203 for (KeySizeConstraint constraint : constraintSet) { 204 if (constraint.disables(key)) { 205 return true; 206 } 207 } 208 } 209 } 210 211 return false; 212 } 213 } 214 215 /** 216 * Key size constraint. 217 * 218 * e.g. "keysize <= 1024" 219 */ 220 private static class KeySizeConstraint { 221 // operator 222 static enum Operator { 223 EQ, // "==" 224 NE, // "!=" 225 LT, // "<" 226 LE, // "<=" 227 GT, // ">" 228 GE; // ">=" 229 230 static Operator of(String s) { 231 switch (s) { 232 case "==": 233 return EQ; 234 case "!=": 235 return NE; 236 case "<": 237 return LT; 238 case "<=": 239 return LE; 240 case ">": 241 return GT; 242 case ">=": 243 return GE; 244 } 245 246 throw new IllegalArgumentException( 247 s + " is not a legal Operator"); 248 } 249 } 250 251 private int minSize; // the minimal available key size 252 private int maxSize; // the maximal available key size 253 private int prohibitedSize = -1; // unavailable key sizes 254 255 public KeySizeConstraint(Operator operator, int length) { 256 switch (operator) { 257 case EQ: // an unavailable key size 258 this.minSize = 0; 259 this.maxSize = Integer.MAX_VALUE; 260 prohibitedSize = length; 261 break; 262 case NE: 263 this.minSize = length; 264 this.maxSize = length; 265 break; 266 case LT: 267 this.minSize = length; 268 this.maxSize = Integer.MAX_VALUE; 269 break; 270 case LE: 271 this.minSize = length + 1; 272 this.maxSize = Integer.MAX_VALUE; 273 break; 274 case GT: 275 this.minSize = 0; 276 this.maxSize = length; 277 break; 278 case GE: 279 this.minSize = 0; 280 this.maxSize = length > 1 ? (length - 1) : 0; 281 break; 282 default: 283 // unlikely to happen 284 this.minSize = Integer.MAX_VALUE; 285 this.maxSize = -1; 286 } 287 } 288 289 // Does this key constraint disable the specified key? 290 public boolean disables(Key key) { 291 int size = KeyUtil.getKeySize(key); 292 293 if (size == 0) { 294 return true; // we don't allow any key of size 0. 295 } else if (size > 0) { 296 return ((size < minSize) || (size > maxSize) || 297 (prohibitedSize == size)); 298 } // Otherwise, the key size is not accessible. Conservatively, 299 // please don't disable such keys. 300 301 return false; 302 } 303 } 304 305 } 306