387 lines · 13.7 KB · Java
RawDownload
/* * Copyright (c) 2010, 2011, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation.  Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package sun.security.ssl; import java.security.AlgorithmConstraints;import java.security.CryptoPrimitive; import java.util.Set;import java.util.HashSet;import java.util.Map;import java.util.EnumSet;import java.util.TreeMap;import java.util.Collection;import java.util.Collections;import java.util.ArrayList; /** * Signature and hash algorithm. * * [RFC5246] The client uses the "signature_algorithms" extension to * indicate to the server which signature/hash algorithm pairs may be * used in digital signatures.  The "extension_data" field of this * extension contains a "supported_signature_algorithms" value. * *     enum { *         none(0), md5(1), sha1(2), sha224(3), sha256(4), sha384(5), *         sha512(6), (255) *     } HashAlgorithm; * *     enum { anonymous(0), rsa(1), dsa(2), ecdsa(3), (255) } *       SignatureAlgorithm; * *     struct { *           HashAlgorithm hash; *           SignatureAlgorithm signature; *     } SignatureAndHashAlgorithm; */final class SignatureAndHashAlgorithm {     // minimum priority for default enabled algorithms    final static int SUPPORTED_ALG_PRIORITY_MAX_NUM = 0x00F0;     // performance optimization    private final static Set<CryptoPrimitive> SIGNATURE_PRIMITIVE_SET =                                    EnumSet.of(CryptoPrimitive.SIGNATURE);     // supported pairs of signature and hash algorithm    private final static Map<Integer, SignatureAndHashAlgorithm> supportedMap;    private final static Map<Integer, SignatureAndHashAlgorithm> priorityMap;     // the hash algorithm    private HashAlgorithm hash;     // the signature algorithm    private SignatureAlgorithm signature;     // id in 16 bit MSB format, i.e. 0x0603 for SHA512withECDSA    private int id;     // the standard algorithm name, for example "SHA512withECDSA"    private String algorithm;     // Priority for the preference order. The lower the better.    //    // If the algorithm is unsupported, its priority should be bigger    // than SUPPORTED_ALG_PRIORITY_MAX_NUM.    private int priority;     // constructor for supported algorithm    private SignatureAndHashAlgorithm(HashAlgorithm hash,            SignatureAlgorithm signature, String algorithm, int priority) {        this.hash = hash;        this.signature = signature;        this.algorithm = algorithm;        this.id = ((hash.value & 0xFF) << 8) | (signature.value & 0xFF);        this.priority = priority;    }     // constructor for unsupported algorithm    private SignatureAndHashAlgorithm(String algorithm, int id, int sequence) {        this.hash = HashAlgorithm.valueOf((id >> 8) & 0xFF);        this.signature = SignatureAlgorithm.valueOf(id & 0xFF);        this.algorithm = algorithm;        this.id = id;         // add one more to the sequece number, in case that the number is zero        this.priority = SUPPORTED_ALG_PRIORITY_MAX_NUM + sequence + 1;    }     // Note that we do not use the sequence argument for supported algorithms,    // so please don't sort by comparing the objects read from handshake    // messages.    static SignatureAndHashAlgorithm valueOf(int hash,            int signature, int sequence) {        hash &= 0xFF;        signature &= 0xFF;         int id = (hash << 8) | signature;        SignatureAndHashAlgorithm signAlg = supportedMap.get(id);        if (signAlg == null) {            // unsupported algorithm            signAlg = new SignatureAndHashAlgorithm(                "Unknown (hash:0x" + Integer.toString(hash, 16) +                ", signature:0x" + Integer.toString(signature, 16) + ")",                id, sequence);        }         return signAlg;    }     int getHashValue() {        return (id >> 8) & 0xFF;    }     int getSignatureValue() {        return id & 0xFF;    }     String getAlgorithmName() {        return algorithm;    }     // return the size of a SignatureAndHashAlgorithm structure in TLS record    static int sizeInRecord() {        return 2;    }     // Get local supported algorithm collection complying to    // algorithm constraints    static Collection<SignatureAndHashAlgorithm>            getSupportedAlgorithms(AlgorithmConstraints constraints) {         Collection<SignatureAndHashAlgorithm> supported = new ArrayList<>();        synchronized (priorityMap) {            for (SignatureAndHashAlgorithm sigAlg : priorityMap.values()) {                if (sigAlg.priority <= SUPPORTED_ALG_PRIORITY_MAX_NUM &&                        constraints.permits(SIGNATURE_PRIMITIVE_SET,                                sigAlg.algorithm, null)) {                    supported.add(sigAlg);                }            }        }         return supported;    }     // Get supported algorithm collection from an untrusted collection    static Collection<SignatureAndHashAlgorithm> getSupportedAlgorithms(            Collection<SignatureAndHashAlgorithm> algorithms ) {        Collection<SignatureAndHashAlgorithm> supported = new ArrayList<>();        for (SignatureAndHashAlgorithm sigAlg : algorithms) {            if (sigAlg.priority <= SUPPORTED_ALG_PRIORITY_MAX_NUM) {                supported.add(sigAlg);            }        }         return supported;    }     static String[] getAlgorithmNames(            Collection<SignatureAndHashAlgorithm> algorithms) {        ArrayList<String> algorithmNames = new ArrayList<>();        if (algorithms != null) {            for (SignatureAndHashAlgorithm sigAlg : algorithms) {                algorithmNames.add(sigAlg.algorithm);            }        }         String[] array = new String[algorithmNames.size()];        return algorithmNames.toArray(array);    }     static Set<String> getHashAlgorithmNames(            Collection<SignatureAndHashAlgorithm> algorithms) {        Set<String> algorithmNames = new HashSet<>();        if (algorithms != null) {            for (SignatureAndHashAlgorithm sigAlg : algorithms) {                if (sigAlg.hash.value > 0) {                    algorithmNames.add(sigAlg.hash.standardName);                }            }        }         return algorithmNames;    }     static String getHashAlgorithmName(SignatureAndHashAlgorithm algorithm) {        return algorithm.hash.standardName;    }     private static void supports(HashAlgorithm hash,            SignatureAlgorithm signature, String algorithm, int priority) {         SignatureAndHashAlgorithm pair =            new SignatureAndHashAlgorithm(hash, signature, algorithm, priority);        if (supportedMap.put(pair.id, pair) != null) {            throw new RuntimeException(                "Duplicate SignatureAndHashAlgorithm definition, id: " +                pair.id);        }        if (priorityMap.put(pair.priority, pair) != null) {            throw new RuntimeException(                "Duplicate SignatureAndHashAlgorithm definition, priority: " +                pair.priority);        }    }     static SignatureAndHashAlgorithm getPreferableAlgorithm(        Collection<SignatureAndHashAlgorithm> algorithms, String expected) {         if (expected == null && !algorithms.isEmpty()) {            for (SignatureAndHashAlgorithm sigAlg : algorithms) {                if (sigAlg.priority <= SUPPORTED_ALG_PRIORITY_MAX_NUM) {                    return sigAlg;                }            }             return null;  // no supported algorithm        }          for (SignatureAndHashAlgorithm algorithm : algorithms) {            int signValue = algorithm.id & 0xFF;            if ((expected.equalsIgnoreCase("dsa") &&                    signValue == SignatureAlgorithm.DSA.value) ||                (expected.equalsIgnoreCase("rsa") &&                    signValue == SignatureAlgorithm.RSA.value) ||                (expected.equalsIgnoreCase("ecdsa") &&                    signValue == SignatureAlgorithm.ECDSA.value) ||                (expected.equalsIgnoreCase("ec") &&                    signValue == SignatureAlgorithm.ECDSA.value)) {                return algorithm;            }        }         return null;    }     static enum HashAlgorithm {        UNDEFINED("undefined",        "", -1),        NONE(          "none",    "NONE",  0),        MD5(            "md5",     "MD5",  1),        SHA1(          "sha1",   "SHA-1",  2),        SHA224(      "sha224", "SHA-224",  3),        SHA256(      "sha256", "SHA-256",  4),        SHA384(      "sha384", "SHA-384",  5),        SHA512(      "sha512", "SHA-512",  6);         final String name;  // not the standard signature algorithm name                            // except the UNDEFINED, other names are defined                            // by TLS 1.2 protocol        final String standardName; // the standard MessageDigest algorithm name        final int value;         private HashAlgorithm(String name, String standardName, int value) {            this.name = name;            this.standardName = standardName;            this.value = value;        }         static HashAlgorithm valueOf(int value) {            HashAlgorithm algorithm = UNDEFINED;            switch (value) {                case 0:                    algorithm = NONE;                    break;                case 1:                    algorithm = MD5;                    break;                case 2:                    algorithm = SHA1;                    break;                case 3:                    algorithm = SHA224;                    break;                case 4:                    algorithm = SHA256;                    break;                case 5:                    algorithm = SHA384;                    break;                case 6:                    algorithm = SHA512;                    break;            }             return algorithm;        }    }     static enum SignatureAlgorithm {        UNDEFINED("undefined", -1),        ANONYMOUS("anonymous",  0),        RSA(            "rsa",  1),        DSA(            "dsa",  2),        ECDSA(        "ecdsa",  3);         final String name;  // not the standard signature algorithm name                            // except the UNDEFINED, other names are defined                            // by TLS 1.2 protocol        final int value;         private SignatureAlgorithm(String name, int value) {            this.name = name;            this.value = value;        }         static SignatureAlgorithm valueOf(int value) {            SignatureAlgorithm algorithm = UNDEFINED;            switch (value) {                case 0:                    algorithm = ANONYMOUS;                    break;                case 1:                    algorithm = RSA;                    break;                case 2:                    algorithm = DSA;                    break;                case 3:                    algorithm = ECDSA;                    break;            }             return algorithm;        }    }     static {        supportedMap = Collections.synchronizedSortedMap(            new TreeMap<Integer, SignatureAndHashAlgorithm>());        priorityMap = Collections.synchronizedSortedMap(            new TreeMap<Integer, SignatureAndHashAlgorithm>());         synchronized (supportedMap) {            int p = SUPPORTED_ALG_PRIORITY_MAX_NUM;            supports(HashAlgorithm.MD5,         SignatureAlgorithm.RSA,                    "MD5withRSA",           --p);            supports(HashAlgorithm.SHA1,        SignatureAlgorithm.DSA,                    "SHA1withDSA",          --p);            supports(HashAlgorithm.SHA1,        SignatureAlgorithm.RSA,                    "SHA1withRSA",          --p);            supports(HashAlgorithm.SHA1,        SignatureAlgorithm.ECDSA,                    "SHA1withECDSA",        --p);            supports(HashAlgorithm.SHA224,      SignatureAlgorithm.RSA,                    "SHA224withRSA",        --p);            supports(HashAlgorithm.SHA224,      SignatureAlgorithm.ECDSA,                    "SHA224withECDSA",      --p);            supports(HashAlgorithm.SHA256,      SignatureAlgorithm.RSA,                    "SHA256withRSA",        --p);            supports(HashAlgorithm.SHA256,      SignatureAlgorithm.ECDSA,                    "SHA256withECDSA",      --p);            supports(HashAlgorithm.SHA384,      SignatureAlgorithm.RSA,                    "SHA384withRSA",        --p);            supports(HashAlgorithm.SHA384,      SignatureAlgorithm.ECDSA,                    "SHA384withECDSA",      --p);            supports(HashAlgorithm.SHA512,      SignatureAlgorithm.RSA,                    "SHA512withRSA",        --p);            supports(HashAlgorithm.SHA512,      SignatureAlgorithm.ECDSA,                    "SHA512withECDSA",      --p);        }    }}