SignatureAndHashAlgorithm.java revision 12745:f068a4ffddd2
1/*
2 * Copyright (c) 2010, 2013, 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.ssl;
27
28import java.security.AlgorithmConstraints;
29import java.security.CryptoPrimitive;
30import java.security.PrivateKey;
31import java.security.Security;
32
33import java.util.Set;
34import java.util.HashSet;
35import java.util.Map;
36import java.util.EnumSet;
37import java.util.TreeMap;
38import java.util.Collection;
39import java.util.Collections;
40import java.util.ArrayList;
41
42import sun.security.util.KeyUtil;
43
44/**
45 * Signature and hash algorithm.
46 *
47 * [RFC5246] The client uses the "signature_algorithms" extension to
48 * indicate to the server which signature/hash algorithm pairs may be
49 * used in digital signatures.  The "extension_data" field of this
50 * extension contains a "supported_signature_algorithms" value.
51 *
52 *     enum {
53 *         none(0), md5(1), sha1(2), sha224(3), sha256(4), sha384(5),
54 *         sha512(6), (255)
55 *     } HashAlgorithm;
56 *
57 *     enum { anonymous(0), rsa(1), dsa(2), ecdsa(3), (255) }
58 *       SignatureAlgorithm;
59 *
60 *     struct {
61 *           HashAlgorithm hash;
62 *           SignatureAlgorithm signature;
63 *     } SignatureAndHashAlgorithm;
64 */
65final class SignatureAndHashAlgorithm {
66
67    // minimum priority for default enabled algorithms
68    static final int SUPPORTED_ALG_PRIORITY_MAX_NUM = 0x00F0;
69
70    // performance optimization
71    private static final Set<CryptoPrimitive> SIGNATURE_PRIMITIVE_SET =
72        Collections.unmodifiableSet(EnumSet.of(CryptoPrimitive.SIGNATURE));
73
74    // supported pairs of signature and hash algorithm
75    private static final Map<Integer, SignatureAndHashAlgorithm> supportedMap;
76    private static final Map<Integer, SignatureAndHashAlgorithm> priorityMap;
77
78    // the hash algorithm
79    private HashAlgorithm hash;
80
81    // id in 16 bit MSB format, i.e. 0x0603 for SHA512withECDSA
82    private int id;
83
84    // the standard algorithm name, for example "SHA512withECDSA"
85    private String algorithm;
86
87    // Priority for the preference order. The lower the better.
88    //
89    // If the algorithm is unsupported, its priority should be bigger
90    // than SUPPORTED_ALG_PRIORITY_MAX_NUM.
91    private int priority;
92
93    // constructor for supported algorithm
94    private SignatureAndHashAlgorithm(HashAlgorithm hash,
95            SignatureAlgorithm signature, String algorithm, int priority) {
96        this.hash = hash;
97        this.algorithm = algorithm;
98        this.id = ((hash.value & 0xFF) << 8) | (signature.value & 0xFF);
99        this.priority = priority;
100    }
101
102    // constructor for unsupported algorithm
103    private SignatureAndHashAlgorithm(String algorithm, int id, int sequence) {
104        this.hash = HashAlgorithm.valueOf((id >> 8) & 0xFF);
105        this.algorithm = algorithm;
106        this.id = id;
107
108        // add one more to the sequence number, in case that the number is zero
109        this.priority = SUPPORTED_ALG_PRIORITY_MAX_NUM + sequence + 1;
110    }
111
112    // Note that we do not use the sequence argument for supported algorithms,
113    // so please don't sort by comparing the objects read from handshake
114    // messages.
115    static SignatureAndHashAlgorithm valueOf(int hash,
116            int signature, int sequence) {
117        hash &= 0xFF;
118        signature &= 0xFF;
119
120        int id = (hash << 8) | signature;
121        SignatureAndHashAlgorithm signAlg = supportedMap.get(id);
122        if (signAlg == null) {
123            // unsupported algorithm
124            signAlg = new SignatureAndHashAlgorithm(
125                "Unknown (hash:0x" + Integer.toString(hash, 16) +
126                ", signature:0x" + Integer.toString(signature, 16) + ")",
127                id, sequence);
128        }
129
130        return signAlg;
131    }
132
133    int getHashValue() {
134        return (id >> 8) & 0xFF;
135    }
136
137    int getSignatureValue() {
138        return id & 0xFF;
139    }
140
141    String getAlgorithmName() {
142        return algorithm;
143    }
144
145    // return the size of a SignatureAndHashAlgorithm structure in TLS record
146    static int sizeInRecord() {
147        return 2;
148    }
149
150    // Get local supported algorithm collection complying to
151    // algorithm constraints
152    static Collection<SignatureAndHashAlgorithm>
153            getSupportedAlgorithms(AlgorithmConstraints constraints) {
154
155        Collection<SignatureAndHashAlgorithm> supported = new ArrayList<>();
156        synchronized (priorityMap) {
157            for (SignatureAndHashAlgorithm sigAlg : priorityMap.values()) {
158                if (sigAlg.priority <= SUPPORTED_ALG_PRIORITY_MAX_NUM &&
159                        constraints.permits(SIGNATURE_PRIMITIVE_SET,
160                                sigAlg.algorithm, null)) {
161                    supported.add(sigAlg);
162                }
163            }
164        }
165
166        return supported;
167    }
168
169    // Get supported algorithm collection from an untrusted collection
170    static Collection<SignatureAndHashAlgorithm> getSupportedAlgorithms(
171            Collection<SignatureAndHashAlgorithm> algorithms ) {
172        Collection<SignatureAndHashAlgorithm> supported = new ArrayList<>();
173        for (SignatureAndHashAlgorithm sigAlg : algorithms) {
174            if (sigAlg.priority <= SUPPORTED_ALG_PRIORITY_MAX_NUM) {
175                supported.add(sigAlg);
176            }
177        }
178
179        return supported;
180    }
181
182    static String[] getAlgorithmNames(
183            Collection<SignatureAndHashAlgorithm> algorithms) {
184        ArrayList<String> algorithmNames = new ArrayList<>();
185        if (algorithms != null) {
186            for (SignatureAndHashAlgorithm sigAlg : algorithms) {
187                algorithmNames.add(sigAlg.algorithm);
188            }
189        }
190
191        String[] array = new String[algorithmNames.size()];
192        return algorithmNames.toArray(array);
193    }
194
195    static Set<String> getHashAlgorithmNames(
196            Collection<SignatureAndHashAlgorithm> algorithms) {
197        Set<String> algorithmNames = new HashSet<>();
198        if (algorithms != null) {
199            for (SignatureAndHashAlgorithm sigAlg : algorithms) {
200                if (sigAlg.hash.value > 0) {
201                    algorithmNames.add(sigAlg.hash.standardName);
202                }
203            }
204        }
205
206        return algorithmNames;
207    }
208
209    static String getHashAlgorithmName(SignatureAndHashAlgorithm algorithm) {
210        return algorithm.hash.standardName;
211    }
212
213    private static void supports(HashAlgorithm hash,
214            SignatureAlgorithm signature, String algorithm, int priority) {
215
216        SignatureAndHashAlgorithm pair =
217            new SignatureAndHashAlgorithm(hash, signature, algorithm, priority);
218        if (supportedMap.put(pair.id, pair) != null) {
219            throw new RuntimeException(
220                "Duplicate SignatureAndHashAlgorithm definition, id: " +
221                pair.id);
222        }
223        if (priorityMap.put(pair.priority, pair) != null) {
224            throw new RuntimeException(
225                "Duplicate SignatureAndHashAlgorithm definition, priority: " +
226                pair.priority);
227        }
228    }
229
230    static SignatureAndHashAlgorithm getPreferableAlgorithm(
231        Collection<SignatureAndHashAlgorithm> algorithms, String expected) {
232
233        return SignatureAndHashAlgorithm.getPreferableAlgorithm(
234                algorithms, expected, null);
235    }
236
237    static SignatureAndHashAlgorithm getPreferableAlgorithm(
238        Collection<SignatureAndHashAlgorithm> algorithms,
239        String expected, PrivateKey signingKey) {
240
241        if (expected == null && !algorithms.isEmpty()) {
242            for (SignatureAndHashAlgorithm sigAlg : algorithms) {
243                if (sigAlg.priority <= SUPPORTED_ALG_PRIORITY_MAX_NUM) {
244                    return sigAlg;
245                }
246            }
247
248            return null;  // no supported algorithm
249        }
250
251        if (expected == null ) {
252            return null;  // no expected algorithm, no supported algorithm
253        }
254
255        /*
256         * Need to check RSA key length to match the length of hash value
257         */
258        int maxDigestLength = Integer.MAX_VALUE;
259        if (signingKey != null &&
260                "rsa".equalsIgnoreCase(signingKey.getAlgorithm()) &&
261                expected.equalsIgnoreCase("rsa")) {
262            /*
263             * RSA keys of 512 bits have been shown to be practically
264             * breakable, it does not make much sense to use the strong
265             * hash algorithm for keys whose key size less than 512 bits.
266             * So it is not necessary to caculate the required max digest
267             * length exactly.
268             *
269             * If key size is greater than or equals to 768, there is no max
270             * digest length limitation in currect implementation.
271             *
272             * If key size is greater than or equals to 512, but less than
273             * 768, the digest length should be less than or equal to 32 bytes.
274             *
275             * If key size is less than 512, the  digest length should be
276             * less than or equal to 20 bytes.
277             */
278            int keySize = KeyUtil.getKeySize(signingKey);
279            if (keySize >= 768) {
280                maxDigestLength = HashAlgorithm.SHA512.length;
281            } else if ((keySize >= 512) && (keySize < 768)) {
282                maxDigestLength = HashAlgorithm.SHA256.length;
283            } else if ((keySize > 0) && (keySize < 512)) {
284                maxDigestLength = HashAlgorithm.SHA1.length;
285            }   // Otherwise, cannot determine the key size, prefer the most
286                // preferable hash algorithm.
287        }
288
289        for (SignatureAndHashAlgorithm algorithm : algorithms) {
290            int signValue = algorithm.id & 0xFF;
291            if (expected.equalsIgnoreCase("rsa") &&
292                    signValue == SignatureAlgorithm.RSA.value) {
293                if (algorithm.hash.length <= maxDigestLength) {
294                    return algorithm;
295                }
296            } else if (
297                    (expected.equalsIgnoreCase("dsa") &&
298                        signValue == SignatureAlgorithm.DSA.value) ||
299                    (expected.equalsIgnoreCase("ecdsa") &&
300                        signValue == SignatureAlgorithm.ECDSA.value) ||
301                    (expected.equalsIgnoreCase("ec") &&
302                        signValue == SignatureAlgorithm.ECDSA.value)) {
303                return algorithm;
304            }
305        }
306
307        return null;
308    }
309
310    static enum HashAlgorithm {
311        UNDEFINED("undefined",        "", -1, -1),
312        NONE(          "none",    "NONE",  0, -1),
313        MD5(            "md5",     "MD5",  1, 16),
314        SHA1(          "sha1",   "SHA-1",  2, 20),
315        SHA224(      "sha224", "SHA-224",  3, 28),
316        SHA256(      "sha256", "SHA-256",  4, 32),
317        SHA384(      "sha384", "SHA-384",  5, 48),
318        SHA512(      "sha512", "SHA-512",  6, 64);
319
320        final String name;  // not the standard signature algorithm name
321                            // except the UNDEFINED, other names are defined
322                            // by TLS 1.2 protocol
323        final String standardName; // the standard MessageDigest algorithm name
324        final int value;
325        final int length;   // digest length in bytes, -1 means not applicable
326
327        private HashAlgorithm(String name, String standardName,
328                int value, int length) {
329            this.name = name;
330            this.standardName = standardName;
331            this.value = value;
332            this.length = length;
333        }
334
335        static HashAlgorithm valueOf(int value) {
336            HashAlgorithm algorithm = UNDEFINED;
337            switch (value) {
338                case 0:
339                    algorithm = NONE;
340                    break;
341                case 1:
342                    algorithm = MD5;
343                    break;
344                case 2:
345                    algorithm = SHA1;
346                    break;
347                case 3:
348                    algorithm = SHA224;
349                    break;
350                case 4:
351                    algorithm = SHA256;
352                    break;
353                case 5:
354                    algorithm = SHA384;
355                    break;
356                case 6:
357                    algorithm = SHA512;
358                    break;
359            }
360
361            return algorithm;
362        }
363    }
364
365    static enum SignatureAlgorithm {
366        UNDEFINED("undefined", -1),
367        ANONYMOUS("anonymous",  0),
368        RSA(            "rsa",  1),
369        DSA(            "dsa",  2),
370        ECDSA(        "ecdsa",  3);
371
372        final String name;  // not the standard signature algorithm name
373                            // except the UNDEFINED, other names are defined
374                            // by TLS 1.2 protocol
375        final int value;
376
377        private SignatureAlgorithm(String name, int value) {
378            this.name = name;
379            this.value = value;
380        }
381
382        static SignatureAlgorithm valueOf(int value) {
383            SignatureAlgorithm algorithm = UNDEFINED;
384            switch (value) {
385                case 0:
386                    algorithm = ANONYMOUS;
387                    break;
388                case 1:
389                    algorithm = RSA;
390                    break;
391                case 2:
392                    algorithm = DSA;
393                    break;
394                case 3:
395                    algorithm = ECDSA;
396                    break;
397            }
398
399            return algorithm;
400        }
401    }
402
403    static {
404        supportedMap = Collections.synchronizedSortedMap(
405            new TreeMap<Integer, SignatureAndHashAlgorithm>());
406        priorityMap = Collections.synchronizedSortedMap(
407            new TreeMap<Integer, SignatureAndHashAlgorithm>());
408
409        synchronized (supportedMap) {
410            int p = SUPPORTED_ALG_PRIORITY_MAX_NUM;
411            supports(HashAlgorithm.MD5,         SignatureAlgorithm.RSA,
412                    "MD5withRSA",           --p);
413            supports(HashAlgorithm.SHA1,        SignatureAlgorithm.DSA,
414                    "SHA1withDSA",          --p);
415            supports(HashAlgorithm.SHA1,        SignatureAlgorithm.RSA,
416                    "SHA1withRSA",          --p);
417            supports(HashAlgorithm.SHA1,        SignatureAlgorithm.ECDSA,
418                    "SHA1withECDSA",        --p);
419            if (Security.getProvider("SunMSCAPI") == null) {
420            supports(HashAlgorithm.SHA224,      SignatureAlgorithm.RSA,
421                    "SHA224withRSA",        --p);
422            supports(HashAlgorithm.SHA224,      SignatureAlgorithm.ECDSA,
423                    "SHA224withECDSA",      --p);
424            }
425            supports(HashAlgorithm.SHA256,      SignatureAlgorithm.RSA,
426                    "SHA256withRSA",        --p);
427            supports(HashAlgorithm.SHA256,      SignatureAlgorithm.ECDSA,
428                    "SHA256withECDSA",      --p);
429            supports(HashAlgorithm.SHA384,      SignatureAlgorithm.RSA,
430                    "SHA384withRSA",        --p);
431            supports(HashAlgorithm.SHA384,      SignatureAlgorithm.ECDSA,
432                    "SHA384withECDSA",      --p);
433            supports(HashAlgorithm.SHA512,      SignatureAlgorithm.RSA,
434                    "SHA512withRSA",        --p);
435            supports(HashAlgorithm.SHA512,      SignatureAlgorithm.ECDSA,
436                    "SHA512withECDSA",      --p);
437        }
438    }
439}
440
441