All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.wildfly.security.key.KeyUtil Maven / Gradle / Ivy

Go to download

This artifact provides a single jar that contains all classes required to use remote EJB and JMS, including all dependencies. It is intended for use by those not using maven, maven users should just import the EJB and JMS BOM's instead (shaded JAR's cause lots of problems with maven, as it is very easy to inadvertently end up with different versions on classes on the class path).

The newest version!
/*
 * JBoss, Home of Professional Open Source.
 * Copyright 2016 Red Hat, Inc., and individual contributors
 * as indicated by the @author tags.
 *
 * Licensed 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 org.wildfly.security.key;

import static java.security.AccessController.doPrivileged;

import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.reflect.Method;
import java.lang.reflect.UndeclaredThrowableException;
import java.security.Key;
import java.security.PrivateKey;
import java.security.PrivilegedAction;
import java.security.interfaces.DSAKey;
import java.security.interfaces.DSAParams;
import java.security.interfaces.DSAPrivateKey;
import java.security.interfaces.ECKey;
import java.security.interfaces.ECPrivateKey;
import java.security.interfaces.RSAKey;
import java.security.interfaces.RSAMultiPrimePrivateCrtKey;
import java.security.interfaces.RSAPrivateKey;
import java.security.spec.AlgorithmParameterSpec;
import java.security.spec.DSAParameterSpec;
import java.security.spec.ECParameterSpec;
import java.util.Arrays;
import java.util.Objects;
import java.util.function.UnaryOperator;

import javax.crypto.SecretKey;
import javax.crypto.interfaces.DHKey;
import javax.crypto.interfaces.DHPrivateKey;
import javax.crypto.interfaces.PBEKey;
import javax.crypto.spec.DHParameterSpec;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import javax.security.auth.Destroyable;

import org.wildfly.common.Assert;
import org.wildfly.security.password.Password;
import org.wildfly.security.password.spec.DigestPasswordAlgorithmSpec;
import org.wildfly.security.password.spec.IteratedPasswordAlgorithmSpec;
import org.wildfly.security.password.spec.IteratedSaltedPasswordAlgorithmSpec;
import org.wildfly.security.password.spec.MaskedPasswordAlgorithmSpec;
import org.wildfly.security.password.spec.OneTimePasswordAlgorithmSpec;
import org.wildfly.security.password.spec.SaltedPasswordAlgorithmSpec;

/**
 * Key utility methods.
 *
 * @author David M. Lloyd
 */
public final class KeyUtil {
    private KeyUtil() {}

    /**
     * Cache so that we only have to figure out a cloning strategy for a given class one time.
     */
    private static final KeyClonerCreator CLONER_CREATOR = new KeyClonerCreator();

    /**
     * Attempt to acquire parameters from the given key.
     *
     * @param key the key (must not be {@code null})
     * @return the parameters, or {@code null} if no known parameters are available
     */
    public static AlgorithmParameterSpec getParameters(Key key) {
        return getParameters(key, AlgorithmParameterSpec.class);
    }

