DisabledAlgorithmConstraints.java revision 12745:f068a4ffddd2
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
26package sun.security.util;
27
28import java.security.CryptoPrimitive;
29import java.security.AlgorithmParameters;
30import java.security.Key;
31import java.util.Locale;
32import java.util.Set;
33import java.util.Collections;
34import java.util.HashSet;
35import java.util.Map;
36import java.util.HashMap;
37import java.util.regex.Pattern;
38import 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 */
46public 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
307