src/java.base/share/classes/sun/security/ssl/SignatureAndHashAlgorithm.java
branchJDK-8145252-TLS13-branch
changeset 56542 56aaa6cb3693
parent 56541 92cbbfc996f3
child 56543 2352538d2f6e
equal deleted inserted replaced
56541:92cbbfc996f3 56542:56aaa6cb3693
     1 /*
       
     2  * Copyright (c) 2010, 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.ssl;
       
    27 
       
    28 import java.security.AlgorithmConstraints;
       
    29 import java.security.CryptoPrimitive;
       
    30 import java.security.PrivateKey;
       
    31 import java.security.Security;
       
    32 
       
    33 import java.util.Set;
       
    34 import java.util.HashSet;
       
    35 import java.util.Map;
       
    36 import java.util.EnumSet;
       
    37 import java.util.TreeMap;
       
    38 import java.util.Collection;
       
    39 import java.util.Collections;
       
    40 import java.util.ArrayList;
       
    41 
       
    42 import 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  */
       
    65 final 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         for (SignatureAndHashAlgorithm sigAlg : priorityMap.values()) {
       
   157             if (sigAlg.priority <= SUPPORTED_ALG_PRIORITY_MAX_NUM &&
       
   158                     constraints.permits(SIGNATURE_PRIMITIVE_SET,
       
   159                             sigAlg.algorithm, null)) {
       
   160                 supported.add(sigAlg);
       
   161             }
       
   162         }
       
   163 
       
   164         return supported;
       
   165     }
       
   166 
       
   167     // Get supported algorithm collection from an untrusted collection
       
   168     static Collection<SignatureAndHashAlgorithm> getSupportedAlgorithms(
       
   169             AlgorithmConstraints constraints,
       
   170             Collection<SignatureAndHashAlgorithm> algorithms ) {
       
   171         Collection<SignatureAndHashAlgorithm> supported = new ArrayList<>();
       
   172         for (SignatureAndHashAlgorithm sigAlg : algorithms) {
       
   173             if (sigAlg.priority <= SUPPORTED_ALG_PRIORITY_MAX_NUM &&
       
   174                     constraints.permits(SIGNATURE_PRIMITIVE_SET,
       
   175                                 sigAlg.algorithm, null)) {
       
   176                 supported.add(sigAlg);
       
   177             }
       
   178         }
       
   179 
       
   180         return supported;
       
   181     }
       
   182 
       
   183     static String[] getAlgorithmNames(
       
   184             Collection<SignatureAndHashAlgorithm> algorithms) {
       
   185         ArrayList<String> algorithmNames = new ArrayList<>();
       
   186         if (algorithms != null) {
       
   187             for (SignatureAndHashAlgorithm sigAlg : algorithms) {
       
   188                 algorithmNames.add(sigAlg.algorithm);
       
   189             }
       
   190         }
       
   191 
       
   192         String[] array = new String[algorithmNames.size()];
       
   193         return algorithmNames.toArray(array);
       
   194     }
       
   195 
       
   196     static Set<String> getHashAlgorithmNames(
       
   197             Collection<SignatureAndHashAlgorithm> algorithms) {
       
   198         Set<String> algorithmNames = new HashSet<>();
       
   199         if (algorithms != null) {
       
   200             for (SignatureAndHashAlgorithm sigAlg : algorithms) {
       
   201                 if (sigAlg.hash.value > 0) {
       
   202                     algorithmNames.add(sigAlg.hash.standardName);
       
   203                 }
       
   204             }
       
   205         }
       
   206 
       
   207         return algorithmNames;
       
   208     }
       
   209 
       
   210     static String getHashAlgorithmName(SignatureAndHashAlgorithm algorithm) {
       
   211         return algorithm.hash.standardName;
       
   212     }
       
   213 
       
   214     private static void supports(HashAlgorithm hash,
       
   215             SignatureAlgorithm signature, String algorithm, int priority) {
       
   216 
       
   217         SignatureAndHashAlgorithm pair =
       
   218             new SignatureAndHashAlgorithm(hash, signature, algorithm, priority);
       
   219         if (supportedMap.put(pair.id, pair) != null) {
       
   220             throw new RuntimeException(
       
   221                 "Duplicate SignatureAndHashAlgorithm definition, id: " +
       
   222                 pair.id);
       
   223         }
       
   224         if (priorityMap.put(pair.priority, pair) != null) {
       
   225             throw new RuntimeException(
       
   226                 "Duplicate SignatureAndHashAlgorithm definition, priority: " +
       
   227                 pair.priority);
       
   228         }
       
   229     }
       
   230 
       
   231     static SignatureAndHashAlgorithm getPreferableAlgorithm(
       
   232         Collection<SignatureAndHashAlgorithm> algorithms, String expected) {
       
   233 
       
   234         return SignatureAndHashAlgorithm.getPreferableAlgorithm(
       
   235                 algorithms, expected, null);
       
   236     }
       
   237 
       
   238     static SignatureAndHashAlgorithm getPreferableAlgorithm(
       
   239             Collection<SignatureAndHashAlgorithm> algorithms,
       
   240             String expected, PrivateKey signingKey) {
       
   241 
       
   242         int maxDigestLength = getMaxDigestLength(signingKey);
       
   243         for (SignatureAndHashAlgorithm algorithm : algorithms) {
       
   244             int signValue = algorithm.id & 0xFF;
       
   245             if ((expected == null) ||
       
   246                     (expected.equalsIgnoreCase("rsa") &&
       
   247                             signValue == SignatureAlgorithm.RSA.value) ||
       
   248                     (expected.equalsIgnoreCase("dsa") &&
       
   249                             signValue == SignatureAlgorithm.DSA.value) ||
       
   250                     (expected.equalsIgnoreCase("ecdsa") &&
       
   251                             signValue == SignatureAlgorithm.ECDSA.value) ||
       
   252                     (expected.equalsIgnoreCase("ec") &&
       
   253                             signValue == SignatureAlgorithm.ECDSA.value)) {
       
   254 
       
   255                 if (algorithm.priority <= SUPPORTED_ALG_PRIORITY_MAX_NUM &&
       
   256                         algorithm.hash.length <= maxDigestLength) {
       
   257 
       
   258                     return algorithm;
       
   259                 }
       
   260             }
       
   261         }
       
   262 
       
   263         return null;
       
   264     }
       
   265 
       
   266     /*
       
   267      * Need to check key length to match the length of hash value
       
   268      */
       
   269     private static int getMaxDigestLength(PrivateKey signingKey) {
       
   270         int maxDigestLength = Integer.MAX_VALUE;
       
   271 
       
   272         // only need to check RSA algorithm at present.
       
   273         if (signingKey != null &&
       
   274                 "rsa".equalsIgnoreCase(signingKey.getAlgorithm())) {
       
   275             /*
       
   276              * RSA keys of 512 bits have been shown to be practically
       
   277              * breakable, it does not make much sense to use the strong
       
   278              * hash algorithm for keys whose key size less than 512 bits.
       
   279              * So it is not necessary to caculate the required max digest
       
   280              * length exactly.
       
   281              *
       
   282              * If key size is greater than or equals to 768, there is no max
       
   283              * digest length limitation in currect implementation.
       
   284              *
       
   285              * If key size is greater than or equals to 512, but less than
       
   286              * 768, the digest length should be less than or equal to 32 bytes.
       
   287              *
       
   288              * If key size is less than 512, the  digest length should be
       
   289              * less than or equal to 20 bytes.
       
   290              */
       
   291             int keySize = KeyUtil.getKeySize(signingKey);
       
   292             if (keySize >= 768) {
       
   293                 maxDigestLength = HashAlgorithm.SHA512.length;
       
   294             } else if ((keySize >= 512) && (keySize < 768)) {
       
   295                 maxDigestLength = HashAlgorithm.SHA256.length;
       
   296             } else if ((keySize > 0) && (keySize < 512)) {
       
   297                 maxDigestLength = HashAlgorithm.SHA1.length;
       
   298             }   // Otherwise, cannot determine the key size, prefer the most
       
   299                 // preferable hash algorithm.
       
   300         }
       
   301 
       
   302         return maxDigestLength;
       
   303     }
       
   304 
       
   305     static enum HashAlgorithm {
       
   306         UNDEFINED("undefined",        "", -1, -1),
       
   307         NONE(          "none",    "NONE",  0, -1),
       
   308         MD5(            "md5",     "MD5",  1, 16),
       
   309         SHA1(          "sha1",   "SHA-1",  2, 20),
       
   310         SHA224(      "sha224", "SHA-224",  3, 28),
       
   311         SHA256(      "sha256", "SHA-256",  4, 32),
       
   312         SHA384(      "sha384", "SHA-384",  5, 48),
       
   313         SHA512(      "sha512", "SHA-512",  6, 64);
       
   314 
       
   315         final String name;  // not the standard signature algorithm name
       
   316                             // except the UNDEFINED, other names are defined
       
   317                             // by TLS 1.2 protocol
       
   318         final String standardName; // the standard MessageDigest algorithm name
       
   319         final int value;
       
   320         final int length;   // digest length in bytes, -1 means not applicable
       
   321 
       
   322         private HashAlgorithm(String name, String standardName,
       
   323                 int value, int length) {
       
   324             this.name = name;
       
   325             this.standardName = standardName;
       
   326             this.value = value;
       
   327             this.length = length;
       
   328         }
       
   329 
       
   330         static HashAlgorithm valueOf(int value) {
       
   331             HashAlgorithm algorithm = UNDEFINED;
       
   332             switch (value) {
       
   333                 case 0:
       
   334                     algorithm = NONE;
       
   335                     break;
       
   336                 case 1:
       
   337                     algorithm = MD5;
       
   338                     break;
       
   339                 case 2:
       
   340                     algorithm = SHA1;
       
   341                     break;
       
   342                 case 3:
       
   343                     algorithm = SHA224;
       
   344                     break;
       
   345                 case 4:
       
   346                     algorithm = SHA256;
       
   347                     break;
       
   348                 case 5:
       
   349                     algorithm = SHA384;
       
   350                     break;
       
   351                 case 6:
       
   352                     algorithm = SHA512;
       
   353                     break;
       
   354             }
       
   355 
       
   356             return algorithm;
       
   357         }
       
   358     }
       
   359 
       
   360     static enum SignatureAlgorithm {
       
   361         UNDEFINED("undefined", -1),
       
   362         ANONYMOUS("anonymous",  0),
       
   363         RSA(            "rsa",  1),
       
   364         DSA(            "dsa",  2),
       
   365         ECDSA(        "ecdsa",  3);
       
   366 
       
   367         final String name;  // not the standard signature algorithm name
       
   368                             // except the UNDEFINED, other names are defined
       
   369                             // by TLS 1.2 protocol
       
   370         final int value;
       
   371 
       
   372         private SignatureAlgorithm(String name, int value) {
       
   373             this.name = name;
       
   374             this.value = value;
       
   375         }
       
   376 
       
   377         static SignatureAlgorithm valueOf(int value) {
       
   378             SignatureAlgorithm algorithm = UNDEFINED;
       
   379             switch (value) {
       
   380                 case 0:
       
   381                     algorithm = ANONYMOUS;
       
   382                     break;
       
   383                 case 1:
       
   384                     algorithm = RSA;
       
   385                     break;
       
   386                 case 2:
       
   387                     algorithm = DSA;
       
   388                     break;
       
   389                 case 3:
       
   390                     algorithm = ECDSA;
       
   391                     break;
       
   392             }
       
   393 
       
   394             return algorithm;
       
   395         }
       
   396     }
       
   397 
       
   398     static {
       
   399         supportedMap = Collections.synchronizedSortedMap(
       
   400             new TreeMap<Integer, SignatureAndHashAlgorithm>());
       
   401         priorityMap = Collections.synchronizedSortedMap(
       
   402             new TreeMap<Integer, SignatureAndHashAlgorithm>());
       
   403 
       
   404         synchronized (supportedMap) {
       
   405             int p = SUPPORTED_ALG_PRIORITY_MAX_NUM;
       
   406             supports(HashAlgorithm.MD5,         SignatureAlgorithm.RSA,
       
   407                     "MD5withRSA",           --p);
       
   408             supports(HashAlgorithm.SHA1,        SignatureAlgorithm.DSA,
       
   409                     "SHA1withDSA",          --p);
       
   410             supports(HashAlgorithm.SHA1,        SignatureAlgorithm.RSA,
       
   411                     "SHA1withRSA",          --p);
       
   412             supports(HashAlgorithm.SHA1,        SignatureAlgorithm.ECDSA,
       
   413                     "SHA1withECDSA",        --p);
       
   414 
       
   415             if (Security.getProvider("SunMSCAPI") == null) {
       
   416                 supports(HashAlgorithm.SHA224,      SignatureAlgorithm.DSA,
       
   417                         "SHA224withDSA",        --p);
       
   418                 supports(HashAlgorithm.SHA224,      SignatureAlgorithm.RSA,
       
   419                         "SHA224withRSA",        --p);
       
   420                 supports(HashAlgorithm.SHA224,      SignatureAlgorithm.ECDSA,
       
   421                         "SHA224withECDSA",      --p);
       
   422             }
       
   423 
       
   424             supports(HashAlgorithm.SHA256,      SignatureAlgorithm.DSA,
       
   425                     "SHA256withDSA",        --p);
       
   426             supports(HashAlgorithm.SHA256,      SignatureAlgorithm.RSA,
       
   427                     "SHA256withRSA",        --p);
       
   428             supports(HashAlgorithm.SHA256,      SignatureAlgorithm.ECDSA,
       
   429                     "SHA256withECDSA",      --p);
       
   430             supports(HashAlgorithm.SHA384,      SignatureAlgorithm.RSA,
       
   431                     "SHA384withRSA",        --p);
       
   432             supports(HashAlgorithm.SHA384,      SignatureAlgorithm.ECDSA,
       
   433                     "SHA384withECDSA",      --p);
       
   434             supports(HashAlgorithm.SHA512,      SignatureAlgorithm.RSA,
       
   435                     "SHA512withRSA",        --p);
       
   436             supports(HashAlgorithm.SHA512,      SignatureAlgorithm.ECDSA,
       
   437                     "SHA512withECDSA",      --p);
       
   438         }
       
   439     }
       
   440 }
       
   441