Home | History | Annotate | Download | only in crypto
      1 /*
      2  * Copyright (C) 2014 The Android Open Source Project
      3  * Copyright (c) 1997, 2011, 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 javax.crypto;
     28 
     29 import java.util.*;
     30 
     31 import java.security.*;
     32 import java.security.Provider.Service;
     33 import java.security.spec.*;
     34 
     35 import sun.security.util.Debug;
     36 import sun.security.jca.*;
     37 import sun.security.jca.GetInstance.Instance;
     38 
     39 /**
     40  * This class provides the functionality of a key agreement (or key
     41  * exchange) protocol.
     42  * <p>
     43  * The keys involved in establishing a shared secret are created by one of the
     44  * key generators (<code>KeyPairGenerator</code> or
     45  * <code>KeyGenerator</code>), a <code>KeyFactory</code>, or as a result from
     46  * an intermediate phase of the key agreement protocol.
     47  *
     48  * <p> For each of the correspondents in the key exchange, <code>doPhase</code>
     49  * needs to be called. For example, if this key exchange is with one other
     50  * party, <code>doPhase</code> needs to be called once, with the
     51  * <code>lastPhase</code> flag set to <code>true</code>.
     52  * If this key exchange is
     53  * with two other parties, <code>doPhase</code> needs to be called twice,
     54  * the first time setting the <code>lastPhase</code> flag to
     55  * <code>false</code>, and the second time setting it to <code>true</code>.
     56  * There may be any number of parties involved in a key exchange.
     57  *
     58  * <p> Android provides the following <code>KeyAgreement</code> algorithms:
     59  * <table>
     60  *     <thead>
     61  *         <tr>
     62  *             <th>Name</th>
     63  *             <th>Supported (API Levels)</th>
     64  *         </tr>
     65  *     </thead>
     66  *     <tbody>
     67  *         <tr>
     68  *             <td>DH</td>
     69  *             <td>1+</td>
     70  *         </tr>
     71  *         <tr>
     72  *             <td>ECDH</td>
     73  *             <td>11+</td>
     74  *         </tr>
     75  *     </tbody>
     76  * </table>
     77  *
     78  * This algorithm is described in the <a href=
     79  * "{@docRoot}openjdk-redirect.html?v=8&path=/technotes/guides/security/StandardNames.html#KeyAgreement">
     80  * KeyAgreement section</a> of the
     81  * Java Cryptography Architecture Standard Algorithm Name Documentation.
     82  *
     83  * @author Jan Luehe
     84  *
     85  * @see KeyGenerator
     86  * @see SecretKey
     87  * @since 1.4
     88  */
     89 
     90 public class KeyAgreement {
     91 
     92     private static final Debug debug =
     93                         Debug.getInstance("jca", "KeyAgreement");
     94 
     95     // The provider
     96     private Provider provider;
     97 
     98     // The provider implementation (delegate)
     99     private KeyAgreementSpi spi;
    100 
    101     // The name of the key agreement algorithm.
    102     private final String algorithm;
    103 
    104     private final Object lock;
    105 
    106     /**
    107      * Creates a KeyAgreement object.
    108      *
    109      * @param keyAgreeSpi the delegate
    110      * @param provider the provider
    111      * @param algorithm the algorithm
    112      */
    113     protected KeyAgreement(KeyAgreementSpi keyAgreeSpi, Provider provider,
    114                            String algorithm) {
    115         this.spi = keyAgreeSpi;
    116         this.provider = provider;
    117         this.algorithm = algorithm;
    118         lock = null;
    119     }
    120 
    121     private KeyAgreement(String algorithm) {
    122         this.algorithm = algorithm;
    123         lock = new Object();
    124     }
    125 
    126     /**
    127      * Returns the algorithm name of this <code>KeyAgreement</code> object.
    128      *
    129      * <p>This is the same name that was specified in one of the
    130      * <code>getInstance</code> calls that created this
    131      * <code>KeyAgreement</code> object.
    132      *
    133      * @return the algorithm name of this <code>KeyAgreement</code> object.
    134      */
    135     public final String getAlgorithm() {
    136         return this.algorithm;
    137     }
    138 
    139     /**
    140      * Returns a <code>KeyAgreement</code> object that implements the
    141      * specified key agreement algorithm.
    142      *
    143      * <p> This method traverses the list of registered security Providers,
    144      * starting with the most preferred Provider.
    145      * A new KeyAgreement object encapsulating the
    146      * KeyAgreementSpi implementation from the first
    147      * Provider that supports the specified algorithm is returned.
    148      *
    149      * <p> Note that the list of registered providers may be retrieved via
    150      * the {@link Security#getProviders() Security.getProviders()} method.
    151      *
    152      * @param algorithm the standard name of the requested key agreement
    153      * algorithm.
    154      * See the KeyAgreement section in the <a href=
    155      * "{@docRoot}openjdk-redirect.html?v=8&path=/technotes/guides/security/StandardNames.html#KeyAgreement">
    156      * Java Cryptography Architecture Standard Algorithm Name Documentation
    157      * for information about standard algorithm names.
    158      *
    159      * @return the new <code>KeyAgreement</code> object.
    160      *
    161      * @exception NullPointerException if the specified algorithm
    162      *          is null.
    163      *
    164      * @exception NoSuchAlgorithmException if no Provider supports a
    165      *          KeyAgreementSpi implementation for the
    166      *          specified algorithm.
    167      *
    168      * @see java.security.Provider
    169      */
    170     public static final KeyAgreement getInstance(String algorithm)
    171             throws NoSuchAlgorithmException {
    172         List services = GetInstance.getServices("KeyAgreement", algorithm);
    173         // make sure there is at least one service from a signed provider
    174         Iterator t = services.iterator();
    175         while (t.hasNext()) {
    176             Service s = (Service)t.next();
    177             if (JceSecurity.canUseProvider(s.getProvider()) == false) {
    178                 continue;
    179             }
    180             return new KeyAgreement(algorithm);
    181         }
    182         throw new NoSuchAlgorithmException
    183                                 ("Algorithm " + algorithm + " not available");
    184     }
    185 
    186     /**
    187      * Returns a <code>KeyAgreement</code> object that implements the
    188      * specified key agreement algorithm.
    189      *
    190      * <p> A new KeyAgreement object encapsulating the
    191      * KeyAgreementSpi implementation from the specified provider
    192      * is returned.  The specified provider must be registered
    193      * in the security provider list.
    194      *
    195      * <p> Note that the list of registered providers may be retrieved via
    196      * the {@link Security#getProviders() Security.getProviders()} method.
    197      *
    198      * @param algorithm the standard name of the requested key agreement
    199      * algorithm.
    200      * See the KeyAgreement section in the <a href=
    201      * "{@docRoot}openjdk-redirect.html?v=8&path=/technotes/guides/security/StandardNames.html#KeyAgreement">
    202      * Java Cryptography Architecture Standard Algorithm Name Documentation
    203      * for information about standard algorithm names.
    204      *
    205      * @param provider the name of the provider.
    206      *
    207      * @return the new <code>KeyAgreement</code> object.
    208      *
    209      * @exception NullPointerException if the specified algorithm
    210      *          is null.
    211      *
    212      * @exception NoSuchAlgorithmException if a KeyAgreementSpi
    213      *          implementation for the specified algorithm is not
    214      *          available from the specified provider.
    215      *
    216      * @exception NoSuchProviderException if the specified provider is not
    217      *          registered in the security provider list.
    218      *
    219      * @exception IllegalArgumentException if the <code>provider</code>
    220      *          is null or empty.
    221      *
    222      * @see java.security.Provider
    223      */
    224     public static final KeyAgreement getInstance(String algorithm,
    225             String provider) throws NoSuchAlgorithmException,
    226             NoSuchProviderException {
    227         Instance instance = JceSecurity.getInstance
    228                 ("KeyAgreement", KeyAgreementSpi.class, algorithm, provider);
    229         return new KeyAgreement((KeyAgreementSpi)instance.impl,
    230                 instance.provider, algorithm);
    231     }
    232 
    233     /**
    234      * Returns a <code>KeyAgreement</code> object that implements the
    235      * specified key agreement algorithm.
    236      *
    237      * <p> A new KeyAgreement object encapsulating the
    238      * KeyAgreementSpi implementation from the specified Provider
    239      * object is returned.  Note that the specified Provider object
    240      * does not have to be registered in the provider list.
    241      *
    242      * @param algorithm the standard name of the requested key agreement
    243      * algorithm.
    244      * See the KeyAgreement section in the <a href=
    245      * "{@docRoot}openjdk-redirect.html?v=8&path=/technotes/guides/security/StandardNames.html#KeyAgreement">
    246      * Java Cryptography Architecture Standard Algorithm Name Documentation
    247      * for information about standard algorithm names.
    248      *
    249      * @param provider the provider.
    250      *
    251      * @return the new <code>KeyAgreement</code> object.
    252      *
    253      * @exception NullPointerException if the specified algorithm
    254      *          is null.
    255      *
    256      * @exception NoSuchAlgorithmException if a KeyAgreementSpi
    257      *          implementation for the specified algorithm is not available
    258      *          from the specified Provider object.
    259      *
    260      * @exception IllegalArgumentException if the <code>provider</code>
    261      *          is null.
    262      *
    263      * @see java.security.Provider
    264      */
    265     public static final KeyAgreement getInstance(String algorithm,
    266             Provider provider) throws NoSuchAlgorithmException {
    267         Instance instance = JceSecurity.getInstance
    268                 ("KeyAgreement", KeyAgreementSpi.class, algorithm, provider);
    269         return new KeyAgreement((KeyAgreementSpi)instance.impl,
    270                 instance.provider, algorithm);
    271     }
    272 
    273     // max number of debug warnings to print from chooseFirstProvider()
    274     private static int warnCount = 10;
    275 
    276     /**
    277      * Choose the Spi from the first provider available. Used if
    278      * delayed provider selection is not possible because init()
    279      * is not the first method called.
    280      */
    281     void chooseFirstProvider() {
    282         if (spi != null) {
    283             return;
    284         }
    285         synchronized (lock) {
    286             if (spi != null) {
    287                 return;
    288             }
    289             if (debug != null) {
    290                 int w = --warnCount;
    291                 if (w >= 0) {
    292                     debug.println("KeyAgreement.init() not first method "
    293                         + "called, disabling delayed provider selection");
    294                     if (w == 0) {
    295                         debug.println("Further warnings of this type will "
    296                             + "be suppressed");
    297                     }
    298                     new Exception("Call trace").printStackTrace();
    299                 }
    300             }
    301             Exception lastException = null;
    302             for (Service s : GetInstance.getServices("KeyAgreement", algorithm)) {
    303                 if (JceSecurity.canUseProvider(s.getProvider()) == false) {
    304                     continue;
    305                 }
    306                 try {
    307                     Object obj = s.newInstance(null);
    308                     if (obj instanceof KeyAgreementSpi == false) {
    309                         continue;
    310                     }
    311                     spi = (KeyAgreementSpi)obj;
    312                     provider = s.getProvider();
    313                     // not needed any more
    314                     return;
    315                 } catch (Exception e) {
    316                     lastException = e;
    317                 }
    318             }
    319             ProviderException e = new ProviderException
    320                     ("Could not construct KeyAgreementSpi instance");
    321             if (lastException != null) {
    322                 e.initCause(lastException);
    323             }
    324             throw e;
    325         }
    326     }
    327 
    328     private final static int I_NO_PARAMS = 1;
    329     private final static int I_PARAMS    = 2;
    330 
    331     private void implInit(KeyAgreementSpi spi, int type, Key key,
    332             AlgorithmParameterSpec params, SecureRandom random)
    333             throws InvalidKeyException, InvalidAlgorithmParameterException {
    334         if (type == I_NO_PARAMS) {
    335             spi.engineInit(key, random);
    336         } else { // I_PARAMS
    337             spi.engineInit(key, params, random);
    338         }
    339     }
    340 
    341     private void chooseProvider(int initType, Key key,
    342             AlgorithmParameterSpec params, SecureRandom random)
    343             throws InvalidKeyException, InvalidAlgorithmParameterException {
    344         synchronized (lock) {
    345             if (spi != null && key == null) {
    346                 implInit(spi, initType, key, params, random);
    347                 return;
    348             }
    349             Exception lastException = null;
    350             for (Service s : GetInstance.getServices("KeyAgreement", algorithm)) {
    351                 // if provider says it does not support this key, ignore it
    352                 if (s.supportsParameter(key) == false) {
    353                     continue;
    354                 }
    355                 if (JceSecurity.canUseProvider(s.getProvider()) == false) {
    356                     continue;
    357                 }
    358                 try {
    359                     KeyAgreementSpi spi = (KeyAgreementSpi)s.newInstance(null);
    360                     implInit(spi, initType, key, params, random);
    361                     provider = s.getProvider();
    362                     this.spi = spi;
    363                     return;
    364                 } catch (Exception e) {
    365                     // NoSuchAlgorithmException from newInstance()
    366                     // InvalidKeyException from init()
    367                     // RuntimeException (ProviderException) from init()
    368                     if (lastException == null) {
    369                         lastException = e;
    370                     }
    371                 }
    372             }
    373             // no working provider found, fail
    374             if (lastException instanceof InvalidKeyException) {
    375                 throw (InvalidKeyException)lastException;
    376             }
    377             if (lastException instanceof InvalidAlgorithmParameterException) {
    378                 throw (InvalidAlgorithmParameterException)lastException;
    379             }
    380             if (lastException instanceof RuntimeException) {
    381                 throw (RuntimeException)lastException;
    382             }
    383             String kName = (key != null) ? key.getClass().getName() : "(null)";
    384             throw new InvalidKeyException
    385                 ("No installed provider supports this key: "
    386                 + kName, lastException);
    387         }
    388     }
    389 
    390     /**
    391      * Returns the provider of this <code>KeyAgreement</code> object.
    392      *
    393      * @return the provider of this <code>KeyAgreement</code> object
    394      */
    395     public final Provider getProvider() {
    396         chooseFirstProvider();
    397         return this.provider;
    398     }
    399 
    400     /**
    401      * Initializes this key agreement with the given key, which is required to
    402      * contain all the algorithm parameters required for this key agreement.
    403      *
    404      * <p> If this key agreement requires any random bytes, it will get
    405      * them using the
    406      * {@link SecureRandom <code>SecureRandom</code>}
    407      * implementation of the highest-priority
    408      * installed provider as the source of randomness.
    409      * (If none of the installed providers supply an implementation of
    410      * SecureRandom, a system-provided source of randomness will be used.)
    411      *
    412      * @param key the party's private information. For example, in the case
    413      * of the Diffie-Hellman key agreement, this would be the party's own
    414      * Diffie-Hellman private key.
    415      *
    416      * @exception InvalidKeyException if the given key is
    417      * inappropriate for this key agreement, e.g., is of the wrong type or
    418      * has an incompatible algorithm type.
    419      */
    420     public final void init(Key key) throws InvalidKeyException {
    421         init(key, JceSecurity.RANDOM);
    422     }
    423 
    424     /**
    425      * Initializes this key agreement with the given key and source of
    426      * randomness. The given key is required to contain all the algorithm
    427      * parameters required for this key agreement.
    428      *
    429      * <p> If the key agreement algorithm requires random bytes, it gets them
    430      * from the given source of randomness, <code>random</code>.
    431      * However, if the underlying
    432      * algorithm implementation does not require any random bytes,
    433      * <code>random</code> is ignored.
    434      *
    435      * @param key the party's private information. For example, in the case
    436      * of the Diffie-Hellman key agreement, this would be the party's own
    437      * Diffie-Hellman private key.
    438      * @param random the source of randomness
    439      *
    440      * @exception InvalidKeyException if the given key is
    441      * inappropriate for this key agreement, e.g., is of the wrong type or
    442      * has an incompatible algorithm type.
    443      */
    444     public final void init(Key key, SecureRandom random)
    445             throws InvalidKeyException {
    446         if (spi != null && (key == null || lock == null)) {
    447             spi.engineInit(key, random);
    448         } else {
    449             try {
    450                 chooseProvider(I_NO_PARAMS, key, null, random);
    451             } catch (InvalidAlgorithmParameterException e) {
    452                 // should never occur
    453                 throw new InvalidKeyException(e);
    454             }
    455         }
    456     }
    457 
    458     /**
    459      * Initializes this key agreement with the given key and set of
    460      * algorithm parameters.
    461      *
    462      * <p> If this key agreement requires any random bytes, it will get
    463      * them using the
    464      * {@link SecureRandom <code>SecureRandom</code>}
    465      * implementation of the highest-priority
    466      * installed provider as the source of randomness.
    467      * (If none of the installed providers supply an implementation of
    468      * SecureRandom, a system-provided source of randomness will be used.)
    469      *
    470      * @param key the party's private information. For example, in the case
    471      * of the Diffie-Hellman key agreement, this would be the party's own
    472      * Diffie-Hellman private key.
    473      * @param params the key agreement parameters
    474      *
    475      * @exception InvalidKeyException if the given key is
    476      * inappropriate for this key agreement, e.g., is of the wrong type or
    477      * has an incompatible algorithm type.
    478      * @exception InvalidAlgorithmParameterException if the given parameters
    479      * are inappropriate for this key agreement.
    480      */
    481     public final void init(Key key, AlgorithmParameterSpec params)
    482         throws InvalidKeyException, InvalidAlgorithmParameterException
    483     {
    484         init(key, params, JceSecurity.RANDOM);
    485     }
    486 
    487     /**
    488      * Initializes this key agreement with the given key, set of
    489      * algorithm parameters, and source of randomness.
    490      *
    491      * @param key the party's private information. For example, in the case
    492      * of the Diffie-Hellman key agreement, this would be the party's own
    493      * Diffie-Hellman private key.
    494      * @param params the key agreement parameters
    495      * @param random the source of randomness
    496      *
    497      * @exception InvalidKeyException if the given key is
    498      * inappropriate for this key agreement, e.g., is of the wrong type or
    499      * has an incompatible algorithm type.
    500      * @exception InvalidAlgorithmParameterException if the given parameters
    501      * are inappropriate for this key agreement.
    502      */
    503     public final void init(Key key, AlgorithmParameterSpec params,
    504                            SecureRandom random)
    505         throws InvalidKeyException, InvalidAlgorithmParameterException
    506     {
    507         if (spi != null) {
    508             spi.engineInit(key, params, random);
    509         } else {
    510             chooseProvider(I_PARAMS, key, params, random);
    511         }
    512     }
    513 
    514     /**
    515      * Executes the next phase of this key agreement with the given
    516      * key that was received from one of the other parties involved in this key
    517      * agreement.
    518      *
    519      * @param key the key for this phase. For example, in the case of
    520      * Diffie-Hellman between 2 parties, this would be the other party's
    521      * Diffie-Hellman public key.
    522      * @param lastPhase flag which indicates whether or not this is the last
    523      * phase of this key agreement.
    524      *
    525      * @return the (intermediate) key resulting from this phase, or null
    526      * if this phase does not yield a key
    527      *
    528      * @exception InvalidKeyException if the given key is inappropriate for
    529      * this phase.
    530      * @exception IllegalStateException if this key agreement has not been
    531      * initialized.
    532      */
    533     public final Key doPhase(Key key, boolean lastPhase)
    534         throws InvalidKeyException, IllegalStateException
    535     {
    536         chooseFirstProvider();
    537         return spi.engineDoPhase(key, lastPhase);
    538     }
    539 
    540     /**
    541      * Generates the shared secret and returns it in a new buffer.
    542      *
    543      * <p>This method resets this <code>KeyAgreement</code> object, so that it
    544      * can be reused for further key agreements. Unless this key agreement is
    545      * reinitialized with one of the <code>init</code> methods, the same
    546      * private information and algorithm parameters will be used for
    547      * subsequent key agreements.
    548      *
    549      * @return the new buffer with the shared secret
    550      *
    551      * @exception IllegalStateException if this key agreement has not been
    552      * completed yet
    553      */
    554     public final byte[] generateSecret() throws IllegalStateException {
    555         chooseFirstProvider();
    556         return spi.engineGenerateSecret();
    557     }
    558 
    559     /**
    560      * Generates the shared secret, and places it into the buffer
    561      * <code>sharedSecret</code>, beginning at <code>offset</code> inclusive.
    562      *
    563      * <p>If the <code>sharedSecret</code> buffer is too small to hold the
    564      * result, a <code>ShortBufferException</code> is thrown.
    565      * In this case, this call should be repeated with a larger output buffer.
    566      *
    567      * <p>This method resets this <code>KeyAgreement</code> object, so that it
    568      * can be reused for further key agreements. Unless this key agreement is
    569      * reinitialized with one of the <code>init</code> methods, the same
    570      * private information and algorithm parameters will be used for
    571      * subsequent key agreements.
    572      *
    573      * @param sharedSecret the buffer for the shared secret
    574      * @param offset the offset in <code>sharedSecret</code> where the
    575      * shared secret will be stored
    576      *
    577      * @return the number of bytes placed into <code>sharedSecret</code>
    578      *
    579      * @exception IllegalStateException if this key agreement has not been
    580      * completed yet
    581      * @exception ShortBufferException if the given output buffer is too small
    582      * to hold the secret
    583      */
    584     public final int generateSecret(byte[] sharedSecret, int offset)
    585         throws IllegalStateException, ShortBufferException
    586     {
    587         chooseFirstProvider();
    588         return spi.engineGenerateSecret(sharedSecret, offset);
    589     }
    590 
    591     /**
    592      * Creates the shared secret and returns it as a <code>SecretKey</code>
    593      * object of the specified algorithm.
    594      *
    595      * <p>This method resets this <code>KeyAgreement</code> object, so that it
    596      * can be reused for further key agreements. Unless this key agreement is
    597      * reinitialized with one of the <code>init</code> methods, the same
    598      * private information and algorithm parameters will be used for
    599      * subsequent key agreements.
    600      *
    601      * @param algorithm the requested secret-key algorithm
    602      *
    603      * @return the shared secret key
    604      *
    605      * @exception IllegalStateException if this key agreement has not been
    606      * completed yet
    607      * @exception NoSuchAlgorithmException if the specified secret-key
    608      * algorithm is not available
    609      * @exception InvalidKeyException if the shared secret-key material cannot
    610      * be used to generate a secret key of the specified algorithm (e.g.,
    611      * the key material is too short)
    612      */
    613     public final SecretKey generateSecret(String algorithm)
    614         throws IllegalStateException, NoSuchAlgorithmException,
    615             InvalidKeyException
    616     {
    617         chooseFirstProvider();
    618         return spi.engineGenerateSecret(algorithm);
    619     }
    620 }
    621