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