/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package javax.crypto; import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.Key; import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; import java.security.Provider; import java.security.ProviderException; import java.security.SecureRandom; import java.security.Security; import java.security.spec.AlgorithmParameterSpec; import java.util.ArrayList; import org.apache.harmony.security.fortress.Engine; /** * This class provides the functionality for a key exchange protocol. This * enables two or more parties to agree on a secret key for symmetric * cryptography. */ public class KeyAgreement { // The service name. private static final String SERVICE = "KeyAgreement"; // Used to access common engine functionality private static final Engine ENGINE = new Engine(SERVICE); // Store SecureRandom private static final SecureRandom RANDOM = new SecureRandom(); // Store used provider private Provider provider; // Provider that was requested during creation. private final Provider specifiedProvider; // Store used spi implementation private KeyAgreementSpi spiImpl; // Store used algorithm name private final String algorithm; /** * Lock held while the SPI is initializing. */ private final Object initLock = new Object(); /** * Creates a new {@code KeyAgreement} instance. * * @param keyAgreeSpi * the SPI delegate. * @param provider * the provider providing this KeyAgreement. * @param algorithm * the name of the key agreement algorithm. */ protected KeyAgreement(KeyAgreementSpi keyAgreeSpi, Provider provider, String algorithm) { this.spiImpl = keyAgreeSpi; this.specifiedProvider = provider; this.algorithm = algorithm; } /** * Returns the name of the key agreement algorithm. * * @return the name of the key agreement algorithm. */ public final String getAlgorithm() { return algorithm; } /** * Returns the provider for this {@code KeyAgreement} instance. * * @return the provider for this {@code KeyAgreement} instance. */ public final Provider getProvider() { getSpi(); return provider; } /** * Creates a new {@code KeyAgreement} for the specified algorithm. * * @param algorithm * the name of the key agreement algorithm to create. * @return a key agreement for the specified algorithm. * @throws NoSuchAlgorithmException * if no installed provider can provide the requested algorithm. * @throws NullPointerException * if the specified algorithm is {@code null}. */ public static final KeyAgreement getInstance(String algorithm) throws NoSuchAlgorithmException { return getKeyAgreement(algorithm, null); } /** * Creates a new {@code KeyAgreement} for the specified algorithm from the * specified provider. * * @param algorithm * the name of the key agreement algorithm to create. * @param provider * the name of the provider that provides the requested * algorithm. * @return a key agreement for the specified algorithm from the specified * provider. * @throws NoSuchAlgorithmException * if the specified provider cannot provide the requested * algorithm. * @throws NoSuchProviderException * if the specified provider does not exist. * @throws IllegalArgumentException * if the specified provider name is {@code null} or empty. */ public static final KeyAgreement getInstance(String algorithm, String provider) throws NoSuchAlgorithmException, NoSuchProviderException { if (provider == null || provider.isEmpty()) { throw new IllegalArgumentException("Provider is null or empty"); } Provider impProvider = Security.getProvider(provider); if (impProvider == null) { throw new NoSuchProviderException(provider); } return getKeyAgreement(algorithm, impProvider); } /** * Create a new {@code KeyAgreement} for the specified algorithm from the * specified provider. The {@code provider} supplied does not have to be * registered. * * @param algorithm * the name of the key agreement algorithm to create. * @param provider * the provider that provides the requested algorithm. * @return a key agreement for the specified algorithm from the specified * provider. * @throws NoSuchAlgorithmException * if the specified provider cannot provide the requested * algorithm. * @throws IllegalArgumentException * if the specified provider is {@code null}. * @throws NullPointerException * if the specified algorithm name is {@code null}. */ public static final KeyAgreement getInstance(String algorithm, Provider provider) throws NoSuchAlgorithmException { if (provider == null) { throw new IllegalArgumentException("provider == null"); } return getKeyAgreement(algorithm, provider); } private static KeyAgreement getKeyAgreement(String algorithm, Provider provider) throws NoSuchAlgorithmException { if (algorithm == null) { throw new NullPointerException("algorithm == null"); } if (tryAlgorithm(null, provider, algorithm) == null) { if (provider == null) { throw new NoSuchAlgorithmException("No provider found for " + algorithm); } else { throw new NoSuchAlgorithmException("Provider " + provider.getName() + " does not provide " + algorithm); } } return new KeyAgreement(null, provider, algorithm); } private static Engine.SpiAndProvider tryAlgorithm(Key key, Provider provider, String algorithm) { if (provider != null) { Provider.Service service = provider.getService(SERVICE, algorithm); if (service == null) { return null; } return tryAlgorithmWithProvider(key, service); } ArrayList services = ENGINE.getServices(algorithm); if (services == null) { return null; } for (Provider.Service service : services) { Engine.SpiAndProvider sap = tryAlgorithmWithProvider(key, service); if (sap != null) { return sap; } } return null; } private static Engine.SpiAndProvider tryAlgorithmWithProvider(Key key, Provider.Service service) { try { if (key != null && !service.supportsParameter(key)) { return null; } Engine.SpiAndProvider sap = ENGINE.getInstance(service, null); if (sap.spi == null || sap.provider == null) { return null; } if (!(sap.spi instanceof KeyAgreementSpi)) { return null; } return sap; } catch (NoSuchAlgorithmException ignored) { } return null; } /** * Makes sure a KeyAgreementSpi that matches this type is selected. */ private KeyAgreementSpi getSpi(Key key) { synchronized (initLock) { if (spiImpl != null && key == null) { return spiImpl; } final Engine.SpiAndProvider sap = tryAlgorithm(key, specifiedProvider, algorithm); if (sap == null) { throw new ProviderException("No provider for " + getAlgorithm()); } spiImpl = (KeyAgreementSpi) sap.spi; provider = sap.provider; return spiImpl; } } /** * Convenience call when the Key is not available. */ private KeyAgreementSpi getSpi() { return getSpi(null); } /** * Initializes this {@code KeyAgreement} with the specified key. * * @param key the key to initialize this key agreement. * @throws InvalidKeyException if the specified key cannot be used to * initialize this key agreement. */ public final void init(Key key) throws InvalidKeyException { getSpi(key).engineInit(key, RANDOM);//new SecureRandom()); } /** * Initializes this {@code KeyAgreement} with the specified key and the * specified randomness source. * * @param key * the key to initialize this key agreement. * @param random * the source for any randomness needed. * @throws InvalidKeyException * if the specified key cannot be used to initialize this key * agreement. */ public final void init(Key key, SecureRandom random) throws InvalidKeyException { getSpi(key).engineInit(key, random); } /** * Initializes this {@code KeyAgreement} with the specified key and the * algorithm parameters. * * @param key * the key to initialize this key agreement. * @param params * the parameters for this key agreement algorithm. * @throws InvalidKeyException * if the specified key cannot be used to initialize this key * agreement. * @throws InvalidAlgorithmParameterException * if the specified parameters are invalid for this key * agreement algorithm. */ public final void init(Key key, AlgorithmParameterSpec params) throws InvalidKeyException, InvalidAlgorithmParameterException { getSpi(key).engineInit(key, params, RANDOM);//new SecureRandom()); } /** * Initializes this {@code KeyAgreement} with the specified key, algorithm * parameters and randomness source. * * @param key * the key to initialize this key agreement. * @param params * the parameters for this key agreement algorithm. * @param random * the source for any randomness needed. * @throws InvalidKeyException * if the specified key cannot be used to initialize this key * agreement. * @throws InvalidAlgorithmParameterException * if the specified parameters are invalid for this key * agreement algorithm. */ public final void init(Key key, AlgorithmParameterSpec params, SecureRandom random) throws InvalidKeyException, InvalidAlgorithmParameterException { getSpi(key).engineInit(key, params, random); } /** * Does the next (or the last) phase of the key agreement, using the * specified key. * * @param key * the key received from the other party for this phase. * @param lastPhase * set to {@code true} if this is the last phase of this key * agreement. * @return the intermediate key from this phase or {@code null} if there is * no intermediate key for this phase. * @throws InvalidKeyException * if the specified key cannot be used in this key agreement or * this phase, * @throws IllegalStateException * if this instance has not been initialized. */ public final Key doPhase(Key key, boolean lastPhase) throws InvalidKeyException, IllegalStateException { return getSpi().engineDoPhase(key, lastPhase); } /** * Generates the shared secret. * * @return the generated shared secret. * @throws IllegalStateException * if this key agreement is not complete. */ public final byte[] generateSecret() throws IllegalStateException { return getSpi().engineGenerateSecret(); } /** * Generates the shared secret and stores it into the buffer {@code * sharedSecred} at {@code offset}. * * @param sharedSecret * the buffer to store the shared secret. * @param offset * the offset in the buffer. * @return the number of bytes stored in the buffer. * @throws IllegalStateException * if this key agreement is not complete. * @throws ShortBufferException * if the specified buffer is too small for the shared secret. */ public final int generateSecret(byte[] sharedSecret, int offset) throws IllegalStateException, ShortBufferException { return getSpi().engineGenerateSecret(sharedSecret, offset); } /** * Generates the shared secret. * * @param algorithm * the algorithm to for the {@code SecretKey} * @return the shared secret as a {@code SecretKey} of the specified * algorithm. * @throws IllegalStateException * if this key agreement is not complete. * @throws NoSuchAlgorithmException * if the specified algorithm for the secret key does not * exists. * @throws InvalidKeyException * if a {@code SecretKey} with the specified algorithm cannot be * created using the generated shared secret. */ public final SecretKey generateSecret(String algorithm) throws IllegalStateException, NoSuchAlgorithmException, InvalidKeyException { return getSpi().engineGenerateSecret(algorithm); } }