BouncyKeyPair.java

/*******************************************************************************
 * GordianKnot: Security Suite
 * Copyright 2012,2025 Tony Washer
 *
 * 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 net.sourceforge.joceanus.gordianknot.impl.bc;

import net.sourceforge.joceanus.gordianknot.api.base.GordianException;
import net.sourceforge.joceanus.gordianknot.api.keypair.GordianKeyPair;
import net.sourceforge.joceanus.gordianknot.api.keypair.GordianKeyPairSpec;
import net.sourceforge.joceanus.gordianknot.api.keypair.GordianStateAwareKeyPair;
import net.sourceforge.joceanus.gordianknot.impl.core.exc.GordianDataException;
import net.sourceforge.joceanus.gordianknot.impl.core.keypair.GordianCoreKeyPair;
import net.sourceforge.joceanus.gordianknot.impl.core.keypair.GordianPrivateKey;
import net.sourceforge.joceanus.gordianknot.impl.core.keypair.GordianPrivateKey.GordianStateAwarePrivateKey;
import net.sourceforge.joceanus.gordianknot.impl.core.keypair.GordianPublicKey;
import org.bouncycastle.crypto.params.AsymmetricKeyParameter;

import java.util.Objects;

/**
 * BouncyCastle Asymmetric KeyPair.
 */