    /**
     * Attempt to acquire parameters of the given type from the given key.
     *
     * @param key the key (must not be {@code null})
     * @param paramSpecClass the parameter specification class (must not be {@code null})
     * @param 

the parameter specification type * @return the parameters, or {@code null} if no known parameters of the given type are available */ public static

P getParameters(Key key, Class

paramSpecClass) { if (key instanceof Password) { final AlgorithmParameterSpec parameterSpec = ((Password) key).getParameterSpec(); return paramSpecClass.isInstance(parameterSpec) ? paramSpecClass.cast(parameterSpec) : null; } else if (key instanceof RSAKey && paramSpecClass.isAssignableFrom(RSAParameterSpec.class)) { return paramSpecClass.cast(new RSAParameterSpec((RSAKey) key)); } else if (key instanceof DSAKey && paramSpecClass.isAssignableFrom(DSAParams.class)) { final DSAKey dsaKey = (DSAKey) key; final DSAParams dsaParams = dsaKey.getParams(); return paramSpecClass.cast(new DSAParameterSpec(dsaParams.getP(), dsaParams.getQ(), dsaParams.getG())); } else if (key instanceof ECKey && paramSpecClass.isAssignableFrom(ECParameterSpec.class)) { return paramSpecClass.cast(((ECKey) key).getParams()); } else if (key instanceof DHKey && paramSpecClass.isAssignableFrom(DHParameterSpec.class)) { return paramSpecClass.cast(((DHKey) key).getParams()); } else if (key instanceof PBEKey && paramSpecClass.isAssignableFrom(PBEParameterSpec.class)) { final PBEKey pbeKey = (PBEKey) key; final byte[] salt = pbeKey.getSalt(); // TODO: we miss the IV here return salt == null ? null : paramSpecClass.cast(new PBEParameterSpec(salt, pbeKey.getIterationCount())); } else { return null; } } /** * Determine if the given key has parameters which match the given parameters. * * @param key the key (must not be {@code null}) * @param parameters the parameters (must not be {@code null}) * @return {@code true} if the parameters match, {@code false} otherwise */ public static boolean hasParameters(final Key key, final AlgorithmParameterSpec parameters) { Assert.checkNotNullParam("key", key); Assert.checkNotNullParam("parameters", parameters); final AlgorithmParameterSpec keyParameters = getParameters(key, AlgorithmParameterSpec.class); return keyParameters != null && parametersEqual(keyParameters, parameters); } /** * Attempt to determine if two algorithm parameter specifications are equal. This method will return {@code true} * if the parameters are definitely the same, or {@code false} if they are not definitely equal or equivalency cannot be determined. * * @param p1 the first parameter specification (must not be {@code null}) * @param p2 the second parameter specification (must not be {@code null}) * @return {@code true} if the parameters are definitely equal, {@code false} otherwise */ public static boolean parametersEqual(final AlgorithmParameterSpec p1, final AlgorithmParameterSpec p2) { Assert.checkNotNullParam("p1", p1); Assert.checkNotNullParam("p2", p2); if (p1 instanceof DSAParams && p2 instanceof DSAParams) { final DSAParams dsa1 = (DSAParams) p1; final DSAParams dsa2 = (DSAParams) p2; return Objects.equals(dsa1.getG(), dsa2.getG()) && Objects.equals(dsa1.getP(), dsa2.getP()) && Objects.equals(dsa1.getQ(), dsa2.getQ()); } else if (p1 instanceof ECParameterSpec && p2 instanceof ECParameterSpec) { final ECParameterSpec ec1 = (ECParameterSpec) p1; final ECParameterSpec ec2 = (ECParameterSpec) p2; return ec1.getCofactor() == ec2.getCofactor() && Objects.equals(ec1.getCurve(), ec2.getCurve()) && Objects.equals(ec1.getGenerator(), ec2.getGenerator()) && Objects.equals(ec1.getOrder(), ec2.getOrder()); } else if (p1 instanceof DHParameterSpec && p2 instanceof DHParameterSpec) { final DHParameterSpec dh1 = (DHParameterSpec) p1; final DHParameterSpec dh2 = (DHParameterSpec) p2; return dh1.getL() == dh2.getL() && Objects.equals(dh1.getP(), dh2.getP()) && Objects.equals(dh1.getG(), dh2.getG()); } else if (p1 instanceof PBEParameterSpec && p2 instanceof PBEParameterSpec) { final PBEParameterSpec pbe1 = (PBEParameterSpec) p1; final PBEParameterSpec pbe2 = (PBEParameterSpec) p2; final AlgorithmParameterSpec param1 = pbe1.getParameterSpec(); final AlgorithmParameterSpec param2 = pbe2.getParameterSpec(); return pbe1.getIterationCount() == pbe2.getIterationCount() && Arrays.equals(pbe1.getSalt(), pbe2.getSalt()) && (param1 == null ? param2 == null : param2 != null && parametersEqual(param1, param2)); } else if (p1 instanceof IvParameterSpec && p2 instanceof IvParameterSpec) { final IvParameterSpec iv1 = (IvParameterSpec) p1; final IvParameterSpec iv2 = (IvParameterSpec) p2; return Arrays.equals(iv1.getIV(), iv2.getIV()); } else { // best effort return p1.equals(p2); } } /** * Attempt to get a stable hash code for the given parameter specification. If a stable hash code cannot be acquired, * the hash code of the class is returned, which results in correct (if non-optimal) behavior. If the parameter * is {@code null}, a hash code of zero is returned. * * @param param the parameter specification * @return the hash code */ public static int parametersHashCode(final AlgorithmParameterSpec param) { if (param == null) { return 0; } else if (param instanceof DSAParams) { final DSAParams dsaParams = (DSAParams) param; return Objects.hash(dsaParams.getG(), dsaParams.getP(), dsaParams.getQ()); } else if (param instanceof ECParameterSpec) { final ECParameterSpec ecSpec = (ECParameterSpec) param; return ecSpec.getCofactor() * 31 + Objects.hash(ecSpec.getCurve(), ecSpec.getGenerator(), ecSpec.getOrder()); } else if (param instanceof DHParameterSpec) { final DHParameterSpec dhSpec = (DHParameterSpec) param; return dhSpec.getL() * 31 + Objects.hash(dhSpec.getP(), dhSpec.getG()); } else if (param instanceof PBEParameterSpec) { final PBEParameterSpec pbeSpec = (PBEParameterSpec) param; final AlgorithmParameterSpec parameterSpec = pbeSpec.getParameterSpec(); return (pbeSpec.getIterationCount() * 31 + Arrays.hashCode(pbeSpec.getSalt())) * 31 + parametersHashCode(parameterSpec); } else if (param instanceof IvParameterSpec) { return Arrays.hashCode(((IvParameterSpec) param).getIV()); } else if (param instanceof RSAParameterSpec || param instanceof IteratedSaltedPasswordAlgorithmSpec || param instanceof IteratedPasswordAlgorithmSpec || param instanceof SaltedPasswordAlgorithmSpec || param instanceof DigestPasswordAlgorithmSpec || param instanceof MaskedPasswordAlgorithmSpec || param instanceof OneTimePasswordAlgorithmSpec ) { // our types all have proper hash codes return param.hashCode(); } else { return param.getClass().hashCode(); } } /** * Attempt to determine if the two keys have the same parameters. This method returns {@code true} if the keys * definitely have the same parameters, or {@code false} if they do not or if parameter equivalency cannot be determined. * * @param key1 the first key (must not be {@code null}) * @param key2 the second key (must not be {@code null}) * @return {@code true} if the parameters are definitely equal, {@code false} otherwise */ public static boolean hasSameParameters(final Key key1, final Key key2) { Assert.checkNotNullParam("key1", key1); Assert.checkNotNullParam("key2", key2); final AlgorithmParameterSpec param1 = getParameters(key1, AlgorithmParameterSpec.class); final AlgorithmParameterSpec param2 = getParameters(key2, AlgorithmParameterSpec.class); return param1 == null && param2 == null || param1 != null && param2 != null && parametersEqual(param1, param2); } /** * Attempt to create a safe clone of the given key object. * This algorithm first checks to see if the key's class implements {@link Destroyable}; if not, it is returned as-is. * Next it checks to see if the key has been destroyed; if so, it is returned as-is. * Next it determines if the key actually implements the {@link Destroyable} interface; if not, it is returned as-is. * Then it determines if there is a public {@code clone} method that returns a compatible type; if so, that method is used. * Then it determines if the key implements a known key interface; if so, a raw implementation of that interface is produced. * Last it checks to see if the key is some other unknown {@link SecretKey} type; if so, it captures its value using a {@link SecretKeySpec}. * If none of these checks succeed, an exception is thrown. * * @param expectType the expected result type (must not be {@code null}) * @param key the key object * @return the cloned key, or the original if the key type is not destroyable */ public static T cloneKey(Class expectType, T key) { Assert.checkNotNullParam("expectType", expectType); if (key instanceof Destroyable) { // medium path if (((Destroyable) key).isDestroyed()) { return expectType.cast(key); } else { return expectType.cast(CLONER_CREATOR.get(key.getClass()).apply(key)); } } else { // fast path return expectType.cast(key); } } private static class KeyClonerCreator extends ClassValue> { protected UnaryOperator computeValue(final Class type) { // slow path // check to see if it is *really* destroyable final Method method; try { method = type.getMethod("destroy"); } catch (NoSuchMethodException e) { // nope (because somehow there is no destroy method at all) return UnaryOperator.identity(); } if (method.getDeclaringClass() == Destroyable.class) { // also nope return UnaryOperator.identity(); } // now figure out how to actually transform it. // see if there's a clone method. UnaryOperator op = checkForCloneMethod(type, type); if (op != null) return op; // see if there's a copy constructor. op = checkForCopyCtor(type, type); if (op != null) return op; if (PrivateKey.class.isAssignableFrom(type)) { // some private key type... if (DSAPrivateKey.class.isAssignableFrom(type)) { op = checkForCloneMethod(type, DSAPrivateKey.class); if (op != null) return op; op = checkForCopyCtor(type, DSAPrivateKey.class); if (op != null) return op; return RawDSAPrivateKey::new; } else if (ECPrivateKey.class.isAssignableFrom(type)) { op = checkForCloneMethod(type, ECPrivateKey.class); if (op != null) return op; op = checkForCopyCtor(type, ECPrivateKey.class); if (op != null) return op; return RawECPrivateKey::new; } else if (RSAMultiPrimePrivateCrtKey.class.isAssignableFrom(type)) { op = checkForCloneMethod(type, RSAMultiPrimePrivateCrtKey.class); if (op != null) return op; op = checkForCopyCtor(type, RSAMultiPrimePrivateCrtKey.class); if (op != null) return op; return RawRSAMultiPrimePrivateCrtKey::new; } else if (RSAPrivateKey.class.isAssignableFrom(type)) { op = checkForCloneMethod(type, RSAPrivateKey.class); if (op != null) return op; op = checkForCopyCtor(type, RSAPrivateKey.class); if (op != null) return op; return RawRSAPrivateKey::new; } else if (DHPrivateKey.class.isAssignableFrom(type)) { op = checkForCloneMethod(type, DHPrivateKey.class); if (op != null) return op; op = checkForCopyCtor(type, DHPrivateKey.class); if (op != null) return op; return RawDHPrivateKey::new; } op = checkForCloneMethod(type, PrivateKey.class); if (op != null) return op; op = checkForCopyCtor(type, PrivateKey.class); if (op != null) return op; } else if (SecretKey.class.isAssignableFrom(type)) { // some secret key type... if (PBEKey.class.isAssignableFrom(type)) { op = checkForCloneMethod(type, PBEKey.class); if (op != null) return op; op = checkForCopyCtor(type, PBEKey.class); if (op != null) return op; return RawPBEKey::new; } else { op = checkForCloneMethod(type, SecretKey.class); if (op != null) return op; op = checkForCopyCtor(type, SecretKey.class); if (op != null) return op; // best guess return orig -> new SecretKeySpec(orig.getEncoded(), orig.getAlgorithm()); } } else { op = checkForCloneMethod(type, Key.class); if (op != null) return op; } return orig -> { throw Assert.unsupported(); }; } private UnaryOperator checkForCloneMethod(final Class declType, final Class returnType) { final MethodHandles.Lookup lookup = MethodHandles.lookup(); final MethodHandle handle = doPrivileged((PrivilegedAction) () -> { try { return lookup.findVirtual(declType, "clone", MethodType.methodType(returnType)); } catch (NoSuchMethodException | IllegalAccessException e) { return null; } }); return handle == null ? null : produceOp(handle); } private UnaryOperator checkForCopyCtor(final Class declType, final Class paramType) { final MethodHandles.Lookup lookup = MethodHandles.lookup(); final MethodHandle handle = doPrivileged((PrivilegedAction) () -> { try { return lookup.findConstructor(declType, MethodType.methodType(void.class, paramType)); } catch (NoSuchMethodException | IllegalAccessException e) { return null; } }); return handle == null ? null : produceOp(handle); } private static UnaryOperator produceOp(final MethodHandle handle) { return original -> { try { return (Key) handle.invoke(original); } catch (RuntimeException | Error e) { throw e; } catch (Throwable throwable) { throw new UndeclaredThrowableException(throwable); } }; } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy