1 /*
   2  * Copyright (c) 2012, 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.util;
  27 
  28 import java.security.AlgorithmParameters;
  29 import java.security.Key;
  30 import java.security.PrivilegedAction;
  31 import java.security.AccessController;
  32 import java.security.InvalidKeyException;
  33 import java.security.interfaces.ECKey;
  34 import java.security.interfaces.RSAKey;
  35 import java.security.interfaces.DSAKey;
  36 import java.security.interfaces.DSAParams;
  37 import java.security.SecureRandom;
  38 import java.security.spec.KeySpec;
  39 import java.security.spec.ECParameterSpec;
  40 import java.security.spec.InvalidParameterSpecException;
  41 import javax.crypto.SecretKey;
  42 import javax.crypto.interfaces.DHKey;
  43 import javax.crypto.interfaces.DHPublicKey;
  44 import javax.crypto.spec.DHParameterSpec;
  45 import javax.crypto.spec.DHPublicKeySpec;
  46 import java.math.BigInteger;
  47 
  48 import sun.security.jca.JCAUtil;
  49 
  50 /**
  51  * A utility class to get key length, valiate keys, etc.
  52  */
  53 public final class KeyUtil {
  54 
  55     /**
  56      * Returns the key size of the given key object in bits.
  57      *
  58      * @param key the key object, cannot be null
  59      * @return the key size of the given key object in bits, or -1 if the
  60      *       key size is not accessible
  61      */
  62     public static final int getKeySize(Key key) {
  63         int size = -1;
  64 
  65         if (key instanceof Length) {
  66             try {
  67                 Length ruler = (Length)key;
  68                 size = ruler.length();
  69             } catch (UnsupportedOperationException usoe) {
  70                 // ignore the exception
  71             }
  72 
  73             if (size >= 0) {
  74                 return size;
  75             }
  76         }
  77 
  78         // try to parse the length from key specification
  79         if (key instanceof SecretKey) {
  80             SecretKey sk = (SecretKey)key;
  81             String format = sk.getFormat();
  82             if ("RAW".equals(format) && sk.getEncoded() != null) {
  83                 size = (sk.getEncoded().length * 8);
  84             }   // Otherwise, it may be a unextractable key of PKCS#11, or
  85                 // a key we are not able to handle.
  86         } else if (key instanceof RSAKey) {
  87             RSAKey pubk = (RSAKey)key;
  88             size = pubk.getModulus().bitLength();
  89         } else if (key instanceof ECKey) {
  90             ECKey pubk = (ECKey)key;
  91             size = pubk.getParams().getOrder().bitLength();
  92         } else if (key instanceof DSAKey) {
  93             DSAKey pubk = (DSAKey)key;
  94             DSAParams params = pubk.getParams();    // params can be null
  95             size = (params != null) ? params.getP().bitLength() : -1;
  96         } else if (key instanceof DHKey) {
  97             DHKey pubk = (DHKey)key;
  98             size = pubk.getParams().getP().bitLength();
  99         }   // Otherwise, it may be a unextractable key of PKCS#11, or
 100             // a key we are not able to handle.
 101 
 102         return size;
 103     }
 104 
 105     /**
 106      * Returns the key size of the given cryptographic parameters in bits.
 107      *
 108      * @param parameters the cryptographic parameters, cannot be null
 109      * @return the key size of the given cryptographic parameters in bits,
 110      *       or -1 if the key size is not accessible
 111      */
 112     public static final int getKeySize(AlgorithmParameters parameters) {
 113 
 114         String algorithm = parameters.getAlgorithm();
 115         switch (algorithm) {
 116             case "EC":
 117                 try {
 118                     ECKeySizeParameterSpec ps = parameters.getParameterSpec(
 119                             ECKeySizeParameterSpec.class);
 120                     if (ps != null) {
 121                         return ps.getKeySize();
 122                     }
 123                 } catch (InvalidParameterSpecException ipse) {
 124                     // ignore
 125                 }
 126 
 127                 try {
 128                     ECParameterSpec ps = parameters.getParameterSpec(
 129                             ECParameterSpec.class);
 130                     if (ps != null) {
 131                         return ps.getOrder().bitLength();
 132                     }
 133                 } catch (InvalidParameterSpecException ipse) {
 134                     // ignore
 135                 }
 136 
 137                 // Note: the ECGenParameterSpec case should be covered by the
 138                 // ECParameterSpec case above.
 139                 // See ECUtil.getECParameterSpec(Provider, String).
 140 
 141                 break;
 142             case "DiffieHellman":
 143                 try {
 144                     DHParameterSpec ps = parameters.getParameterSpec(
 145                             DHParameterSpec.class);
 146                     if (ps != null) {
 147                         return ps.getP().bitLength();
 148                     }
 149                 } catch (InvalidParameterSpecException ipse) {
 150                     // ignore
 151                 }
 152                 break;
 153 
 154             // May support more AlgorithmParameters algorithms in the future.
 155         }
 156 
 157         return -1;
 158     }
 159 
 160     /**
 161      * Returns whether the key is valid or not.
 162      * <P>
 163      * Note that this method is only apply to DHPublicKey at present.
 164      *
 165      * @param  key the key object, cannot be null
 166      *
 167      * @throws NullPointerException if {@code key} is null
 168      * @throws InvalidKeyException if {@code key} is invalid
 169      */
 170     public static final void validate(Key key)
 171             throws InvalidKeyException {
 172         if (key == null) {
 173             throw new NullPointerException(
 174                 "The key to be validated cannot be null");
 175         }
 176 
 177         if (key instanceof DHPublicKey) {
 178             validateDHPublicKey((DHPublicKey)key);
 179         }
 180     }
 181 
 182 
 183     /**
 184      * Returns whether the key spec is valid or not.
 185      * <P>
 186      * Note that this method is only apply to DHPublicKeySpec at present.
 187      *
 188      * @param  keySpec
 189      *         the key spec object, cannot be null
 190      *
 191      * @throws NullPointerException if {@code keySpec} is null
 192      * @throws InvalidKeyException if {@code keySpec} is invalid
 193      */
 194     public static final void validate(KeySpec keySpec)
 195             throws InvalidKeyException {
 196         if (keySpec == null) {
 197             throw new NullPointerException(
 198                 "The key spec to be validated cannot be null");
 199         }
 200 
 201         if (keySpec instanceof DHPublicKeySpec) {
 202             validateDHPublicKey((DHPublicKeySpec)keySpec);
 203         }
 204     }
 205 
 206     /**
 207      * Returns whether the specified provider is Oracle provider or not.
 208      *
 209      * @param  providerName
 210      *         the provider name
 211      * @return true if, and only if, the provider of the specified
 212      *         {@code providerName} is Oracle provider
 213      */
 214     public static final boolean isOracleJCEProvider(String providerName) {
 215         return providerName != null &&
 216                 (providerName.equals("SunJCE") ||
 217                     providerName.equals("SunMSCAPI") ||
 218                     providerName.equals("OracleUcrypto") ||
 219                     providerName.startsWith("SunPKCS11"));
 220     }
 221 
 222     /**
 223      * Check the format of TLS PreMasterSecret.
 224      * <P>
 225      * To avoid vulnerabilities described by section 7.4.7.1, RFC 5246,
 226      * treating incorrectly formatted message blocks and/or mismatched
 227      * version numbers in a manner indistinguishable from correctly
 228      * formatted RSA blocks.
 229      *
 230      * RFC 5246 describes the approach as:
 231      * <pre>{@literal
 232      *
 233      *  1. Generate a string R of 48 random bytes
 234      *
 235      *  2. Decrypt the message to recover the plaintext M
 236      *
 237      *  3. If the PKCS#1 padding is not correct, or the length of message
 238      *     M is not exactly 48 bytes:
 239      *        pre_master_secret = R
 240      *     else If ClientHello.client_version <= TLS 1.0, and version
 241      *     number check is explicitly disabled:
 242      *        premaster secret = M
 243      *     else If M[0..1] != ClientHello.client_version:
 244      *        premaster secret = R
 245      *     else:
 246      *        premaster secret = M
 247      *
 248      * Note that #2 should have completed before the call to this method.
 249      * }</pre>
 250      *
 251      * @param  clientVersion the version of the TLS protocol by which the
 252      *         client wishes to communicate during this session
 253      * @param  serverVersion the negotiated version of the TLS protocol which
 254      *         contains the lower of that suggested by the client in the client
 255      *         hello and the highest supported by the server.
 256      * @param  encoded the encoded key in its "RAW" encoding format
 257      * @param  isFailOver whether or not the previous decryption of the
 258      *         encrypted PreMasterSecret message run into problem
 259      * @return the polished PreMasterSecret key in its "RAW" encoding format
 260      */
 261     public static byte[] checkTlsPreMasterSecretKey(
 262             int clientVersion, int serverVersion, SecureRandom random,
 263             byte[] encoded, boolean isFailOver) {
 264 
 265         if (random == null) {
 266             random = JCAUtil.getSecureRandom();
 267         }
 268         byte[] replacer = new byte[48];
 269         random.nextBytes(replacer);
 270 
 271         if (!isFailOver && (encoded != null)) {
 272             // check the length
 273             if (encoded.length != 48) {
 274                 // private, don't need to clone the byte array.
 275                 return replacer;
 276             }
 277 
 278             int encodedVersion =
 279                     ((encoded[0] & 0xFF) << 8) | (encoded[1] & 0xFF);
 280             if (clientVersion != encodedVersion) {
 281                 if (clientVersion > 0x0301 ||               // 0x0301: TLSv1
 282                        serverVersion != encodedVersion) {
 283                     encoded = replacer;
 284                 }   // Otherwise, For compatibility, we maintain the behavior
 285                     // that the version in pre_master_secret can be the
 286                     // negotiated version for TLS v1.0 and SSL v3.0.
 287             }
 288 
 289             // private, don't need to clone the byte array.
 290             return encoded;
 291         }
 292 
 293         // private, don't need to clone the byte array.
 294         return replacer;
 295     }
 296 
 297     /**
 298      * Returns whether the Diffie-Hellman public key is valid or not.
 299      *
 300      * Per RFC 2631 and NIST SP800-56A, the following algorithm is used to
 301      * validate Diffie-Hellman public keys:
 302      * 1. Verify that y lies within the interval [2,p-1]. If it does not,
 303      *    the key is invalid.
 304      * 2. Compute y^q mod p. If the result == 1, the key is valid.
 305      *    Otherwise the key is invalid.
 306      */
 307     private static void validateDHPublicKey(DHPublicKey publicKey)
 308             throws InvalidKeyException {
 309         DHParameterSpec paramSpec = publicKey.getParams();
 310 
 311         BigInteger p = paramSpec.getP();
 312         BigInteger g = paramSpec.getG();
 313         BigInteger y = publicKey.getY();
 314 
 315         validateDHPublicKey(p, g, y);
 316     }
 317 
 318     private static void validateDHPublicKey(DHPublicKeySpec publicKeySpec)
 319             throws InvalidKeyException {
 320         validateDHPublicKey(publicKeySpec.getP(),
 321             publicKeySpec.getG(), publicKeySpec.getY());
 322     }
 323 
 324     private static void validateDHPublicKey(BigInteger p,
 325             BigInteger g, BigInteger y) throws InvalidKeyException {
 326 
 327         // For better interoperability, the interval is limited to [2, p-2].
 328         BigInteger leftOpen = BigInteger.ONE;
 329         BigInteger rightOpen = p.subtract(BigInteger.ONE);
 330         if (y.compareTo(leftOpen) <= 0) {
 331             throw new InvalidKeyException(
 332                     "Diffie-Hellman public key is too small");
 333         }
 334         if (y.compareTo(rightOpen) >= 0) {
 335             throw new InvalidKeyException(
 336                     "Diffie-Hellman public key is too large");
 337         }
 338 
 339         // y^q mod p == 1?
 340         // Unable to perform this check as q is unknown in this circumstance.
 341 
 342         // p is expected to be prime.  However, it is too expensive to check
 343         // that p is prime.  Instead, in order to mitigate the impact of
 344         // non-prime values, we check that y is not a factor of p.
 345         BigInteger r = p.remainder(y);
 346         if (r.equals(BigInteger.ZERO)) {
 347             throw new InvalidKeyException("Invalid Diffie-Hellman parameters");
 348         }
 349     }
 350 
 351     /**
 352      * Trim leading (most significant) zeroes from the result.
 353      *
 354      * @throws NullPointerException if {@code b} is null
 355      */
 356     public static byte[] trimZeroes(byte[] b) {
 357         int i = 0;
 358         while ((i < b.length - 1) && (b[i] == 0)) {
 359             i++;
 360         }
 361         if (i == 0) {
 362             return b;
 363         }
 364         byte[] t = new byte[b.length - i];
 365         System.arraycopy(b, i, t, 0, t.length);
 366         return t;
 367     }
 368 
 369 }
 370