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.equals("OracleUcrypto") || 219 providerName.startsWith("SunPKCS11")); 220 } 221 222 /** 223 * Check the format of TLS PreMasterSecret. 224 * <P> 225 * To avoid vulnerabilities described by section 7.4.7.1, RFC 5246, 226 * treating incorrectly formatted message blocks and/or mismatched 227 * version numbers in a manner indistinguishable from correctly 228 * formatted RSA blocks. 229 * 230 * RFC 5246 describes the approach as: 231 * <pre>{@literal 232 * 233 * 1. Generate a string R of 48 random bytes 234 * 235 * 2. Decrypt the message to recover the plaintext M 236 * 237 * 3. If the PKCS#1 padding is not correct, or the length of message 238 * M is not exactly 48 bytes: 239 * pre_master_secret = R 240 * else If ClientHello.client_version <= TLS 1.0, and version 241 * number check is explicitly disabled: 242 * premaster secret = M 243 * else If M[0..1] != ClientHello.client_version: 244 * premaster secret = R 245 * else: 246 * premaster secret = M 247 * 248 * Note that #2 should have completed before the call to this method. 249 * }</pre> 250 * 251 * @param clientVersion the version of the TLS protocol by which the 252 * client wishes to communicate during this session 253 * @param serverVersion the negotiated version of the TLS protocol which 254 * contains the lower of that suggested by the client in the client 255 * hello and the highest supported by the server. 256 * @param encoded the encoded key in its "RAW" encoding format 257 * @param isFailOver whether or not the previous decryption of the 258 * encrypted PreMasterSecret message run into problem 259 * @return the polished PreMasterSecret key in its "RAW" encoding format 260 */ 261 public static byte[] checkTlsPreMasterSecretKey( 262 int clientVersion, int serverVersion, SecureRandom random, 263 byte[] encoded, boolean isFailOver) { 264 265 if (random == null) { 266 random = JCAUtil.getSecureRandom(); 267 } 268 byte[] replacer = new byte[48]; 269 random.nextBytes(replacer); 270 271 if (!isFailOver && (encoded != null)) { 272 // check the length 273 if (encoded.length != 48) { 274 // private, don't need to clone the byte array. 275 return replacer; 276 } 277 278 int encodedVersion = 279 ((encoded[0] & 0xFF) << 8) | (encoded[1] & 0xFF); 280 if (clientVersion != encodedVersion) { 281 if (clientVersion > 0x0301 || // 0x0301: TLSv1 282 serverVersion != encodedVersion) { 283 encoded = replacer; 284 } // Otherwise, For compatibility, we maintain the behavior 285 // that the version in pre_master_secret can be the 286 // negotiated version for TLS v1.0 and SSL v3.0. 287 } 288 289 // private, don't need to clone the byte array. 290 return encoded; 291 } 292 293 // private, don't need to clone the byte array. 294 return replacer; 295 } 296 297 /** 298 * Returns whether the Diffie-Hellman public key is valid or not. 299 * 300 * Per RFC 2631 and NIST SP800-56A, the following algorithm is used to 301 * validate Diffie-Hellman public keys: 302 * 1. Verify that y lies within the interval [2,p-1]. If it does not, 303 * the key is invalid. 304 * 2. Compute y^q mod p. If the result == 1, the key is valid. 305 * Otherwise the key is invalid. 306 */ 307 private static void validateDHPublicKey(DHPublicKey publicKey) 308 throws InvalidKeyException { 309 DHParameterSpec paramSpec = publicKey.getParams(); 310 311 BigInteger p = paramSpec.getP(); 312 BigInteger g = paramSpec.getG(); 313 BigInteger y = publicKey.getY(); 314 315 validateDHPublicKey(p, g, y); 316 } 317 318 private static void validateDHPublicKey(DHPublicKeySpec publicKeySpec) 319 throws InvalidKeyException { 320 validateDHPublicKey(publicKeySpec.getP(), 321 publicKeySpec.getG(), publicKeySpec.getY()); 322 } 323 324 private static void validateDHPublicKey(BigInteger p, 325 BigInteger g, BigInteger y) throws InvalidKeyException { 326 327 // For better interoperability, the interval is limited to [2, p-2]. 328 BigInteger leftOpen = BigInteger.ONE; 329 BigInteger rightOpen = p.subtract(BigInteger.ONE); 330 if (y.compareTo(leftOpen) <= 0) { 331 throw new InvalidKeyException( 332 "Diffie-Hellman public key is too small"); 333 } 334 if (y.compareTo(rightOpen) >= 0) { 335 throw new InvalidKeyException( 336 "Diffie-Hellman public key is too large"); 337 } 338 339 // y^q mod p == 1? 340 // Unable to perform this check as q is unknown in this circumstance. 341 342 // p is expected to be prime. However, it is too expensive to check 343 // that p is prime. Instead, in order to mitigate the impact of 344 // non-prime values, we check that y is not a factor of p. 345 BigInteger r = p.remainder(y); 346 if (r.equals(BigInteger.ZERO)) { 347 throw new InvalidKeyException("Invalid Diffie-Hellman parameters"); 348 } 349 } 350 351 /** 352 * Trim leading (most significant) zeroes from the result. 353 * 354 * @throws NullPointerException if {@code b} is null 355 */ 356 public static byte[] trimZeroes(byte[] b) { 357 int i = 0; 358 while ((i < b.length - 1) && (b[i] == 0)) { 359 i++; 360 } 361 if (i == 0) { 362 return b; 363 } 364 byte[] t = new byte[b.length - i]; 365 System.arraycopy(b, i, t, 0, t.length); 366 return t; 367 } 368 369 } 370