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