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
27package sun.security.util;
28
29import java.security.Key;
30import java.security.PrivilegedAction;
31import java.security.AccessController;
32import java.security.InvalidKeyException;
33import java.security.interfaces.ECKey;
34import java.security.interfaces.RSAKey;
35import java.security.interfaces.DSAKey;
36import java.security.interfaces.DSAParams;
37import java.security.spec.ECParameterSpec;
38import java.security.spec.KeySpec;
39import javax.crypto.SecretKey;
40import javax.crypto.interfaces.DHKey;
41import javax.crypto.interfaces.DHPublicKey;
42import javax.crypto.spec.DHParameterSpec;
43import javax.crypto.spec.DHPublicKeySpec;
44import java.math.BigInteger;
45
46/**
47 * A utility class to get key length, valiate keys, etc.
48 */
49public 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     */
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     */
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     */
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     */
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     */
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
199    private static void validateDHPublicKey(DHPublicKeySpec publicKeySpec)
200            throws InvalidKeyException {
201        validateDHPublicKey(publicKeySpec.getP(),
202            publicKeySpec.getG(), publicKeySpec.getY());
203    }
204
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