1 /* 2 * Copyright (c) 1997, 2012, 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 com.sun.crypto.provider; 27 28 import java.util.*; 29 import java.lang.*; 30 import java.math.BigInteger; 31 import java.security.InvalidAlgorithmParameterException; 32 import java.security.InvalidKeyException; 33 import java.security.Key; 34 import java.security.NoSuchAlgorithmException; 35 import java.security.SecureRandom; 36 import java.security.ProviderException; 37 import java.security.spec.AlgorithmParameterSpec; 38 import java.security.spec.InvalidKeySpecException; 39 import javax.crypto.KeyAgreementSpi; 40 import javax.crypto.ShortBufferException; 41 import javax.crypto.SecretKey; 42 import javax.crypto.spec.*; 43 44 /** 45 * This class implements the Diffie-Hellman key agreement protocol between 46 * any number of parties. 47 * 48 * @author Jan Luehe 49 * 50 */ 51 52 public final class DHKeyAgreement 53 extends KeyAgreementSpi { 54 55 private boolean generateSecret = false; 56 private BigInteger init_p = null; 57 private BigInteger init_g = null; 58 private BigInteger x = BigInteger.ZERO; // the private value 59 private BigInteger y = BigInteger.ZERO; 60 61 /** 62 * Empty constructor 63 */ 64 public DHKeyAgreement() { 65 } 66 67 /** 68 * Initializes this key agreement with the given key and source of 69 * randomness. The given key is required to contain all the algorithm 70 * parameters required for this key agreement. 71 * 72 * <p> If the key agreement algorithm requires random bytes, it gets them 73 * from the given source of randomness, <code>random</code>. 74 * However, if the underlying 75 * algorithm implementation does not require any random bytes, 76 * <code>random</code> is ignored. 77 * 78 * @param key the party's private information. For example, in the case 79 * of the Diffie-Hellman key agreement, this would be the party's own 80 * Diffie-Hellman private key. 81 * @param random the source of randomness 82 * 83 * @exception InvalidKeyException if the given key is 84 * inappropriate for this key agreement, e.g., is of the wrong type or 85 * has an incompatible algorithm type. 86 */ 87 protected void engineInit(Key key, SecureRandom random) 88 throws InvalidKeyException 89 { 90 try { 91 engineInit(key, null, random); 92 } catch (InvalidAlgorithmParameterException e) { 93 // never happens, because we did not pass any parameters 94 } 95 } 96 97 /** 98 * Initializes this key agreement with the given key, set of 99 * algorithm parameters, and source of randomness. 100 * 101 * @param key the party's private information. For example, in the case 102 * of the Diffie-Hellman key agreement, this would be the party's own 103 * Diffie-Hellman private key. 104 * @param params the key agreement parameters 105 * @param random the source of randomness 106 * 107 * @exception InvalidKeyException if the given key is 108 * inappropriate for this key agreement, e.g., is of the wrong type or 109 * has an incompatible algorithm type. 110 * @exception InvalidAlgorithmParameterException if the given parameters 111 * are inappropriate for this key agreement. 112 */ 113 protected void engineInit(Key key, AlgorithmParameterSpec params, 114 SecureRandom random) 115 throws InvalidKeyException, InvalidAlgorithmParameterException 116 { 117 // ignore "random" parameter, because our implementation does not 118 // require any source of randomness 119 generateSecret = false; 120 init_p = null; 121 init_g = null; 122 123 if ((params != null) && !(params instanceof DHParameterSpec)) { 124 throw new InvalidAlgorithmParameterException 125 ("Diffie-Hellman parameters expected"); 126 } 127 if (!(key instanceof javax.crypto.interfaces.DHPrivateKey)) { 128 throw new InvalidKeyException("Diffie-Hellman private key " 129 + "expected"); 130 } 131 javax.crypto.interfaces.DHPrivateKey dhPrivKey; 132 dhPrivKey = (javax.crypto.interfaces.DHPrivateKey)key; 133 134 // check if private key parameters are compatible with 135 // initialized ones 136 if (params != null) { 137 init_p = ((DHParameterSpec)params).getP(); 138 init_g = ((DHParameterSpec)params).getG(); 139 } 140 BigInteger priv_p = dhPrivKey.getParams().getP(); 141 BigInteger priv_g = dhPrivKey.getParams().getG(); 142 if (init_p != null && priv_p != null && !(init_p.equals(priv_p))) { 143 throw new InvalidKeyException("Incompatible parameters"); 144 } 145 if (init_g != null && priv_g != null && !(init_g.equals(priv_g))) { 146 throw new InvalidKeyException("Incompatible parameters"); 147 } 148 if ((init_p == null && priv_p == null) 149 || (init_g == null && priv_g == null)) { 150 throw new InvalidKeyException("Missing parameters"); 151 } 152 init_p = priv_p; 153 init_g = priv_g; 154 155 // store the x value 156 this.x = dhPrivKey.getX(); 157 } 158 159 /** 160 * Executes the next phase of this key agreement with the given 161 * key that was received from one of the other parties involved in this key 162 * agreement. 163 * 164 * @param key the key for this phase. For example, in the case of 165 * Diffie-Hellman between 2 parties, this would be the other party's 166 * Diffie-Hellman public key. 167 * @param lastPhase flag which indicates whether or not this is the last 168 * phase of this key agreement. 169 * 170 * @return the (intermediate) key resulting from this phase, or null if 171 * this phase does not yield a key 172 * 173 * @exception InvalidKeyException if the given key is inappropriate for 174 * this phase. 175 * @exception IllegalStateException if this key agreement has not been 176 * initialized. 177 */ 178 protected Key engineDoPhase(Key key, boolean lastPhase) 179 throws InvalidKeyException, IllegalStateException 180 { 181 if (!(key instanceof javax.crypto.interfaces.DHPublicKey)) { 182 throw new InvalidKeyException("Diffie-Hellman public key " 183 + "expected"); 184 } 185 javax.crypto.interfaces.DHPublicKey dhPubKey; 186 dhPubKey = (javax.crypto.interfaces.DHPublicKey)key; 187 188 if (init_p == null || init_g == null) { 189 throw new IllegalStateException("Not initialized"); 190 } 191 192 // check if public key parameters are compatible with 193 // initialized ones 194 BigInteger pub_p = dhPubKey.getParams().getP(); 195 BigInteger pub_g = dhPubKey.getParams().getG(); 196 if (pub_p != null && !(init_p.equals(pub_p))) { 197 throw new InvalidKeyException("Incompatible parameters"); 198 } 199 if (pub_g != null && !(init_g.equals(pub_g))) { 200 throw new InvalidKeyException("Incompatible parameters"); 201 } 202 203 // store the y value 204 this.y = dhPubKey.getY(); 205 206 // we've received a public key (from one of the other parties), 207 // so we are ready to create the secret, which may be an 208 // intermediate secret, in which case we wrap it into a 209 // Diffie-Hellman public key object and return it. 210 generateSecret = true; 211 if (lastPhase == false) { 212 byte[] intermediate = engineGenerateSecret(); 213 return new DHPublicKey(new BigInteger(1, intermediate), 214 init_p, init_g); 215 } else { 216 return null; 217 } 218 } 219 220 /** 221 * Generates the shared secret and returns it in a new buffer. 222 * 223 * <p>This method resets this <code>KeyAgreementSpi</code> object, 224 * so that it 225 * can be reused for further key agreements. Unless this key agreement is 226 * reinitialized with one of the <code>engineInit</code> methods, the same 227 * private information and algorithm parameters will be used for 228 * subsequent key agreements. 229 * 230 * @return the new buffer with the shared secret 231 * 232 * @exception IllegalStateException if this key agreement has not been 233 * completed yet 234 */ 235 protected byte[] engineGenerateSecret() 236 throws IllegalStateException 237 { 238 int expectedLen = (init_p.bitLength() + 7) >>> 3; 239 byte[] result = new byte[expectedLen]; 240 try { 241 engineGenerateSecret(result, 0); 242 } catch (ShortBufferException sbe) { 243 // should never happen since secret lengths in the two 244 // methods are identical 245 } 246 return result; 247 } 248 249 /** 250 * Generates the shared secret, and places it into the buffer 251 * <code>sharedSecret</code>, beginning at <code>offset</code>. 252 * 253 * <p>If the <code>sharedSecret</code> buffer is too small to hold the 254 * result, a <code>ShortBufferException</code> is thrown. 255 * In this case, this call should be repeated with a larger output buffer. 256 * 257 * <p>This method resets this <code>KeyAgreementSpi</code> object, 258 * so that it 259 * can be reused for further key agreements. Unless this key agreement is 260 * reinitialized with one of the <code>engineInit</code> methods, the same 261 * private information and algorithm parameters will be used for 262 * subsequent key agreements. 263 * 264 * @param sharedSecret the buffer for the shared secret 265 * @param offset the offset in <code>sharedSecret</code> where the 266 * shared secret will be stored 267 * 268 * @return the number of bytes placed into <code>sharedSecret</code> 269 * 270 * @exception IllegalStateException if this key agreement has not been 271 * completed yet 272 * @exception ShortBufferException if the given output buffer is too small 273 * to hold the secret 274 */ 275 protected int engineGenerateSecret(byte[] sharedSecret, int offset) 276 throws IllegalStateException, ShortBufferException 277 { 278 if (generateSecret == false) { 279 throw new IllegalStateException 280 ("Key agreement has not been completed yet"); 281 } 282 283 if (sharedSecret == null) { 284 throw new ShortBufferException 285 ("No buffer provided for shared secret"); 286 } 287 288 BigInteger modulus = init_p; 289 int expectedLen = (modulus.bitLength() + 7) >>> 3; 290 if ((sharedSecret.length - offset) < expectedLen) { 291 throw new ShortBufferException 292 ("Buffer too short for shared secret"); 293 } 294 295 // Reset the key agreement after checking for ShortBufferException 296 // above, so user can recover w/o losing internal state 297 generateSecret = false; 298 299 /* 300 * NOTE: BigInteger.toByteArray() returns a byte array containing 301 * the two's-complement representation of this BigInteger with 302 * the most significant byte is in the zeroth element. This 303 * contains the minimum number of bytes required to represent 304 * this BigInteger, including at least one sign bit whose value 305 * is always 0. 306 * 307 * Keys are always positive, and the above sign bit isn't 308 * actually used when representing keys. (i.e. key = new 309 * BigInteger(1, byteArray)) To obtain an array containing 310 * exactly expectedLen bytes of magnitude, we strip any extra 311 * leading 0's, or pad with 0's in case of a "short" secret. 312 */ 313 byte[] secret = this.y.modPow(this.x, modulus).toByteArray(); 314 if (secret.length == expectedLen) { 315 System.arraycopy(secret, 0, sharedSecret, offset, 316 secret.length); 317 } else { 318 // Array too short, pad it w/ leading 0s 319 if (secret.length < expectedLen) { 320 System.arraycopy(secret, 0, sharedSecret, 321 offset + (expectedLen - secret.length), 322 secret.length); 323 } else { 324 // Array too long, check and trim off the excess 325 if ((secret.length == (expectedLen+1)) && secret[0] == 0) { 326 // ignore the leading sign byte 327 System.arraycopy(secret, 1, sharedSecret, offset, expectedLen); 328 } else { 329 throw new ProviderException("Generated secret is out-of-range"); 330 } 331 } 332 } 333 return expectedLen; 334 } 335 336 /** 337 * Creates the shared secret and returns it as a secret key object 338 * of the requested algorithm type. 339 * 340 * <p>This method resets this <code>KeyAgreementSpi</code> object, 341 * so that it 342 * can be reused for further key agreements. Unless this key agreement is 343 * reinitialized with one of the <code>engineInit</code> methods, the same 344 * private information and algorithm parameters will be used for 345 * subsequent key agreements. 346 * 347 * @param algorithm the requested secret key algorithm 348 * 349 * @return the shared secret key 350 * 351 * @exception IllegalStateException if this key agreement has not been 352 * completed yet 353 * @exception NoSuchAlgorithmException if the requested secret key 354 * algorithm is not available 355 * @exception InvalidKeyException if the shared secret key material cannot 356 * be used to generate a secret key of the requested algorithm type (e.g., 357 * the key material is too short) 358 */ 359 protected SecretKey engineGenerateSecret(String algorithm) 360 throws IllegalStateException, NoSuchAlgorithmException, 361 InvalidKeyException 362 { 363 if (algorithm == null) { 364 throw new NoSuchAlgorithmException("null algorithm"); 365 } 366 byte[] secret = engineGenerateSecret(); 367 if (algorithm.equalsIgnoreCase("DES")) { 368 // DES 369 return new DESKey(secret); 370 } else if (algorithm.equalsIgnoreCase("DESede") 371 || algorithm.equalsIgnoreCase("TripleDES")) { 372 // Triple DES 373 return new DESedeKey(secret); 374 } else if (algorithm.equalsIgnoreCase("Blowfish")) { 375 // Blowfish 376 int keysize = secret.length; 377 if (keysize >= BlowfishConstants.BLOWFISH_MAX_KEYSIZE) 378 keysize = BlowfishConstants.BLOWFISH_MAX_KEYSIZE; 379 SecretKeySpec skey = new SecretKeySpec(secret, 0, keysize, 380 "Blowfish"); 381 return skey; 382 } else if (algorithm.equalsIgnoreCase("AES")) { 383 // AES 384 int keysize = secret.length; 385 SecretKeySpec skey = null; 386 int idx = AESConstants.AES_KEYSIZES.length - 1; 387 while (skey == null && idx >= 0) { 388 // Generate the strongest key using the shared secret 389 // assuming the key sizes in AESConstants class are 390 // in ascending order 391 if (keysize >= AESConstants.AES_KEYSIZES[idx]) { 392 keysize = AESConstants.AES_KEYSIZES[idx]; 393 skey = new SecretKeySpec(secret, 0, keysize, "AES"); 394 } 395 idx--; 396 } 397 if (skey == null) { 398 throw new InvalidKeyException("Key material is too short"); 399 } 400 return skey; 401 } else if (algorithm.equals("TlsPremasterSecret")) { 402 // return entire secret 403 return new SecretKeySpec(secret, "TlsPremasterSecret"); 404 } else { 405 throw new NoSuchAlgorithmException("Unsupported secret key " 406 + "algorithm: "+ algorithm); 407 } 408 } 409 }