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         if (tryAlgorithm(null, provider, algorithm) == null) {
    182             if (provider == null) {
    183                 throw new NoSuchAlgorithmException("No provider found for " + algorithm);
    184             } else {
    185                 throw new NoSuchAlgorithmException("Provider " + provider.getName()
    186                         + " does not provide " + algorithm);
    187             }
    188         }
    189         return new KeyAgreement(null, provider, algorithm);
    190     }
    191 
    192     private static Engine.SpiAndProvider tryAlgorithm(Key key, Provider provider, String algorithm) {
    193         if (provider != null) {
    194             Provider.Service service = provider.getService(SERVICE, algorithm);
    195             if (service == null) {
    196                 return null;
    197             }
    198             return tryAlgorithmWithProvider(key, service);
    199         }
    200         ArrayList<Provider.Service> services = ENGINE.getServices(algorithm);
    201         if (services == null) {
    202             return null;
    203         }
    204         for (Provider.Service service : services) {
    205             Engine.SpiAndProvider sap = tryAlgorithmWithProvider(key, service);
    206             if (sap != null) {
    207                 return sap;
    208             }
    209         }
    210         return null;
    211     }
    212 
    213     private static Engine.SpiAndProvider tryAlgorithmWithProvider(Key key, Provider.Service service) {
    214         try {
    215             if (key != null && !service.supportsParameter(key)) {
    216                 return null;
    217             }
    218 
    219             Engine.SpiAndProvider sap = ENGINE.getInstance(service, null);
    220             if (sap.spi == null || sap.provider == null) {
    221                 return null;
    222             }
    223             if (!(sap.spi instanceof KeyAgreementSpi)) {
    224                 return null;
    225             }
    226             return sap;
    227         } catch (NoSuchAlgorithmException ignored) {
    228         }
    229         return null;
    230     }
    231 
    232     /**
    233      * Makes sure a KeyAgreementSpi that matches this type is selected.
    234      */
    235     private KeyAgreementSpi getSpi(Key key) {
    236         synchronized (initLock) {
    237             if (spiImpl != null && key == null) {
    238                 return spiImpl;
    239             }
    240 
    241             final Engine.SpiAndProvider sap = tryAlgorithm(key, specifiedProvider, algorithm);
    242             if (sap == null) {
    243                 throw new ProviderException("No provider for " + getAlgorithm());
    244             }
    245 
    246             spiImpl = (KeyAgreementSpi) sap.spi;
    247             provider = sap.provider;
    248 
    249             return spiImpl;
    250         }
    251     }
    252 
    253     /**
    254      * Convenience call when the Key is not available.
    255      */
    256     private KeyAgreementSpi getSpi() {
    257         return getSpi(null);
    258     }
    259 
    260     /**
    261      * Initializes this {@code KeyAgreement} with the specified key.
    262      *
    263      * @param key the key to initialize this key agreement.
    264      * @throws InvalidKeyException if the specified key cannot be used to
    265      *             initialize this key agreement.
    266      */
    267     public final void init(Key key) throws InvalidKeyException {
    268         getSpi(key).engineInit(key, RANDOM);//new SecureRandom());
    269     }
    270 
    271     /**
    272      * Initializes this {@code KeyAgreement} with the specified key and the
    273      * specified randomness source.
    274      *
    275      * @param key
    276      *            the key to initialize this key agreement.
    277      * @param random
    278      *            the source for any randomness needed.
    279      * @throws InvalidKeyException
    280      *             if the specified key cannot be used to initialize this key
    281      *             agreement.
    282      */
    283     public final void init(Key key, SecureRandom random)
    284             throws InvalidKeyException {
    285         getSpi(key).engineInit(key, random);
    286     }
    287 
    288     /**
    289      * Initializes this {@code KeyAgreement} with the specified key and the
    290      * algorithm parameters.
    291      *
    292      * @param key
    293      *            the key to initialize this key agreement.
    294      * @param params
    295      *            the parameters for this key agreement algorithm.
    296      * @throws InvalidKeyException
    297      *             if the specified key cannot be used to initialize this key
    298      *             agreement.
    299      * @throws InvalidAlgorithmParameterException
    300      *             if the specified parameters are invalid for this key
    301      *             agreement algorithm.
    302      */
    303     public final void init(Key key, AlgorithmParameterSpec params)
    304             throws InvalidKeyException, InvalidAlgorithmParameterException {
    305         getSpi(key).engineInit(key, params, RANDOM);//new SecureRandom());
    306     }
    307 
    308     /**
    309      * Initializes this {@code KeyAgreement} with the specified key, algorithm
    310      * parameters and randomness source.
    311      *
    312      * @param key
    313      *            the key to initialize this key agreement.
    314      * @param params
    315      *            the parameters for this key agreement algorithm.
    316      * @param random
    317      *            the source for any randomness needed.
    318      * @throws InvalidKeyException
    319      *             if the specified key cannot be used to initialize this key
    320      *             agreement.
    321      * @throws InvalidAlgorithmParameterException
    322      *             if the specified parameters are invalid for this key
    323      *             agreement algorithm.
    324      */
    325     public final void init(Key key, AlgorithmParameterSpec params,
    326             SecureRandom random) throws InvalidKeyException,
    327             InvalidAlgorithmParameterException {
    328         getSpi(key).engineInit(key, params, random);
    329     }
    330 
    331     /**
    332      * Does the next (or the last) phase of the key agreement, using the
    333      * specified key.
    334      *
    335      * @param key
    336      *            the key received from the other party for this phase.
    337      * @param lastPhase
    338      *            set to {@code true} if this is the last phase of this key
    339      *            agreement.
    340      * @return the intermediate key from this phase or {@code null} if there is
    341      *         no intermediate key for this phase.
    342      * @throws InvalidKeyException
    343      *             if the specified key cannot be used in this key agreement or
    344      *             this phase,
    345      * @throws IllegalStateException
    346      *             if this instance has not been initialized.
    347      */
    348     public final Key doPhase(Key key, boolean lastPhase)
    349             throws InvalidKeyException, IllegalStateException {
    350         return getSpi().engineDoPhase(key, lastPhase);
    351     }
    352 
    353     /**
    354      * Generates the shared secret.
    355      *
    356      * @return the generated shared secret.
    357      * @throws IllegalStateException
    358      *             if this key agreement is not complete.
    359      */
    360     public final byte[] generateSecret() throws IllegalStateException {
    361         return getSpi().engineGenerateSecret();
    362     }
    363 
    364     /**
    365      * Generates the shared secret and stores it into the buffer {@code
    366      * sharedSecred} at {@code offset}.
    367      *
    368      * @param sharedSecret
    369      *            the buffer to store the shared secret.
    370      * @param offset
    371      *            the offset in the buffer.
    372      * @return the number of bytes stored in the buffer.
    373      * @throws IllegalStateException
    374      *             if this key agreement is not complete.
    375      * @throws ShortBufferException
    376      *             if the specified buffer is too small for the shared secret.
    377      */
    378     public final int generateSecret(byte[] sharedSecret, int offset)
    379             throws IllegalStateException, ShortBufferException {
    380         return getSpi().engineGenerateSecret(sharedSecret, offset);
    381     }
    382 
    383     /**
    384      * Generates the shared secret.
    385      *
    386      * @param algorithm
    387      *            the algorithm to for the {@code SecretKey}
    388      * @return the shared secret as a {@code SecretKey} of the specified
    389      *         algorithm.
    390      * @throws IllegalStateException
    391      *             if this key agreement is not complete.
    392      * @throws NoSuchAlgorithmException
    393      *             if the specified algorithm for the secret key does not
    394      *             exists.
    395      * @throws InvalidKeyException
    396      *             if a {@code SecretKey} with the specified algorithm cannot be
    397      *             created using the generated shared secret.
    398      */
    399     public final SecretKey generateSecret(String algorithm)
    400             throws IllegalStateException, NoSuchAlgorithmException,
    401             InvalidKeyException {
    402         return getSpi().engineGenerateSecret(algorithm);
    403     }
    404 
    405 }
    406