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.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.spec.ECParameterSpec;
38 import java.security.spec.KeySpec;
39 import javax.crypto.SecretKey;
40 import javax.crypto.interfaces.DHKey;
41 import javax.crypto.interfaces.DHPublicKey;
42 import javax.crypto.spec.DHParameterSpec;
43 import javax.crypto.spec.DHPublicKeySpec;
44 import java.math.BigInteger;
45 
46 /**
47  * A utility class to get key length, valiate keys, etc.
48  */
49 public final class KeyUtil {
50 
51     /**
52      * Returns the key size of the given key object in bits.
53      *
54      * @param key the key object, cannot be null
55      * @return the key size of the given key object in bits, or -1 if the
56      *       key size is not accessible
57      */
getKeySize(Key key)58     public static final int getKeySize(Key key) {
59         int size = -1;
60 
61         if (key instanceof Length) {
62             try {
63                 Length ruler = (Length)key;
64                 size = ruler.length();
65             } catch (UnsupportedOperationException usoe) {
66                 // ignore the exception
67             }
68 
69             if (size >= 0) {
70                 return size;
71             }
72         }
73 
74         // try to parse the length from key specification
75         if (key instanceof SecretKey) {
76             SecretKey sk = (SecretKey)key;
77             String format = sk.getFormat();
78             if ("RAW".equals(format) && sk.getEncoded() != null) {
79                 size = (sk.getEncoded().length * 8);
80             }   // Otherwise, it may be a unextractable key of PKCS#11, or
81                 // a key we are not able to handle.
82         } else if (key instanceof RSAKey) {
83             RSAKey pubk = (RSAKey)key;
84             size = pubk.getModulus().bitLength();
85         } else if (key instanceof ECKey) {
86             ECKey pubk = (ECKey)key;
87             ECParameterSpec params = pubk.getParams();
88             // According to RFC 3279 section 2.3.5, EC keys are allowed
89             // to inherit parameters in an X.509 certificate issuer's
90             // key parameters, so the parameters may be null. The parent
91             // key will be rejected if its parameters don't pass, so this
92             // is okay.
93             if (params != null) {
94                 size = params.getOrder().bitLength();
95             }
96         } else if (key instanceof DSAKey) {
97             DSAKey pubk = (DSAKey)key;
98             DSAParams params = pubk.getParams();
99             // According to RFC 3279 section 2.3.2, DSA keys are allowed
100             // to inherit parameters in an X.509 certificate issuer's
101             // key parameters, so the parameters may be null. The parent
102             // key will be rejected if its parameters don't pass, so this
103             // is okay.
104             if (params != null) {
105                 size = params.getP().bitLength();
106             }
107         } else if (key instanceof DHKey) {
108             DHKey pubk = (DHKey)key;
109             size = pubk.getParams().getP().bitLength();
110         }   // Otherwise, it may be a unextractable key of PKCS#11, or
111             // a key we are not able to handle.
112 
113         return size;
114     }
115 
116     /**
117      * Returns whether the key is valid or not.
118      * <P>
119      * Note that this method is only apply to DHPublicKey at present.
120      *
121      * @param  publicKey
122      *         the key object, cannot be null
123      *
124      * @throws NullPointerException if {@code publicKey} is null
125      * @throws InvalidKeyException if {@code publicKey} is invalid
126      */
validate(Key key)127     public static final void validate(Key key)
128             throws InvalidKeyException {
129         if (key == null) {
130             throw new NullPointerException(
131                 "The key to be validated cannot be null");
132         }
133 
134         if (key instanceof DHPublicKey) {
135             validateDHPublicKey((DHPublicKey)key);
136         }
137     }
138 
139 
140     /**
141      * Returns whether the key spec is valid or not.
142      * <P>
143      * Note that this method is only apply to DHPublicKeySpec at present.
144      *
145      * @param  keySpec
146      *         the key spec object, cannot be null
147      *
148      * @throws NullPointerException if {@code keySpec} is null
149      * @throws InvalidKeyException if {@code keySpec} is invalid
150      */
validate(KeySpec keySpec)151     public static final void validate(KeySpec keySpec)
152             throws InvalidKeyException {
153         if (keySpec == null) {
154             throw new NullPointerException(
155                 "The key spec to be validated cannot be null");
156         }
157 
158         if (keySpec instanceof DHPublicKeySpec) {
159             validateDHPublicKey((DHPublicKeySpec)keySpec);
160         }
161     }
162 
163     /**
164      * Returns whether the specified provider is Oracle provider or not.
165      * <P>
166      * Note that this method is only apply to SunJCE and SunPKCS11 at present.
167      *
168      * @param  providerName
169      *         the provider name
170      * @return true if, and only if, the provider of the specified
171      *         {@code providerName} is Oracle provider
172      */
isOracleJCEProvider(String providerName)173     public static final boolean isOracleJCEProvider(String providerName) {
174         return providerName != null && (providerName.equals("SunJCE") ||
175                                         providerName.startsWith("SunPKCS11"));
176     }
177 
178     /**
179      * Returns whether the Diffie-Hellman public key is valid or not.
180      *
181      * Per RFC 2631 and NIST SP800-56A, the following algorithm is used to
182      * validate Diffie-Hellman public keys:
183      * 1. Verify that y lies within the interval [2,p-1]. If it does not,
184      *    the key is invalid.
185      * 2. Compute y^q mod p. If the result == 1, the key is valid.
186      *    Otherwise the key is invalid.
187      */
validateDHPublicKey(DHPublicKey publicKey)188     private static void validateDHPublicKey(DHPublicKey publicKey)
189             throws InvalidKeyException {
190         DHParameterSpec paramSpec = publicKey.getParams();
191 
192         BigInteger p = paramSpec.getP();
193         BigInteger g = paramSpec.getG();
194         BigInteger y = publicKey.getY();
195 
196         validateDHPublicKey(p, g, y);
197     }
198 
validateDHPublicKey(DHPublicKeySpec publicKeySpec)199     private static void validateDHPublicKey(DHPublicKeySpec publicKeySpec)
200             throws InvalidKeyException {
201         validateDHPublicKey(publicKeySpec.getP(),
202             publicKeySpec.getG(), publicKeySpec.getY());
203     }
204 
validateDHPublicKey(BigInteger p, BigInteger g, BigInteger y)205     private static void validateDHPublicKey(BigInteger p,
206             BigInteger g, BigInteger y) throws InvalidKeyException {
207 
208         // For better interoperability, the interval is limited to [2, p-2].
209         BigInteger leftOpen = BigInteger.ONE;
210         BigInteger rightOpen = p.subtract(BigInteger.ONE);
211         if (y.compareTo(leftOpen) <= 0) {
212             throw new InvalidKeyException(
213                     "Diffie-Hellman public key is too small");
214         }
215         if (y.compareTo(rightOpen) >= 0) {
216             throw new InvalidKeyException(
217                     "Diffie-Hellman public key is too large");
218         }
219 
220         // Don't bother to check against the y^q mod p if safe primes are used.
221     }
222 }
223 
224