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 */ KeyAgreement(KeyAgreementSpi keyAgreeSpi, Provider provider, String algorithm)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 */ getAlgorithm()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 */ getProvider()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 */ getInstance(String algorithm)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 */ getInstance(String algorithm, String provider)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 */ getInstance(String algorithm, Provider provider)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 getKeyAgreement(String algorithm, Provider provider)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 tryAlgorithm(Key key, Provider provider, String algorithm)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 tryAlgorithmWithProvider(Key key, Provider.Service service)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 */ getSpi(Key key)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 */ getSpi()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 */ init(Key key)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 */ init(Key key, SecureRandom random)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 */ init(Key key, AlgorithmParameterSpec params)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 */ init(Key key, AlgorithmParameterSpec params, SecureRandom random)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 */ doPhase(Key key, boolean lastPhase)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 */ generateSecret()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 */ generateSecret(byte[] sharedSecret, int offset)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 */ generateSecret(String algorithm)399 public final SecretKey generateSecret(String algorithm) 400 throws IllegalStateException, NoSuchAlgorithmException, 401 InvalidKeyException { 402 return getSpi().engineGenerateSecret(algorithm); 403 } 404 405 } 406