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      */
getKeySize(Key key)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