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 }