Home | History | Annotate | Download | only in crypto
      1 /*
      2  *  Licensed to the Apache Software Foundation (ASF) under one or more
      3  *  contributor license agreements.  See the NOTICE file distributed with
      4  *  this work for additional information regarding copyright ownership.
      5  *  The ASF licenses this file to You under the Apache License, Version 2.0
      6  *  (the "License"); you may not use this file except in compliance with
      7  *  the License.  You may obtain a copy of the License at
      8  *
      9  *     http://www.apache.org/licenses/LICENSE-2.0
     10  *
     11  *  Unless required by applicable law or agreed to in writing, software
     12  *  distributed under the License is distributed on an "AS IS" BASIS,
     13  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     14  *  See the License for the specific language governing permissions and
     15  *  limitations under the License.
     16  */
     17 
     18 package javax.crypto;
     19 
     20 import java.security.InvalidAlgorithmParameterException;
     21 import java.security.InvalidKeyException;
     22 import java.security.Key;
     23 import java.security.NoSuchAlgorithmException;
     24 import java.security.NoSuchProviderException;
     25 import java.security.Provider;
     26 import java.security.ProviderException;
     27 import java.security.SecureRandom;
     28 import java.security.Security;
     29 import java.security.spec.AlgorithmParameterSpec;
     30 import java.util.ArrayList;
     31 import org.apache.harmony.security.fortress.Engine;
     32 
     33 /**
     34  * This class provides the functionality for a key exchange protocol. This
     35  * enables two or more parties to agree on a secret key for symmetric
     36  * cryptography.
     37  */
     38 public class KeyAgreement {
     39 
     40     // The service name.
     41     private static final String SERVICE = "KeyAgreement";
     42 
     43     // Used to access common engine functionality
     44     private static final Engine ENGINE = new Engine(SERVICE);
     45 
     46     // Store SecureRandom
     47     private static final SecureRandom RANDOM = new SecureRandom();
     48 
     49     // Store used provider
     50     private Provider provider;
     51 
     52     // Provider that was requested during creation.
     53     private final Provider specifiedProvider;
     54 
     55     // Store used spi implementation
     56     private KeyAgreementSpi spiImpl;
     57 
     58     // Store used algorithm name
     59     private final String algorithm;
     60 
     61     /**
     62      * Lock held while the SPI is initializing.
     63      */
     64     private final Object initLock = new Object();
     65 
     66     /**
     67      * Creates a new {@code KeyAgreement} instance.
     68      *
     69      * @param keyAgreeSpi
     70      *            the <b>SPI</b> delegate.
     71      * @param provider
     72      *            the provider providing this KeyAgreement.
     73      * @param algorithm
     74      *            the name of the key agreement algorithm.
     75      */
     76     protected KeyAgreement(KeyAgreementSpi keyAgreeSpi, Provider provider,
     77             String algorithm) {
     78         this.spiImpl = keyAgreeSpi;
     79         this.specifiedProvider = provider;
     80         this.algorithm = algorithm;
     81     }
     82 
     83     /**
     84      * Returns the name of the key agreement algorithm.
     85      *
     86      * @return the name of the key agreement algorithm.
     87      */
     88     public final String getAlgorithm() {
     89         return algorithm;
     90     }
     91 
     92     /**
     93      * Returns the provider for this {@code KeyAgreement} instance.
     94      *
     95      * @return the provider for this {@code KeyAgreement} instance.
     96      */
     97     public final Provider getProvider() {
     98         getSpi();
     99         return provider;
    100     }
    101 
    102     /**
    103      * Creates a new {@code KeyAgreement} for the specified algorithm.
    104      *
    105      * @param algorithm
    106      *            the name of the key agreement algorithm to create.
    107      * @return a key agreement for the specified algorithm.
    108      * @throws NoSuchAlgorithmException
    109      *             if no installed provider can provide the requested algorithm.
    110      * @throws NullPointerException
    111      *             if the specified algorithm is {@code null}.
    112      */
    113     public static final KeyAgreement getInstance(String algorithm) throws NoSuchAlgorithmException {
    114         return getKeyAgreement(algorithm, null);
    115     }
    116 
    117     /**
    118      * Creates a new {@code KeyAgreement} for the specified algorithm from the
    119      * specified provider.
    120      *
    121      * @param algorithm
    122      *            the name of the key agreement algorithm to create.
    123      * @param provider
    124      *            the name of the provider that provides the requested
    125      *            algorithm.
    126      * @return a key agreement for the specified algorithm from the specified
    127      *         provider.
    128      * @throws NoSuchAlgorithmException
    129      *             if the specified provider cannot provide the requested
    130      *             algorithm.
    131      * @throws NoSuchProviderException
    132      *             if the specified provider does not exist.
    133      * @throws IllegalArgumentException
    134      *             if the specified provider name is {@code null} or empty.
    135      */
    136     public static final KeyAgreement getInstance(String algorithm, String provider)
    137             throws NoSuchAlgorithmException, NoSuchProviderException {
    138         if (provider == null || provider.isEmpty()) {
    139             throw new IllegalArgumentException("Provider is null or empty");
    140         }
    141         Provider impProvider = Security.getProvider(provider);
    142         if (impProvider == null) {
    143             throw new NoSuchProviderException(provider);
    144         }
    145         return getKeyAgreement(algorithm, impProvider);
    146     }
    147 
    148     /**
    149      * Create a new {@code KeyAgreement} for the specified algorithm from the
    150      * specified provider. The {@code provider} supplied does not have to be
    151      * registered.
    152      *
    153      * @param algorithm
    154      *            the name of the key agreement algorithm to create.
    155      * @param provider
    156      *            the provider that provides the requested algorithm.
    157      * @return a key agreement for the specified algorithm from the specified
    158      *         provider.
    159      * @throws NoSuchAlgorithmException
    160      *             if the specified provider cannot provide the requested
    161      *             algorithm.
    162      * @throws IllegalArgumentException
    163      *             if the specified provider is {@code null}.
    164      * @throws NullPointerException
    165      *             if the specified algorithm name is {@code null}.
    166      */
    167     public static final KeyAgreement getInstance(String algorithm, Provider provider)
    168             throws NoSuchAlgorithmException {
    169         if (provider == null) {
    170             throw new IllegalArgumentException("provider == null");
    171         }
    172         return getKeyAgreement(algorithm, provider);
    173     }
    174 
    175     private static KeyAgreement getKeyAgreement(String algorithm, Provider provider)
    176             throws NoSuchAlgorithmException {
    177         if (algorithm == null) {
    178             throw new NullPointerException("algorithm == null");
    179         }
    180 
    181         boolean providerSupportsAlgorithm;
    182         try {
    183             providerSupportsAlgorithm = tryAlgorithm(null /* key */, provider, algorithm) != null;
    184         } catch (InvalidKeyException e) {
    185             throw new IllegalStateException("InvalidKeyException thrown when key == null", e);
    186         }
    187         if (!providerSupportsAlgorithm) {
    188             if (provider == null) {
    189                 throw new NoSuchAlgorithmException("No provider found for " + algorithm);
    190             } else {
    191                 throw new NoSuchAlgorithmException("Provider " + provider.getName()
    192                         + " does not provide " + algorithm);
    193             }
    194         }
    195         return new KeyAgreement(null, provider, algorithm);
    196     }
    197 
    198     /**
    199      * @throws InvalidKeyException if the specified key cannot be used to
    200      *             initialize any provider.
    201      */
    202     private static Engine.SpiAndProvider tryAlgorithm(Key key, Provider provider, String algorithm)
    203             throws InvalidKeyException {
    204         if (provider != null) {
    205             Provider.Service service = provider.getService(SERVICE, algorithm);
    206             if (service == null) {
    207                 return null;
    208             }
    209             return tryAlgorithmWithProvider(service);
    210         }
    211         ArrayList<Provider.Service> services = ENGINE.getServices(algorithm);
    212         if (services == null || services.isEmpty()) {
    213             return null;
    214         }
    215         boolean keySupported = false;
    216         for (Provider.Service service : services) {
    217             if (key == null || service.supportsParameter(key)) {
    218                 keySupported = true;
    219                 Engine.SpiAndProvider sap = tryAlgorithmWithProvider(service);
    220                 if (sap != null) {
    221                     return sap;
    222                 }
    223             }
    224         }
    225         if (!keySupported) {
    226             throw new InvalidKeyException("No provider supports the provided key");
    227         }
    228         return null;
    229     }
    230 
    231     private static Engine.SpiAndProvider tryAlgorithmWithProvider(Provider.Service service) {
    232         try {
    233             Engine.SpiAndProvider sap = ENGINE.getInstance(service, null);
    234             if (sap.spi == null || sap.provider == null) {
    235                 return null;
    236             }
    237             if (!(sap.spi instanceof KeyAgreementSpi)) {
    238                 return null;
    239             }
    240             return sap;
    241         } catch (NoSuchAlgorithmException ignored) {
    242         }
    243         return null;
    244     }
    245 
    246     /**
    247      * Makes sure a KeyAgreementSpi that matches this type is selected.
    248      *
    249      * @throws InvalidKeyException if the specified key cannot be used to
    250      *             initialize this key agreement.
    251      */
    252     private KeyAgreementSpi getSpi(Key key) throws InvalidKeyException {
    253         synchronized (initLock) {
    254             if (spiImpl != null && key == null) {
    255                 return spiImpl;
    256             }
    257 
    258             final Engine.SpiAndProvider sap = tryAlgorithm(key, specifiedProvider, algorithm);
    259             if (sap == null) {
    260                 throw new ProviderException("No provider for " + getAlgorithm());
    261             }
    262 
    263             spiImpl = (KeyAgreementSpi) sap.spi;
    264             provider = sap.provider;
    265 
    266             return spiImpl;
    267         }
    268     }
    269 
    270     /**
    271      * Convenience call when the Key is not available.
    272      */
    273     private KeyAgreementSpi getSpi() {
    274         try {
    275             return getSpi(null /* key */);
    276         } catch (InvalidKeyException e) {
    277             throw new IllegalStateException("InvalidKeyException thrown when key == null", e);
    278         }
    279     }
    280 
    281     /**
    282      * Returns the {@code KeyAgreementSpi} backing this {@code KeyAgreement} or {@code null} if no
    283      * {@code KeyAgreementSpi} is backing this {@code KeyAgreement}.
    284      *
    285      * @hide
    286      */
    287     public KeyAgreementSpi getCurrentSpi() {
    288         synchronized (initLock) {
    289             return spiImpl;
    290         }
    291     }
    292 
    293     /**
    294      * Initializes this {@code KeyAgreement} with the specified key.
    295      *
    296      * @param key the key to initialize this key agreement.
    297      * @throws InvalidKeyException if the specified key cannot be used to
    298      *             initialize this key agreement.
    299      */
    300     public final void init(Key key) throws InvalidKeyException {
    301         getSpi(key).engineInit(key, RANDOM);//new SecureRandom());
    302     }
    303 
    304     /**
    305      * Initializes this {@code KeyAgreement} with the specified key and the
    306      * specified randomness source.
    307      *
    308      * @param key
    309      *            the key to initialize this key agreement.
    310      * @param random
    311      *            the source for any randomness needed.
    312      * @throws InvalidKeyException
    313      *             if the specified key cannot be used to initialize this key
    314      *             agreement.
    315      */
    316     public final void init(Key key, SecureRandom random)
    317             throws InvalidKeyException {
    318         getSpi(key).engineInit(key, random);
    319     }
    320 
    321     /**
    322      * Initializes this {@code KeyAgreement} with the specified key and the
    323      * algorithm parameters.
    324      *
    325      * @param key
    326      *            the key to initialize this key agreement.
    327      * @param params
    328      *            the parameters for this key agreement algorithm.
    329      * @throws InvalidKeyException
    330      *             if the specified key cannot be used to initialize this key
    331      *             agreement.
    332      * @throws InvalidAlgorithmParameterException
    333      *             if the specified parameters are invalid for this key
    334      *             agreement algorithm.
    335      */
    336     public final void init(Key key, AlgorithmParameterSpec params)
    337             throws InvalidKeyException, InvalidAlgorithmParameterException {
    338         getSpi(key).engineInit(key, params, RANDOM);//new SecureRandom());
    339     }
    340 
    341     /**
    342      * Initializes this {@code KeyAgreement} with the specified key, algorithm
    343      * parameters and randomness source.
    344      *
    345      * @param key
    346      *            the key to initialize this key agreement.
    347      * @param params
    348      *            the parameters for this key agreement algorithm.
    349      * @param random
    350      *            the source for any randomness needed.
    351      * @throws InvalidKeyException
    352      *             if the specified key cannot be used to initialize this key
    353      *             agreement.
    354      * @throws InvalidAlgorithmParameterException
    355      *             if the specified parameters are invalid for this key
    356      *             agreement algorithm.
    357      */
    358     public final void init(Key key, AlgorithmParameterSpec params,
    359             SecureRandom random) throws InvalidKeyException,
    360             InvalidAlgorithmParameterException {
    361         getSpi(key).engineInit(key, params, random);
    362     }
    363 
    364     /**
    365      * Does the next (or the last) phase of the key agreement, using the
    366      * specified key.
    367      *
    368      * @param key
    369      *            the key received from the other party for this phase.
    370      * @param lastPhase
    371      *            set to {@code true} if this is the last phase of this key
    372      *            agreement.
    373      * @return the intermediate key from this phase or {@code null} if there is
    374      *         no intermediate key for this phase.
    375      * @throws InvalidKeyException
    376      *             if the specified key cannot be used in this key agreement or
    377      *             this phase,
    378      * @throws IllegalStateException
    379      *             if this instance has not been initialized.
    380      */
    381     public final Key doPhase(Key key, boolean lastPhase)
    382             throws InvalidKeyException, IllegalStateException {
    383         return getSpi().engineDoPhase(key, lastPhase);
    384     }
    385 
    386     /**
    387      * Generates the shared secret.
    388      *
    389      * @return the generated shared secret.
    390      * @throws IllegalStateException
    391      *             if this key agreement is not complete.
    392      */
    393     public final byte[] generateSecret() throws IllegalStateException {
    394         return getSpi().engineGenerateSecret();
    395     }
    396 
    397     /**
    398      * Generates the shared secret and stores it into the buffer {@code
    399      * sharedSecred} at {@code offset}.
    400      *
    401      * @param sharedSecret
    402      *            the buffer to store the shared secret.
    403      * @param offset
    404      *            the offset in the buffer.
    405      * @return the number of bytes stored in the buffer.
    406      * @throws IllegalStateException
    407      *             if this key agreement is not complete.
    408      * @throws ShortBufferException
    409      *             if the specified buffer is too small for the shared secret.
    410      */
    411     public final int generateSecret(byte[] sharedSecret, int offset)
    412             throws IllegalStateException, ShortBufferException {
    413         return getSpi().engineGenerateSecret(sharedSecret, offset);
    414     }
    415 
    416     /**
    417      * Generates the shared secret.
    418      *
    419      * @param algorithm
    420      *            the algorithm to for the {@code SecretKey}
    421      * @return the shared secret as a {@code SecretKey} of the specified
    422      *         algorithm.
    423      * @throws IllegalStateException
    424      *             if this key agreement is not complete.
    425      * @throws NoSuchAlgorithmException
    426      *             if the specified algorithm for the secret key does not
    427      *             exists.
    428      * @throws InvalidKeyException
    429      *             if a {@code SecretKey} with the specified algorithm cannot be
    430      *             created using the generated shared secret.
    431      */
    432     public final SecretKey generateSecret(String algorithm)
    433             throws IllegalStateException, NoSuchAlgorithmException,
    434             InvalidKeyException {
    435         return getSpi().engineGenerateSecret(algorithm);
    436     }
    437 
    438 }
    439