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