public class BouncyKeyPair
        extends GordianCoreKeyPair {
    /**
     * Constructor.
     * @param pPublic the public key
     */
    protected BouncyKeyPair(final BouncyPublicKey<?> pPublic) {
        this(pPublic, null);
    }

    /**
     * Constructor.
     * @param pPublic the public key
     * @param pPrivate the private key
     */
    protected BouncyKeyPair(final BouncyPublicKey<?> pPublic,
                            final BouncyPrivateKey<?> pPrivate) {
        super(pPublic, pPrivate);
    }

    @Override
    public BouncyPublicKey<?> getPublicKey() {
        return (BouncyPublicKey<?>) super.getPublicKey();
    }

    @Override
    public BouncyPrivateKey<?> getPrivateKey() {
        return (BouncyPrivateKey<?>) super.getPrivateKey();
    }

    @Override
    public BouncyKeyPair getPublicOnly() {
        return new BouncyKeyPair(getPublicKey());
    }

    /**
     * Check for bouncyKeyPair.
     * @param pKeyPair the keyPair to check
     * @throws GordianException on error
     */
    public static void checkKeyPair(final GordianKeyPair pKeyPair) throws GordianException {
        /* Check that it is a BouncyKeyPair */
        if (!(pKeyPair instanceof BouncyKeyPair)) {
            /* Reject keyPair */
            throw new GordianDataException("Invalid KeyPair");
        }
    }

    /**
     * Check for bouncyKeyPair.
     * @param pKeyPair the keyPair to check
     * @param pSpec the required keySpec
     * @throws GordianException on error
     */
    public static void checkKeyPair(final GordianKeyPair pKeyPair,
                                    final GordianKeyPairSpec pSpec) throws GordianException {
        /* Check the keyPair */
        checkKeyPair(pKeyPair);

        /* Check that it the correct key type */
        if (!pSpec.equals(pKeyPair.getKeyPairSpec())) {
            /* Reject keyPair */
            throw new GordianDataException("Invalid KeyPairType");
        }
    }

    /**
     * Bouncy PublicKey.
     * @param <T> parameter type
     */
    public abstract static class BouncyPublicKey<T extends AsymmetricKeyParameter>
            extends GordianPublicKey {
        /**
         * Public Key details.
         */
        private final T theKey;

        /**
         * Constructor.
         * @param pKeySpec the key spec
         * @param pKey the key
         */
        protected BouncyPublicKey(final GordianKeyPairSpec pKeySpec,
                                  final T pKey) {
            super(pKeySpec);
            theKey = pKey;
        }

        /**
         * Obtain the public key.
         * @return the key
         */
        public T getPublicKey() {
            return theKey;
        }

        /**
         * matchKey.
         * @param pThat the key to match
         * @return true/false
         */
        protected abstract boolean matchKey(AsymmetricKeyParameter pThat);

        @Override
        public boolean equals(final Object pThat) {
            /* Handle the trivial cases */
            if (pThat == this) {
                return true;
            }
            if (pThat == null) {
                return false;
            }

            /* Make sure that the object is the same class */
            if (!(pThat instanceof BouncyPublicKey)) {
                return false;
            }

            /* Access the target field */
            final BouncyPublicKey<?> myThat = (BouncyPublicKey<?>) pThat;

            /* Check differences */
            return getKeySpec().equals(myThat.getKeySpec())
                    && matchKey(myThat.getPublicKey());
        }

        @Override
        public int hashCode() {
            return Objects.hash(getKeySpec().hashCode(), theKey);
        }
    }

    /**
     * Bouncy PrivateKey.
     * @param <T> parameter type
     */
    public abstract static class BouncyPrivateKey<T extends AsymmetricKeyParameter>
            extends GordianPrivateKey {
        /**
         * Private Key details.
         */
        private final T theKey;

        /**
         * Constructor.
         * @param pKeySpec the key spec
         * @param pKey the key
         */
        protected BouncyPrivateKey(final GordianKeyPairSpec pKeySpec,
                                   final T pKey) {
            super(pKeySpec);
            theKey = pKey;
        }

        /**
         * Obtain the private key.
         * @return the key
         */
        public T getPrivateKey() {
            return theKey;
        }

        /**
         * matchKey.
         * @param pThat the key to match
         * @return true/false
         */
        protected abstract boolean matchKey(AsymmetricKeyParameter pThat);

        @Override
        public boolean equals(final Object pThat) {
            /* Handle the trivial cases */
            if (pThat == this) {
                return true;
            }
            if (pThat == null) {
                return false;
            }

            /* Make sure that the object is the same class */
            if (!(pThat instanceof BouncyPrivateKey)) {
                return false;
            }

            /* Access the target field */
            final BouncyPrivateKey<?> myThat = (BouncyPrivateKey<?>) pThat;

            /* Check differences */
            return getKeySpec().equals(myThat.getKeySpec())
                    && matchKey(myThat.getPrivateKey());
        }

        @Override
        public int hashCode() {
            return Objects.hash(getKeySpec(), theKey);
        }
    }

    /**
     * Bouncy StateAware PrivateKey.
     * @param <T> parameter type
     */
    public abstract static class BouncyStateAwarePrivateKey<T extends AsymmetricKeyParameter>
            extends BouncyPrivateKey<T>
            implements GordianStateAwarePrivateKey {
        /**
         * The private key.
         */
        private final T thePrivateKey;

        /**
         * Constructor.
         * @param pKeySpec the key spec
         * @param pKey the key
         */
        protected BouncyStateAwarePrivateKey(final GordianKeyPairSpec pKeySpec,
                                             final T pKey) {
            super(pKeySpec, pKey);
            thePrivateKey = pKey;
        }

        @Override
        public T getPrivateKey() {
            return thePrivateKey;
        }

        @Override
        public abstract BouncyStateAwarePrivateKey<T> getKeyShard(int pNumUsages);

        @Override
        public boolean equals(final Object pThat) {
            /* Handle the trivial cases */
            if (pThat == this) {
                return true;
            }
            if (pThat == null) {
                return false;
            }

            /* Make sure that the object is the same class */
            if (!(pThat instanceof BouncyStateAwarePrivateKey)) {
                return false;
            }

            /* Access the target field */
            final BouncyStateAwarePrivateKey<?> myThat = (BouncyStateAwarePrivateKey<?>) pThat;

            /* Check differences */
            return getKeySpec().equals(myThat.getKeySpec())
                    && matchKey(myThat.getPrivateKey());
        }

        @Override
        public int hashCode() {
            return Objects.hash(getKeySpec(), thePrivateKey);
        }
    }

    /**
     * Bouncy StateAware KeyPair.
      */
    public static class BouncyStateAwareKeyPair
            extends BouncyKeyPair
            implements GordianStateAwareKeyPair {
        /**
         * Constructor.
         * @param pPublic the public key
         * @param pPrivate the private key
         */
        BouncyStateAwareKeyPair(final BouncyPublicKey<?> pPublic,
                                final BouncyStateAwarePrivateKey<?> pPrivate) {
            super(pPublic, pPrivate);
        }

        @Override
        public BouncyStateAwarePrivateKey<?> getPrivateKey() {
            return (BouncyStateAwarePrivateKey<?>) super.getPrivateKey();
        }

        @Override
        public long getUsagesRemaining() {
            return getPrivateKey().getUsagesRemaining();
        }

        @Override
        public BouncyStateAwareKeyPair getKeyPairShard(final int pNumUsages) {
            return new BouncyStateAwareKeyPair(getPublicKey(), getPrivateKey().getKeyShard(pNumUsages));
        }
    }
}