Home | History | Annotate | Download | only in util
      1 /*
      2  * Copyright 2016 The Android Open Source Project
      3  * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
      4  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
      5  *
      6  * This code is free software; you can redistribute it and/or modify it
      7  * under the terms of the GNU General Public License version 2 only, as
      8  * published by the Free Software Foundation.  Oracle designates this
      9  * particular file as subject to the "Classpath" exception as provided
     10  * by Oracle in the LICENSE file that accompanied this code.
     11  *
     12  * This code is distributed in the hope that it will be useful, but WITHOUT
     13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
     14  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
     15  * version 2 for more details (a copy is included in the LICENSE file that
     16  * accompanied this code).
     17  *
     18  * You should have received a copy of the GNU General Public License version
     19  * 2 along with this work; if not, write to the Free Software Foundation,
     20  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
     21  *
     22  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
     23  * or visit www.oracle.com if you need additional information or have any
     24  * questions.
     25  */
     26 
     27 package sun.security.util;
     28 
     29 import java.security.Key;
     30 import java.security.interfaces.ECKey;
     31 import java.security.interfaces.RSAKey;
     32 import java.security.interfaces.DSAKey;
     33 import java.security.interfaces.DSAParams;
     34 import java.security.SecureRandom;
     35 import java.security.spec.ECParameterSpec;
     36 import javax.crypto.SecretKey;
     37 import javax.crypto.interfaces.DHKey;
     38 
     39 /**
     40  * A utility class to get key length, valiate keys, etc.
     41  */
     42 public final class KeyUtil {
     43 
     44     /**
     45      * Returns the key size of the given key object in bits.
     46      *
     47      * @param key the key object, cannot be null
     48      * @return the key size of the given key object in bits, or -1 if the
     49      *       key size is not accessible
     50      */
     51     public static final int getKeySize(Key key) {
     52         int size = -1;
     53 
     54         if (key instanceof Length) {
     55             try {
     56                 Length ruler = (Length)key;
     57                 size = ruler.length();
     58             } catch (UnsupportedOperationException usoe) {
     59                 // ignore the exception
     60             }
     61 
     62             if (size >= 0) {
     63                 return size;
     64             }
     65         }
     66 
     67         // try to parse the length from key specification
     68         if (key instanceof SecretKey) {
     69             SecretKey sk = (SecretKey)key;
     70             String format = sk.getFormat();
     71             if ("RAW".equals(format) && sk.getEncoded() != null) {
     72                 size = (sk.getEncoded().length * 8);
     73             }   // Otherwise, it may be a unextractable key of PKCS#11, or
     74                 // a key we are not able to handle.
     75         } else if (key instanceof RSAKey) {
     76             RSAKey pubk = (RSAKey)key;
     77             size = pubk.getModulus().bitLength();
     78         } else if (key instanceof ECKey) {
     79             ECKey pubk = (ECKey)key;
     80             // BEGIN Android-changed
     81             // Was: size = pubk.getParams().getOrder().bitLength();
     82             ECParameterSpec params = pubk.getParams();
     83             // According to RFC 3279 section 2.3.5, EC keys are allowed
     84             // to inherit parameters in an X.509 certificate issuer's
     85             // key parameters, so the parameters may be null. The parent
     86             // key will be rejected if its parameters don't pass, so this
     87             // is okay.
     88             if (params != null) {
     89                 size = params.getOrder().bitLength();
     90             }
     91             // END Android-changed
     92         } else if (key instanceof DSAKey) {
     93             DSAKey pubk = (DSAKey)key;
     94             // BEGIN Android-changed
     95             // Was: size = pubk.getParams().getP().bitLength();
     96             DSAParams params = pubk.getParams();
     97             // According to RFC 3279 section 2.3.2, DSA keys are allowed
     98             // to inherit parameters in an X.509 certificate issuer's
     99             // key parameters, so the parameters may be null. The parent
    100             // key will be rejected if its parameters don't pass, so this
    101             // is okay.
    102             if (params != null) {
    103                 size = params.getP().bitLength();
    104             }
    105             // END Android-changed
    106         } else if (key instanceof DHKey) {
    107             DHKey pubk = (DHKey)key;
    108             size = pubk.getParams().getP().bitLength();
    109         }   // Otherwise, it may be a unextractable key of PKCS#11, or
    110             // a key we are not able to handle.
    111 
    112         return size;
    113     }
    114 
    115     // BEGIN Android-removed
    116     /*
    117     /**
    118      * Returns whether the key is valid or not.
    119      * <P>
    120      * Note that this method is only apply to DHPublicKey at present.
    121      *
    122      * @param  publicKey
    123      *         the key object, cannot be null
    124      *
    125      * @throws NullPointerException if {@code publicKey} is null
    126      * @throws InvalidKeyException if {@code publicKey} is invalid
    127      *
    128     public static final void validate(Key key)
    129             throws InvalidKeyException {
    130         if (key == null) {
    131             throw new NullPointerException(
    132                 "The key to be validated cannot be null");
    133         }
    134 
    135         if (key instanceof DHPublicKey) {
    136             validateDHPublicKey((DHPublicKey)key);
    137         }
    138     }
    139 
    140 
    141     /**
    142      * Returns whether the key spec is valid or not.
    143      * <P>
    144      * Note that this method is only apply to DHPublicKeySpec at present.
    145      *
    146      * @param  keySpec
    147      *         the key spec object, cannot be null
    148      *
    149      * @throws NullPointerException if {@code keySpec} is null
    150      * @throws InvalidKeyException if {@code keySpec} is invalid
    151      *
    152     public static final void validate(KeySpec keySpec)
    153             throws InvalidKeyException {
    154         if (keySpec == null) {
    155             throw new NullPointerException(
    156                 "The key spec to be validated cannot be null");
    157         }
    158 
    159         if (keySpec instanceof DHPublicKeySpec) {
    160             validateDHPublicKey((DHPublicKeySpec)keySpec);
    161         }
    162     }
    163 
    164     /**
    165      * Returns whether the specified provider is Oracle provider or not.
    166      * <P>
    167      * Note that this method is only apply to SunJCE and SunPKCS11 at present.
    168      *
    169      * @param  providerName
    170      *         the provider name
    171      * @return true if, and only if, the provider of the specified
    172      *         {@code providerName} is Oracle provider
    173      *
    174     public static final boolean isOracleJCEProvider(String providerName) {
    175         return providerName != null && (providerName.equals("SunJCE") ||
    176                                         providerName.startsWith("SunPKCS11"));
    177     }
    178 
    179     /**
    180      * Check the format of TLS PreMasterSecret.
    181      * <P>
    182      * To avoid vulnerabilities described by section 7.4.7.1, RFC 5246,
    183      * treating incorrectly formatted message blocks and/or mismatched
    184      * version numbers in a manner indistinguishable from correctly
    185      * formatted RSA blocks.
    186      *
    187      * RFC 5246 describes the approach as :
    188      *
    189      *  1. Generate a string R of 48 random bytes
    190      *
    191      *  2. Decrypt the message to recover the plaintext M
    192      *
    193      *  3. If the PKCS#1 padding is not correct, or the length of message
    194      *     M is not exactly 48 bytes:
    195      *        pre_master_secret = R
    196      *     else If ClientHello.client_version <= TLS 1.0, and version
    197      *     number check is explicitly disabled:
    198      *        premaster secret = M
    199      *     else If M[0..1] != ClientHello.client_version:
    200      *        premaster secret = R
    201      *     else:
    202      *        premaster secret = M
    203      *
    204      * Note that #2 should have completed before the call to this method.
    205      *
    206      * @param  clientVersion the version of the TLS protocol by which the
    207      *         client wishes to communicate during this session
    208      * @param  serverVersion the negotiated version of the TLS protocol which
    209      *         contains the lower of that suggested by the client in the client
    210      *         hello and the highest supported by the server.
    211      * @param  encoded the encoded key in its "RAW" encoding format
    212      * @param  isFailover whether or not the previous decryption of the
    213      *         encrypted PreMasterSecret message run into problem
    214      * @return the polished PreMasterSecret key in its "RAW" encoding format
    215      *
    216     public static byte[] checkTlsPreMasterSecretKey(
    217             int clientVersion, int serverVersion, SecureRandom random,
    218             byte[] encoded, boolean isFailOver) {
    219 
    220         if (random == null) {
    221             random = new SecureRandom();
    222         }
    223         byte[] replacer = new byte[48];
    224         random.nextBytes(replacer);
    225 
    226         if (!isFailOver && (encoded != null)) {
    227             // check the length
    228             if (encoded.length != 48) {
    229                 // private, don't need to clone the byte array.
    230                 return replacer;
    231             }
    232 
    233             int encodedVersion =
    234                     ((encoded[0] & 0xFF) << 8) | (encoded[1] & 0xFF);
    235             if (clientVersion != encodedVersion) {
    236                 if (clientVersion > 0x0301 ||               // 0x0301: TLSv1
    237                        serverVersion != encodedVersion) {
    238                     encoded = replacer;
    239                 }   // Otherwise, For compatibility, we maintain the behavior
    240                     // that the version in pre_master_secret can be the
    241                     // negotiated version for TLS v1.0 and SSL v3.0.
    242             }
    243 
    244             // private, don't need to clone the byte array.
    245             return encoded;
    246         }
    247 
    248         // private, don't need to clone the byte array.
    249         return replacer;
    250     }
    251 
    252     /**
    253      * Returns whether the Diffie-Hellman public key is valid or not.
    254      *
    255      * Per RFC 2631 and NIST SP800-56A, the following algorithm is used to
    256      * validate Diffie-Hellman public keys:
    257      * 1. Verify that y lies within the interval [2,p-1]. If it does not,
    258      *    the key is invalid.
    259      * 2. Compute y^q mod p. If the result == 1, the key is valid.
    260      *    Otherwise the key is invalid.
    261      *
    262     private static void validateDHPublicKey(DHPublicKey publicKey)
    263             throws InvalidKeyException {
    264         DHParameterSpec paramSpec = publicKey.getParams();
    265 
    266         BigInteger p = paramSpec.getP();
    267         BigInteger g = paramSpec.getG();
    268         BigInteger y = publicKey.getY();
    269 
    270         validateDHPublicKey(p, g, y);
    271     }
    272 
    273     private static void validateDHPublicKey(DHPublicKeySpec publicKeySpec)
    274             throws InvalidKeyException {
    275         validateDHPublicKey(publicKeySpec.getP(),
    276             publicKeySpec.getG(), publicKeySpec.getY());
    277     }
    278 
    279     private static void validateDHPublicKey(BigInteger p,
    280             BigInteger g, BigInteger y) throws InvalidKeyException {
    281 
    282         // For better interoperability, the interval is limited to [2, p-2].
    283         BigInteger leftOpen = BigInteger.ONE;
    284         BigInteger rightOpen = p.subtract(BigInteger.ONE);
    285         if (y.compareTo(leftOpen) <= 0) {
    286             throw new InvalidKeyException(
    287                     "Diffie-Hellman public key is too small");
    288         }
    289         if (y.compareTo(rightOpen) >= 0) {
    290             throw new InvalidKeyException(
    291                     "Diffie-Hellman public key is too large");
    292         }
    293 
    294         // y^q mod p == 1?
    295         // Unable to perform this check as q is unknown in this circumstance.
    296 
    297         // p is expected to be prime.  However, it is too expensive to check
    298         // that p is prime.  Instead, in order to mitigate the impact of
    299         // non-prime values, we check that y is not a factor of p.
    300         BigInteger r = p.remainder(y);
    301         if (r.equals(BigInteger.ZERO)) {
    302             throw new InvalidKeyException("Invalid Diffie-Hellman parameters");
    303         }
    304     }
    305 
    306     /**
    307      * Trim leading (most significant) zeroes from the result.
    308      *
    309      * @throws NullPointerException if {@code b} is null
    310      *
    311     public static byte[] trimZeroes(byte[] b) {
    312         int i = 0;
    313         while ((i < b.length - 1) && (b[i] == 0)) {
    314             i++;
    315         }
    316         if (i == 0) {
    317             return b;
    318         }
    319         byte[] t = new byte[b.length - i];
    320         System.arraycopy(b, i, t, 0, t.length);
    321         return t;
    322     }
    323     */
    324     // END Android-removed
    325 }
    326 
    327