| /* | |
| * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved. | |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | |
| * | |
| * This code is free software; you can redistribute it and/or modify it | |
| * under the terms of the GNU General Public License version 2 only, as | |
| * published by the Free Software Foundation. Oracle designates this | |
| * particular file as subject to the "Classpath" exception as provided | |
| * by Oracle in the LICENSE file that accompanied this code. | |
| * | |
| * This code is distributed in the hope that it will be useful, but WITHOUT | |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
| * version 2 for more details (a copy is included in the LICENSE file that | |
| * accompanied this code). | |
| * | |
| * You should have received a copy of the GNU General Public License version | |
| * 2 along with this work; if not, write to the Free Software Foundation, | |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | |
| * | |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | |
| * or visit www.oracle.com if you need additional information or have any | |
| * questions. | |
| */ | |
| package javax.crypto; | |
| import sun.security.jca.GetInstance; | |
| import sun.security.jca.GetInstance.Instance; | |
| import sun.security.util.Debug; | |
| import java.security.InvalidAlgorithmParameterException; | |
| import java.security.NoSuchAlgorithmException; | |
| import java.security.NoSuchProviderException; | |
| import java.security.Security; | |
| import java.security.Provider; | |
| import java.security.Provider.Service; | |
| import java.security.spec.AlgorithmParameterSpec; | |
| import java.util.Iterator; | |
| import java.util.Objects; | |
| /** | |
| * This class provides the functionality of a Key Derivation Function (KDF), | |
| * which is a cryptographic algorithm for deriving additional keys from input | |
| * keying material (IKM) and (optionally) other data. | |
| * <p> | |
| * {@code KDF} objects are instantiated with the {@code getInstance} family of | |
| * methods. | |
| * <p> | |
| * The class has two derive methods, {@code deriveKey} and {@code deriveData}. | |
| * The {@code deriveKey} method accepts an algorithm name and returns a | |
| * {@code SecretKey} object with the specified algorithm. The {@code deriveData} | |
| * method returns a byte array of raw data. | |
| * <p> | |
| * API Usage Example: | |
| * {@snippet lang = java: | |
| * KDF kdfHkdf = KDF.getInstance("HKDF-SHA256"); | |
| * | |
| * AlgorithmParameterSpec derivationSpec = | |
| * HKDFParameterSpec.ofExtract() | |
| * .addIKM(ikm) | |
| * .addSalt(salt).thenExpand(info, 32); | |
| * | |
| * SecretKey sKey = kdfHkdf.deriveKey("AES", derivationSpec); | |
| *} | |
| * <br> | |
| * <h2><a id="ConcurrentAccess">Concurrent Access</a></h2> | |
| * Unless otherwise documented by an implementation, the methods defined in this | |
| * class are not thread-safe. Multiple threads that need to access a single | |
| * object concurrently should synchronize amongst themselves and provide the | |
| * necessary locking. Multiple threads each manipulating separate objects need | |
| * not synchronize. | |
| * <br> | |
| * <h2><a id="DelayedProviderSelection">Delayed Provider Selection</a></h2> | |
| * If a provider is not specified when calling one of the {@code getInstance} | |
| * methods, the implementation delays the selection of the provider until the | |
| * {@code deriveKey} or {@code deriveData} method is called. This is called | |
| * <i>delayed provider selection</i>. The primary reason this is done is to | |
| * ensure that the selected provider can handle the key material that is passed | |
| * to those methods - for example, the key material may reside on a hardware | |
| * device that only a specific {@code KDF} provider can utilize. The {@code | |
| * getInstance} method returns a {@code KDF} object as long as there exists | |
| * at least one registered security provider that implements the algorithm | |
| * and supports the optional parameters. The delayed provider selection | |
| * process traverses the list of registered security providers, starting with | |
| * the most preferred {@code Provider}. The first provider that supports the | |
| * specified algorithm, optional parameters, and key material is selected. | |
| * <p> | |
| * If the {@code getProviderName} or {@code getParameters} method is called | |
| * before the {@code deriveKey} or {@code deriveData} methods, the first | |
| * provider supporting the {@code KDF} algorithm and optional | |
| * {@code KDFParameters} is chosen. This provider may not support the key | |
| * material that is subsequently passed to the {@code deriveKey} or | |
| * {@code deriveData} methods. Therefore, it is recommended not to call the | |
| * {@code getProviderName} or {@code getParameters} methods until after a key | |
| * derivation operation. Once a provider is selected, it cannot be changed. | |
| * | |
| * @see KDFParameters | |
| * @see SecretKey | |
| * @since 25 | |
| */ | |
| public final class KDF { | |
| private static final Debug pdebug = Debug.getInstance("provider", | |
| "Provider"); | |
| private static final boolean skipDebug = Debug.isOn("engine=") | |
| && !Debug.isOn("kdf"); | |
| private record Delegate(KDFSpi spi, Provider provider) {} | |
| //guarded by 'lock' | |
| private Delegate theOne; | |
| //guarded by 'lock' | |
| private final Delegate candidate; | |
| // The name of the KDF algorithm. | |
| private final String algorithm; | |
| // Additional KDF configuration parameters | |
| private final KDFParameters kdfParameters; | |
| // remaining services to try in provider selection | |
| // null once provider is selected | |
| private final Iterator<Service> serviceIterator; | |
| // This lock provides mutual exclusion, preventing multiple threads from | |
| // concurrently initializing the same instance (delayed provider selection) | |
| // in a way which would corrupt the internal state. | |
| private final Object lock = new Object(); | |
| // Instantiates a {@code KDF} object. This constructor is called when a | |
| // provider is supplied to {@code getInstance}. | |
| // | |
| // @param delegate the delegate | |
| // @param algorithm the algorithm | |
| // @param kdfParameters the parameters | |
| private KDF(Delegate delegate, String algorithm) { | |
| this.theOne = delegate; | |
| this.algorithm = algorithm; | |
| // note that the parameters are being passed to the impl in getInstance | |
| this.kdfParameters = null; | |
| this.candidate = null; | |
| serviceIterator = null; | |
| } | |
| // Instantiates a {@code KDF} object. This constructor is called when a | |
| // provider is not supplied to {@code getInstance}. | |
| // | |
| // @param firstPairOfSpiAndProv the delegate | |
| // @param t the service iterator | |
| // @param algorithm the algorithm | |
| // @param kdfParameters the algorithm parameters | |
| private KDF(Delegate firstPairOfSpiAndProv, Iterator<Service> t, | |
| String algorithm, | |
| KDFParameters kdfParameters) { | |
| this.candidate = firstPairOfSpiAndProv; | |
| serviceIterator = t; | |
| this.algorithm = algorithm; | |
| this.kdfParameters = kdfParameters; | |
| } | |
| /** | |
| * Returns the algorithm name of this {@code KDF} object. | |
| * | |
| * @return the algorithm name of this {@code KDF} object | |
| */ | |
| public String getAlgorithm() { | |
| return this.algorithm; | |
| } | |
| /** | |
| * Returns the name of the provider. | |
| * | |
| * @return the name of the provider | |
| * | |
| * @see <a href="#DelayedProviderSelection">Delayed Provider | |
| * Selection</a> | |
| */ | |
| public String getProviderName() { | |
| useFirstSpi(); | |
| return theOne.provider().getName(); | |
| } | |
| /** | |
| * Returns the {@code KDFParameters} used with this {@code KDF} object. | |
| * <p> | |
| * The returned parameters may be the same that were used to initialize | |
| * this {@code KDF} object, or may contain additional default or | |
| * random parameter values used by the underlying KDF algorithm. | |
| * If the required parameters were not supplied and can be generated by | |
| * the {@code KDF} object, the generated parameters are returned; | |
| * otherwise {@code null} is returned. | |
| * | |
| * @return the parameters used with this {@code KDF} object, or | |
| * {@code null} | |
| * | |
| * @see <a href="#DelayedProviderSelection">Delayed Provider | |
| * Selection</a> | |
| */ | |
| public KDFParameters getParameters() { | |
| useFirstSpi(); | |
| return theOne.spi().engineGetParameters(); | |
| } | |
| /** | |
| * Returns a {@code KDF} object that implements the specified algorithm. | |
| * | |
| * @implNote The JDK Reference Implementation additionally uses the | |
| * {@code jdk.security.provider.preferred} | |
| * {@link Security#getProperty(String) Security} property to | |
| * determine the preferred provider order for the specified | |
| * algorithm. This may be different than the order of providers | |
| * returned by | |
| * {@link Security#getProviders() Security.getProviders()}. | |
| * | |
| * @param algorithm | |
| * the key derivation algorithm to use. See the {@code KDF} section | |
| * in the <a href="{@docRoot}/../specs/security/standard-names.html#kdf-algorithms"> | |
| * Java Security Standard Algorithm Names Specification</a> for | |
| * information about standard KDF algorithm names. | |
| * | |
| * @spec security/standard-names.html Java Security Standard Algorithm Names | |
| * @return a {@code KDF} object | |
| * | |
| * @throws NoSuchAlgorithmException | |
| * if no {@code Provider} supports a {@code KDF} implementation for | |
| * the specified algorithm | |
| * @throws NullPointerException | |
| * if {@code algorithm} is {@code null} | |
| * @see <a href="#DelayedProviderSelection">Delayed Provider | |
| * Selection</a> | |
| */ | |
| public static KDF getInstance(String algorithm) | |
| throws NoSuchAlgorithmException { | |
| Objects.requireNonNull(algorithm, "algorithm must not be null"); | |
| try { | |
| return getInstance(algorithm, (KDFParameters) null); | |
| } catch (InvalidAlgorithmParameterException e) { | |
| throw new NoSuchAlgorithmException( | |
| "No implementation found using null KDFParameters", e); | |
| } | |
| } | |
| /** | |
| * Returns a {@code KDF} object that implements the specified algorithm from | |
| * the specified security provider. The specified provider must be | |
| * registered in the security provider list. | |
| * | |
| * @param algorithm | |
| * the key derivation algorithm to use. See the {@code KDF} section | |
| * in the <a href="{@docRoot}/../specs/security/standard-names.html#kdf-algorithms"> | |
| * Java Security Standard Algorithm Names Specification</a> for | |
| * information about standard KDF algorithm names. | |
| * @param provider | |
| * the provider to use for this key derivation | |
| * | |
| * @spec security/standard-names.html Java Security Standard Algorithm Names | |
| * @return a {@code KDF} object | |
| * | |
| * @throws NoSuchAlgorithmException | |
| * if the specified provider does not support the specified | |
| * {@code KDF} algorithm | |
| * @throws NoSuchProviderException | |
| * if the specified provider is not registered in the security | |
| * provider list | |
| * @throws NullPointerException | |
| * if {@code algorithm} or {@code provider} is {@code null} | |
| */ | |
| public static KDF getInstance(String algorithm, String provider) | |
| throws NoSuchAlgorithmException, NoSuchProviderException { | |
| Objects.requireNonNull(algorithm, "algorithm must not be null"); | |
| Objects.requireNonNull(provider, "provider must not be null"); | |
| try { | |
| return getInstance(algorithm, null, provider); | |
| } catch (InvalidAlgorithmParameterException e) { | |
| throw new NoSuchAlgorithmException( | |
| "No implementation found using null KDFParameters", e); | |
| } | |
| } | |
| /** | |
| * Returns a {@code KDF} object that implements the specified algorithm from | |
| * the specified security provider. | |
| * | |
| * @param algorithm | |
| * the key derivation algorithm to use. See the {@code KDF} section | |
| * in the <a href="{@docRoot}/../specs/security/standard-names.html#kdf-algorithms"> | |
| * Java Security Standard Algorithm Names Specification</a> for | |
| * information about standard KDF algorithm names. | |
| * @param provider | |
| * the provider to use for this key derivation | |
| * | |
| * @spec security/standard-names.html Java Security Standard Algorithm Names | |
| * @return a {@code KDF} object | |
| * | |
| * @throws NoSuchAlgorithmException | |
| * if the specified provider does not support the specified | |
| * {@code KDF} algorithm | |
| * @throws NullPointerException | |
| * if {@code algorithm} or {@code provider} is {@code null} | |
| */ | |
| public static KDF getInstance(String algorithm, Provider provider) | |
| throws NoSuchAlgorithmException { | |
| Objects.requireNonNull(algorithm, "algorithm must not be null"); | |
| Objects.requireNonNull(provider, "provider must not be null"); | |
| try { | |
| return getInstance(algorithm, null, provider); | |
| } catch (InvalidAlgorithmParameterException e) { | |
| throw new NoSuchAlgorithmException( | |
| "No implementation found using null KDFParameters", e); | |
| } | |
| } | |
| /** | |
| * Returns a {@code KDF} object that implements the specified algorithm and | |
| * is initialized with the specified parameters. | |
| * | |
| * @implNote The JDK Reference Implementation additionally uses the | |
| * {@code jdk.security.provider.preferred} | |
| * {@link Security#getProperty(String) Security} property to | |
| * determine the preferred provider order for the specified | |
| * algorithm. This may be different than the order of providers | |
| * returned by | |
| * {@link Security#getProviders() Security.getProviders()}. | |
| * | |
| * @param algorithm | |
| * the key derivation algorithm to use. See the {@code KDF} section | |
| * in the <a href="{@docRoot}/../specs/security/standard-names.html#kdf-algorithms"> | |
| * Java Security Standard Algorithm Names Specification</a> for | |
| * information about standard KDF algorithm names. | |
| * @param kdfParameters | |
| * the {@code KDFParameters} used to configure the derivation | |
| * algorithm or {@code null} if no parameters are provided | |
| * | |
| * @spec security/standard-names.html Java Security Standard Algorithm Names | |
| * @return a {@code KDF} object | |
| * | |
| * @throws NoSuchAlgorithmException | |
| * if no {@code Provider} supports a {@code KDF} implementation for | |
| * the specified algorithm | |
| * @throws InvalidAlgorithmParameterException | |
| * if at least one {@code Provider} supports a {@code KDF} | |
| * implementation for the specified algorithm but none of them | |
| * support the specified parameters | |
| * @throws NullPointerException | |
| * if {@code algorithm} is {@code null} | |
| * @see <a href="#DelayedProviderSelection">Delayed Provider | |
| * Selection</a> | |
| */ | |
| public static KDF getInstance(String algorithm, | |
| KDFParameters kdfParameters) | |
| throws NoSuchAlgorithmException, InvalidAlgorithmParameterException { | |
| Objects.requireNonNull(algorithm, "algorithm must not be null"); | |
| // make sure there is at least one service from a signed provider | |
| Iterator<Service> t = GetInstance.getServices("KDF", algorithm); | |
| Delegate d = getNext(t, kdfParameters); | |
| return (t.hasNext() ? | |
| new KDF(d, t, algorithm, kdfParameters) : | |
| new KDF(d, algorithm)); | |
| } | |
| /** | |
| * Returns a {@code KDF} object that implements the specified algorithm from | |
| * the specified provider and is initialized with the specified parameters. | |
| * The specified provider must be registered in the security provider list. | |
| * | |
| * @param algorithm | |
| * the key derivation algorithm to use. See the {@code KDF} section | |
| * in the <a href="{@docRoot}/../specs/security/standard-names.html#kdf-algorithms"> | |
| * Java Security Standard Algorithm Names Specification</a> for | |
| * information about standard KDF algorithm names. | |
| * @param kdfParameters | |
| * the {@code KDFParameters} used to configure the derivation | |
| * algorithm or {@code null} if no parameters are provided | |
| * @param provider | |
| * the provider to use for this key derivation | |
| * | |
| * @spec security/standard-names.html Java Security Standard Algorithm Names | |
| * @return a {@code KDF} object | |
| * | |
| * @throws NoSuchAlgorithmException | |
| * if the specified provider does not support the specified | |
| * {@code KDF} algorithm | |
| * @throws NoSuchProviderException | |
| * if the specified provider is not registered in the security | |
| * provider list | |
| * @throws InvalidAlgorithmParameterException | |
| * if the specified provider supports the specified {@code KDF} | |
| * algorithm but does not support the specified parameters | |
| * @throws NullPointerException | |
| * if {@code algorithm} or {@code provider} is {@code null} | |
| */ | |
| public static KDF getInstance(String algorithm, | |
| KDFParameters kdfParameters, | |
| String provider) | |
| throws NoSuchAlgorithmException, NoSuchProviderException, | |
| InvalidAlgorithmParameterException { | |
| Objects.requireNonNull(algorithm, "algorithm must not be null"); | |
| Objects.requireNonNull(provider, "provider must not be null"); | |
| try { | |
| Instance instance = GetInstance.getInstance("KDF", KDFSpi.class, | |
| algorithm, | |
| kdfParameters, | |
| provider); | |
| if (!JceSecurity.canUseProvider(instance.provider)) { | |
| String msg = "JCE cannot authenticate the provider " | |
| + instance.provider.getName(); | |
| throw new NoSuchProviderException(msg); | |
| } | |
| return new KDF(new Delegate((KDFSpi) instance.impl, | |
| instance.provider), algorithm); | |
| } catch (NoSuchAlgorithmException nsae) { | |
| return handleException(nsae); | |
| } | |
| } | |
| /** | |
| * Returns a {@code KDF} object that implements the specified algorithm from | |
| * the specified provider and is initialized with the specified parameters. | |
| * | |
| * @param algorithm | |
| * the key derivation algorithm to use. See the {@code KDF} section | |
| * in the <a href="{@docRoot}/../specs/security/standard-names.html#kdf-algorithms"> | |
| * Java Security Standard Algorithm Names Specification</a> for | |
| * information about standard KDF algorithm names. | |
| * @param kdfParameters | |
| * the {@code KDFParameters} used to configure the derivation | |
| * algorithm or {@code null} if no parameters are provided | |
| * @param provider | |
| * the provider to use for this key derivation | |
| * | |
| * @spec security/standard-names.html Java Security Standard Algorithm Names | |
| * @return a {@code KDF} object | |
| * | |
| * @throws NoSuchAlgorithmException | |
| * if the specified provider does not support the specified | |
| * {@code KDF} algorithm | |
| * @throws InvalidAlgorithmParameterException | |
| * if the specified provider supports the specified {@code KDF} | |
| * algorithm but does not support the specified parameters | |
| * @throws NullPointerException | |
| * if {@code algorithm} or {@code provider} is {@code null} | |
| */ | |
| public static KDF getInstance(String algorithm, | |
| KDFParameters kdfParameters, | |
| Provider provider) | |
| throws NoSuchAlgorithmException, | |
| InvalidAlgorithmParameterException { | |
| Objects.requireNonNull(algorithm, "algorithm must not be null"); | |
| Objects.requireNonNull(provider, "provider must not be null"); | |
| try { | |
| Instance instance = GetInstance.getInstance("KDF", KDFSpi.class, | |
| algorithm, | |
| kdfParameters, | |
| provider); | |
| if (!JceSecurity.canUseProvider(instance.provider)) { | |
| String msg = "JCE cannot authenticate the provider " | |
| + instance.provider.getName(); | |
| throw new SecurityException(msg); | |
| } | |
| return new KDF(new Delegate((KDFSpi) instance.impl, | |
| instance.provider), algorithm); | |
| } catch (NoSuchAlgorithmException nsae) { | |
| return handleException(nsae); | |
| } | |
| } | |
| private static KDF handleException(NoSuchAlgorithmException e) | |
| throws NoSuchAlgorithmException, | |
| InvalidAlgorithmParameterException { | |
| Throwable cause = e.getCause(); | |
| if (cause instanceof InvalidAlgorithmParameterException iape) { | |
| throw iape; | |
| } | |
| throw e; | |
| } | |
| // Rethrows the IAPE thrown by an implementation, adding an explanation | |
| // for the situation in which it fails. | |
| private void rethrow(InvalidAlgorithmParameterException e) | |
| throws InvalidAlgorithmParameterException { | |
| var source = serviceIterator == null | |
| ? "specified" : "previously selected"; | |
| if (!skipDebug && pdebug != null) { | |
| pdebug.println("A " + this.getAlgorithm() | |
| + " derivation cannot be performed " | |
| + "using the supplied derivation " | |
| + "inputs with the " + source + " " | |
| + theOne.provider().getName() | |
| + " provider."); | |
| } | |
| throw new InvalidAlgorithmParameterException( | |
| "The " + source + " " + theOne.provider.getName() | |
| + " provider does not support this input", e); | |
| } | |
| /** | |
| * Derives a key, returned as a {@code SecretKey} object. | |
| * | |
| * @param alg | |
| * the algorithm of the resultant {@code SecretKey} object. | |
| * See the SecretKey Algorithms section in the | |
| * <a href="{@docRoot}/../specs/security/standard-names.html#secretkey-algorithms"> | |
| * Java Security Standard Algorithm Names Specification</a> | |
| * for information about standard secret key algorithm names. | |
| * @param derivationSpec | |
| * the object describing the inputs to the derivation function | |
| * | |
| * @return the derived key | |
| * | |
| * @throws InvalidAlgorithmParameterException | |
| * if the information contained within the {@code derivationSpec} is | |
| * invalid or if the combination of {@code alg} and the | |
| * {@code derivationSpec} results in something invalid | |
| * @throws NoSuchAlgorithmException | |
| * if {@code alg} is empty or invalid | |
| * @throws NullPointerException | |
| * if {@code alg} or {@code derivationSpec} is null | |
| * | |
| * @see <a href="#DelayedProviderSelection">Delayed Provider | |
| * Selection</a> | |
| * @spec security/standard-names.html Java Security Standard Algorithm Names | |
| * | |
| */ | |
| public SecretKey deriveKey(String alg, | |
| AlgorithmParameterSpec derivationSpec) | |
| throws InvalidAlgorithmParameterException, | |
| NoSuchAlgorithmException { | |
| if (alg == null) { | |
| throw new NullPointerException( | |
| "the algorithm for the SecretKey return value must not be" | |
| + " null"); | |
| } | |
| if (alg.isEmpty()) { | |
| throw new NoSuchAlgorithmException( | |
| "the algorithm for the SecretKey return value must not be " | |
| + "empty"); | |
| } | |
| Objects.requireNonNull(derivationSpec); | |
| if (checkSpiNonNull(theOne)) { | |
| try { | |
| return theOne.spi().engineDeriveKey(alg, derivationSpec); | |
| } catch (InvalidAlgorithmParameterException e) { | |
| rethrow(e); | |
| return null; // will not be called | |
| } | |
| } else { | |
| return (SecretKey) chooseProvider(alg, derivationSpec); | |
| } | |
| } | |
| /** | |
| * Derives a key, returns raw data as a byte array. | |
| * | |
| * @param derivationSpec | |
| * the object describing the inputs to the derivation function | |
| * | |
| * @return the derived key in its raw bytes | |
| * | |
| * @throws InvalidAlgorithmParameterException | |
| * if the information contained within the {@code derivationSpec} is | |
| * invalid | |
| * @throws UnsupportedOperationException | |
| * if the derived keying material is not extractable | |
| * @throws NullPointerException | |
| * if {@code derivationSpec} is null | |
| * | |
| * @see <a href="#DelayedProviderSelection">Delayed Provider | |
| * Selection</a> | |
| * | |
| */ | |
| public byte[] deriveData(AlgorithmParameterSpec derivationSpec) | |
| throws InvalidAlgorithmParameterException { | |
| Objects.requireNonNull(derivationSpec); | |
| if (checkSpiNonNull(theOne)) { | |
| try { | |
| return theOne.spi().engineDeriveData(derivationSpec); | |
| } catch (InvalidAlgorithmParameterException e) { | |
| rethrow(e); | |
| return null; // will not be called | |
| } | |
| } else { | |
| try { | |
| return (byte[]) chooseProvider(null, derivationSpec); | |
| } catch (NoSuchAlgorithmException e) { | |
| // this will never be thrown in the deriveData case | |
| throw new AssertionError(e); | |
| } | |
| } | |
| } | |
| /** | |
| * Use the firstSpi as the chosen KDFSpi and set the fields accordingly | |
| */ | |
| private void useFirstSpi() { | |
| if (checkSpiNonNull(theOne)) return; | |
| synchronized (lock) { | |
| if (!checkSpiNonNull(theOne)) { | |
| theOne = candidate; | |
| } | |
| } | |
| } | |
| /** | |
| * Selects the provider which supports the passed {@code algorithm} and | |
| * {@code derivationSpec} values, and assigns the global spi and provider | |
| * variables if they have not been assigned yet. | |
| * <p> | |
| * If the spi has already been set, it will just return the result. | |
| */ | |
| private Object chooseProvider(String algorithm, | |
| AlgorithmParameterSpec derivationSpec) | |
| throws InvalidAlgorithmParameterException, | |
| NoSuchAlgorithmException { | |
| boolean isDeriveData = (algorithm == null); | |
| synchronized (lock) { | |
| if (checkSpiNonNull(theOne)) { | |
| return (isDeriveData) ? | |
| theOne.spi().engineDeriveData(derivationSpec) : | |
| theOne.spi().engineDeriveKey(algorithm, derivationSpec); | |
| } | |
| Exception lastException = null; | |
| if (!checkSpiNonNull(candidate)) { | |
| throw new AssertionError("Unexpected Error: candidate is null!"); | |
| } | |
| Delegate currOne = candidate; | |
| try { | |
| while (true) { | |
| try { | |
| Object result = (isDeriveData) ? | |
| currOne.spi().engineDeriveData(derivationSpec) : | |
| currOne.spi().engineDeriveKey(algorithm, | |
| derivationSpec); | |
| // found a working KDFSpi | |
| this.theOne = currOne; | |
| if (!skipDebug && pdebug != null) { | |
| pdebug.println("The provider " | |
| + currOne.provider().getName() | |
| + " is selected"); | |
| } | |
| return result; | |
| } catch (Exception e) { | |
| if (!skipDebug && pdebug != null) { | |
| pdebug.println("A " + this.getAlgorithm() | |
| + " derivation cannot be performed " | |
| + "using the supplied derivation " | |
| + "inputs, using " | |
| + currOne.provider().getName() | |
| + ". Another Provider will be " | |
| + "attempted."); | |
| e.printStackTrace(pdebug.getPrintStream()); | |
| } | |
| if (lastException == null) { | |
| lastException = e; | |
| } | |
| // try next one if available | |
| assert serviceIterator != null : "serviceIterator was null"; | |
| currOne = getNext(serviceIterator, kdfParameters); | |
| } | |
| } | |
| } catch (InvalidAlgorithmParameterException e) { | |
| throw e; // getNext reached end and have seen IAPE | |
| } catch (NoSuchAlgorithmException e) { | |
| if (!skipDebug && pdebug != null) { | |
| pdebug.println( | |
| "All available Providers have been examined " | |
| + "without a match for performing the " | |
| + this.getAlgorithm() | |
| + " derivation using the supplied derivation " | |
| + "inputs. Therefore, the caught " | |
| + "NoSuchAlgorithmException will be logged, and " | |
| + "an InvalidAlgorithmParameterException will " | |
| + "then be thrown with the last known Exception."); | |
| e.printStackTrace(pdebug.getPrintStream()); | |
| } | |
| // getNext reached end without finding an implementation | |
| throw new InvalidAlgorithmParameterException( | |
| "No provider supports this input", lastException); | |
| } | |
| } | |
| } | |
| private static Delegate getNext(Iterator<Service> serviceIter, | |
| KDFParameters kdfParameters) | |
| throws NoSuchAlgorithmException, InvalidAlgorithmParameterException { | |
| // fetch next one if available | |
| boolean hasOne = false; | |
| while (serviceIter.hasNext()) { | |
| Service s = serviceIter.next(); | |
| Provider prov = s.getProvider(); | |
| if (!JceSecurity.canUseProvider(prov)) { | |
| // continue to next iteration | |
| continue; | |
| } | |
| hasOne = true; | |
| try { | |
| Object obj = s.newInstance(kdfParameters); | |
| if (!(obj instanceof KDFSpi)) { | |
| if (!skipDebug && pdebug != null) { | |
| pdebug.println( | |
| "obj was not an instance of KDFSpi (should not " | |
| + "happen)"); | |
| } | |
| continue; | |
| } | |
| return new Delegate((KDFSpi) obj, prov); | |
| } catch (NoSuchAlgorithmException nsae) { | |
| // continue to the next provider | |
| if (!skipDebug && pdebug != null) { | |
| pdebug.println("A derivation cannot be performed " | |
| + "using the supplied KDFParameters, using " | |
| + prov.getName() | |
| + ". Another Provider will be attempted."); | |
| nsae.printStackTrace(pdebug.getPrintStream()); | |
| } | |
| } | |
| } | |
| if (!skipDebug && pdebug != null) { | |
| pdebug.println( | |
| "No provider can be found that supports the " | |
| + "specified algorithm and parameters"); | |
| } | |
| if (hasOne) throw new InvalidAlgorithmParameterException( | |
| "The KDFParameters supplied could not be used in combination " | |
| + "with the supplied algorithm for the selected Provider"); | |
| else throw new NoSuchAlgorithmException( | |
| "No available provider supports the specified algorithm"); | |
| } | |
| private static boolean checkSpiNonNull(Delegate d) { | |
| // d.spi() cannot be null if d != null | |
| return (d != null); | |
| } | |
| } |