JcaKeyPairGenerator.java

/*
 * GordianKnot: Security Suite
 * Copyright 2012-2026. 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 io.github.tonywasher.joceanus.gordianknot.impl.jca;

import io.github.tonywasher.joceanus.gordianknot.api.base.GordianException;
import io.github.tonywasher.joceanus.gordianknot.api.keypair.GordianDHGroup;
import io.github.tonywasher.joceanus.gordianknot.api.keypair.GordianDSAKeyType;
import io.github.tonywasher.joceanus.gordianknot.api.keypair.GordianKeyPair;
import io.github.tonywasher.joceanus.gordianknot.api.keypair.GordianKeyPairSpec;
import io.github.tonywasher.joceanus.gordianknot.api.keypair.GordianLMSKeySpec;
import io.github.tonywasher.joceanus.gordianknot.api.keypair.GordianLMSKeySpec.GordianHSSKeySpec;
import io.github.tonywasher.joceanus.gordianknot.api.keypair.GordianRSAModulus;
import io.github.tonywasher.joceanus.gordianknot.api.keypair.GordianXMSSKeySpec;
import io.github.tonywasher.joceanus.gordianknot.api.keypair.GordianXMSSKeySpec.GordianXMSSDigestType;
import io.github.tonywasher.joceanus.gordianknot.impl.core.base.GordianBaseFactory;
import io.github.tonywasher.joceanus.gordianknot.impl.core.exc.GordianCryptoException;
import io.github.tonywasher.joceanus.gordianknot.impl.core.exc.GordianLogicException;
import io.github.tonywasher.joceanus.gordianknot.impl.core.keypair.GordianCoreKeyPairGenerator;
import io.github.tonywasher.joceanus.gordianknot.impl.core.keypair.GordianKeyPairValidity;
import io.github.tonywasher.joceanus.gordianknot.impl.jca.JcaKeyPair.JcaDHPrivateKey;
import io.github.tonywasher.joceanus.gordianknot.impl.jca.JcaKeyPair.JcaDHPublicKey;
import io.github.tonywasher.joceanus.gordianknot.impl.jca.JcaKeyPair.JcaPrivateKey;
import io.github.tonywasher.joceanus.gordianknot.impl.jca.JcaKeyPair.JcaPublicKey;
import io.github.tonywasher.joceanus.gordianknot.impl.jca.JcaKeyPair.JcaStateAwareKeyPair;
import io.github.tonywasher.joceanus.gordianknot.impl.jca.JcaKeyPair.JcaStateAwarePrivateKey;
import org.bouncycastle.crypto.params.DHParameters;
import org.bouncycastle.jcajce.provider.asymmetric.dh.BCDHPrivateKey;
import org.bouncycastle.jcajce.provider.asymmetric.dh.BCDHPublicKey;
import org.bouncycastle.jcajce.spec.DHDomainParameterSpec;
import org.bouncycastle.jcajce.spec.EdDSAParameterSpec;
import org.bouncycastle.jcajce.spec.MLDSAParameterSpec;
import org.bouncycastle.jcajce.spec.MLKEMParameterSpec;
import org.bouncycastle.jcajce.spec.SLHDSAParameterSpec;
import org.bouncycastle.jcajce.spec.XDHParameterSpec;
import org.bouncycastle.jce.spec.ElGamalParameterSpec;
import org.bouncycastle.pqc.crypto.lms.LMSParameters;
import org.bouncycastle.pqc.jcajce.spec.BIKEParameterSpec;
import org.bouncycastle.pqc.jcajce.spec.CMCEParameterSpec;
import org.bouncycastle.pqc.jcajce.spec.FalconParameterSpec;
import org.bouncycastle.pqc.jcajce.spec.FrodoParameterSpec;
import org.bouncycastle.pqc.jcajce.spec.HQCParameterSpec;
import org.bouncycastle.pqc.jcajce.spec.LMSHSSKeyGenParameterSpec;
import org.bouncycastle.pqc.jcajce.spec.LMSKeyGenParameterSpec;
import org.bouncycastle.pqc.jcajce.spec.MayoParameterSpec;
import org.bouncycastle.pqc.jcajce.spec.NTRULPRimeParameterSpec;
import org.bouncycastle.pqc.jcajce.spec.NTRUParameterSpec;
import org.bouncycastle.pqc.jcajce.spec.PicnicParameterSpec;
import org.bouncycastle.pqc.jcajce.spec.SABERParameterSpec;
import org.bouncycastle.pqc.jcajce.spec.SNTRUPrimeParameterSpec;
import org.bouncycastle.pqc.jcajce.spec.SnovaParameterSpec;
import org.bouncycastle.pqc.jcajce.spec.XMSSMTParameterSpec;
import org.bouncycastle.pqc.jcajce.spec.XMSSParameterSpec;

import java.security.InvalidAlgorithmParameterException;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.AlgorithmParameterSpec;
import java.security.spec.ECGenParameterSpec;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Arrays;

/**
 * Jca KeyPair generator.
 */
public abstract class JcaKeyPairGenerator
        extends GordianCoreKeyPairGenerator {
    /**
     * Parse error.
     */
    private static final String PARSE_ERROR = "Failed to parse encoding";

    /**
     * Factory.
     */
    private KeyFactory theFactory;

    /**
     * Constructor.
     *
     * @param pFactory the Security Factory
     * @param pKeySpec the keySpec
     */
    JcaKeyPairGenerator(final GordianBaseFactory pFactory,
                        final GordianKeyPairSpec pKeySpec) {
        super(pFactory, pKeySpec);
    }

    /**
     * Obtain Set the key factory.
     *
     * @return the keyFactory
     */
    KeyFactory getKeyFactory() {
        return theFactory;
    }

    /**
     * Set the key factory.
     *
     * @param pFactory the keyFactory
     */
    void setKeyFactory(final KeyFactory pFactory) {
        theFactory = pFactory;
    }

    @Override
    public PKCS8EncodedKeySpec getPKCS8Encoding(final GordianKeyPair pKeyPair) throws GordianException {
        /* Check the keyPair */
        JcaKeyPair.checkKeyPair(pKeyPair);

        /* derive the encoding */
        final JcaPrivateKey myPrivateKey = (JcaPrivateKey) getPrivateKey(pKeyPair);
        return new PKCS8EncodedKeySpec(myPrivateKey.getPrivateKey().getEncoded());
    }

    @Override
    public X509EncodedKeySpec getX509Encoding(final GordianKeyPair pKeyPair) throws GordianException {
        /* Check the keyPair */
        JcaKeyPair.checkKeyPair(pKeyPair);

        /* derive the encoding */
        final JcaPublicKey myPublicKey = (JcaPublicKey) getPublicKey(pKeyPair);
        return new X509EncodedKeySpec(myPublicKey.getPublicKey().getEncoded());
    }

    @Override
    public JcaKeyPair deriveKeyPair(final X509EncodedKeySpec pPublicKey,
                                    final PKCS8EncodedKeySpec pPrivateKey) throws GordianException {
        /* Protect against exceptions */
        try {
            /* Check the keySpec */
            checkKeySpec(pPrivateKey);

            /* derive the keyPair */
            final JcaPublicKey myPublic = derivePublicKey(pPublicKey);
            final JcaPrivateKey myPrivate = createPrivate(theFactory.generatePrivate(pPrivateKey));
            final JcaKeyPair myPair = new JcaKeyPair(myPublic, myPrivate);

            /* Check that we have a matching pair */
            GordianKeyPairValidity.checkValidity(getFactory(), myPair);

            /* Return the keyPair */
            return myPair;

        } catch (InvalidKeySpecException e) {
            throw new GordianCryptoException(PARSE_ERROR, e);
        }
    }

    /**
     * Create private key.
     *
     * @param pPrivateKey the private key
     * @return the private key
     */
    protected JcaPrivateKey createPrivate(final PrivateKey pPrivateKey) {
        return new JcaPrivateKey(getKeySpec(), pPrivateKey);
    }

    /**
     * Create public key.
     *
     * @param pPublicKey the public key
     * @return the public key
     */
    protected JcaPublicKey createPublic(final PublicKey pPublicKey) {
        return new JcaPublicKey(getKeySpec(), pPublicKey);
    }

    @Override
    public JcaKeyPair derivePublicOnlyKeyPair(final X509EncodedKeySpec pPublicKey) throws GordianException {
        final JcaPublicKey myPublic = derivePublicKey(pPublicKey);
        return new JcaKeyPair(myPublic);
    }

    /**
     * Derive the public key.
     *
     * @param pEncodedKey the encoded public key
     * @return the public key
     * @throws GordianException on error
     */
    protected JcaPublicKey derivePublicKey(final X509EncodedKeySpec pEncodedKey) throws GordianException {
        /* Protect against exceptions */
        try {
            /* Check the keySpec */
            checkKeySpec(pEncodedKey);

            /* derive the key */
            return createPublic(theFactory.generatePublic(pEncodedKey));

        } catch (InvalidKeySpecException e) {
            throw new GordianCryptoException(PARSE_ERROR, e);
        }
    }

    /**
     * Jca RSA KeyPair generator.
     */
    public static class JcaRSAKeyPairGenerator
            extends JcaKeyPairGenerator {
        /**
         * RSA algorithm.
         */
        private static final String RSA_ALGO = "RSA";

        /**
         * Generator.
         */
        private final KeyPairGenerator theGenerator;

        /**
         * Constructor.
         *
         * @param pFactory the Security Factory
         * @param pKeySpec the keySpec
         * @throws GordianException on error
         */
        JcaRSAKeyPairGenerator(final GordianBaseFactory pFactory,
                               final GordianKeyPairSpec pKeySpec) throws GordianException {
            /* initialize underlying class */
            super(pFactory, pKeySpec);

            /* Create and initialize the generator */
            theGenerator = getJavaKeyPairGenerator(RSA_ALGO, false);
            theGenerator.initialize(pKeySpec.getRSAModulus().getLength(), getRandom());

            /* Create the factory */
            setKeyFactory(getJavaKeyFactory(RSA_ALGO, false));
        }

        @Override
        public JcaKeyPair generateKeyPair() {
            /* Generate and return the keyPair */
            final KeyPair myPair = theGenerator.generateKeyPair();
            final JcaPublicKey myPublic = createPublic(myPair.getPublic());
            final JcaPrivateKey myPrivate = createPrivate(myPair.getPrivate());
            return new JcaKeyPair(myPublic, myPrivate);
        }
    }

    /**
     * Jca ElGamal KeyPair generator.
     */
    public static class JcaElGamalKeyPairGenerator
            extends JcaKeyPairGenerator {
        /**
         * RSA algorithm.
         */
        private static final String ELGAMAL_ALGO = "ELGAMAL";

        /**
         * Generator.
         */
        private final KeyPairGenerator theGenerator;

        /**
         * Constructor.
         *
         * @param pFactory the Security Factory
         * @param pKeySpec the keySpec
         * @throws GordianException on error
         */
        JcaElGamalKeyPairGenerator(final GordianBaseFactory pFactory,
                                   final GordianKeyPairSpec pKeySpec) throws GordianException {
            /* initialize underlying class */
            super(pFactory, pKeySpec);

            /* Protect against exceptions */
            try {
                /* Create the parameter generator */
                final GordianDHGroup myGroup = pKeySpec.getDHGroup();
                final DHParameters myParms = myGroup.getParameters();
                final ElGamalParameterSpec mySpec = new ElGamalParameterSpec(myParms.getP(), myParms.getQ());

                /* Create and initialize the generator */
                theGenerator = getJavaKeyPairGenerator(ELGAMAL_ALGO, false);
                theGenerator.initialize(mySpec, getRandom());

                /* Create the factory */
                setKeyFactory(getJavaKeyFactory(ELGAMAL_ALGO, false));

            } catch (InvalidAlgorithmParameterException e) {
                throw new GordianCryptoException("Failed to create ElGamalGenerator", e);
            }
        }

        @Override
        public JcaKeyPair generateKeyPair() {
            /* Generate and return the keyPair */
            final KeyPair myPair = theGenerator.generateKeyPair();
            final JcaPublicKey myPublic = createPublic(myPair.getPublic());
            final JcaPrivateKey myPrivate = createPrivate(myPair.getPrivate());
            return new JcaKeyPair(myPublic, myPrivate);
        }
    }

    /**
     * Jca Elliptic KeyPair generator.
     */
    public static class JcaECKeyPairGenerator
            extends JcaKeyPairGenerator {
        /**
         * Generator.
         */
        private final KeyPairGenerator theGenerator;

        /**
         * Constructor.
         *
         * @param pFactory the Security Factory
         * @param pKeySpec the keySpec
         * @throws GordianException on error
         */
        JcaECKeyPairGenerator(final GordianBaseFactory pFactory,
                              final GordianKeyPairSpec pKeySpec) throws GordianException {
            /* initialize underlying class */
            super(pFactory, pKeySpec);

            /* Protect against exceptions */
            try {
                /* Create and initialize the generator */
                final String myAlgo = getAlgorithm();
                theGenerator = getJavaKeyPairGenerator(myAlgo, false);
                final ECGenParameterSpec myParms = new ECGenParameterSpec(pKeySpec.getElliptic().getCurveName());
                theGenerator.initialize(myParms, getRandom());

                /* Create the factory */
                setKeyFactory(getJavaKeyFactory(myAlgo, false));

            } catch (InvalidAlgorithmParameterException e) {
                throw new GordianCryptoException("Failed to create ECgenerator for:  " + pKeySpec, e);
            }
        }

        @Override
        public JcaKeyPair generateKeyPair() {
            /* Generate and return the keyPair */
            final KeyPair myPair = theGenerator.generateKeyPair();
            final JcaPublicKey myPublic = createPublic(myPair.getPublic());
            final JcaPrivateKey myPrivate = createPrivate(myPair.getPrivate());
            return new JcaKeyPair(myPublic, myPrivate);
        }

        /**
         * Obtain algorithm for keySpec.
         *
         * @return the algorithm
         */
        private String getAlgorithm() {
            switch (this.getKeySpec().getKeyPairType()) {
                case DSTU4145:
                    return "DSTU4145";
                case GOST2012:
                    return "ECGOST3410-2012";
                default:
                    return "EC";
            }
        }
    }

    /**
     * Jca DSA KeyPair generator.
     */
    public static class JcaDSAKeyPairGenerator
            extends JcaKeyPairGenerator {
        /**
         * DSA algorithm.
         */
        private static final String DSA_ALGO = "DSA";

        /**
         * Generator.
         */
        private final KeyPairGenerator theGenerator;

        /**
         * Constructor.
         *
         * @param pFactory the Security Factory
         * @param pKeySpec the keySpec
         * @throws GordianException on error
         */
        JcaDSAKeyPairGenerator(final GordianBaseFactory pFactory,
                               final GordianKeyPairSpec pKeySpec) throws GordianException {
            /* initialize underlying class */
            super(pFactory, pKeySpec);

            /* Create and initialize the generator */
            final GordianDSAKeyType myKeyType = pKeySpec.getDSAKeyType();
            theGenerator = getJavaKeyPairGenerator(DSA_ALGO, false);
            theGenerator.initialize(myKeyType.getKeySize(), getRandom());

            /* Create the factory */
            setKeyFactory(getJavaKeyFactory(DSA_ALGO, false));
        }

        @Override
        public JcaKeyPair generateKeyPair() {
            /* Generate and return the keyPair */
            final KeyPair myPair = theGenerator.generateKeyPair();
            final JcaPublicKey myPublic = createPublic(myPair.getPublic());
            final JcaPrivateKey myPrivate = createPrivate(myPair.getPrivate());
            return new JcaKeyPair(myPublic, myPrivate);
        }
    }

    /**
     * Jca DiffieHellman KeyPair generator.
     */
    public static class JcaDHKeyPairGenerator
            extends JcaKeyPairGenerator {
        /**
         * DH algorithm.
         */
        private static final String DH_ALGO = "DH";

        /**
         * Generator.
         */
        private final KeyPairGenerator theGenerator;

        /**
         * Constructor.
         *
         * @param pFactory the Security Factory
         * @param pKeySpec the keySpec
         * @throws GordianException on error
         */
        JcaDHKeyPairGenerator(final GordianBaseFactory pFactory,
                              final GordianKeyPairSpec pKeySpec) throws GordianException {
            /* initialize underlying class */
            super(pFactory, pKeySpec);

            /* Protect against exceptions */
            try {
                /* Create the parameter generator */
                final GordianDHGroup myGroup = pKeySpec.getDHGroup();
                final DHParameters myParms = myGroup.getParameters();
                final DHDomainParameterSpec mySpec = new DHDomainParameterSpec(myParms);

                /* Create and initialize the generator */
                theGenerator = getJavaKeyPairGenerator(DH_ALGO, false);
                theGenerator.initialize(mySpec, getRandom());

                /* Create the factory */
                setKeyFactory(getJavaKeyFactory(DH_ALGO, false));

            } catch (InvalidAlgorithmParameterException e) {
                throw new GordianCryptoException("Failed to create DHgenerator", e);
            }
        }

        @Override
        public JcaKeyPair generateKeyPair() {
            /* Generate and return the keyPair */
            final KeyPair myPair = theGenerator.generateKeyPair();
            final JcaPublicKey myPublic = createPublic(myPair.getPublic());
            final JcaPrivateKey myPrivate = createPrivate(myPair.getPrivate());
            return new JcaKeyPair(myPublic, myPrivate);
        }

        @Override
        protected JcaPrivateKey createPrivate(final PrivateKey pPrivateKey) {
            return new JcaDHPrivateKey(getKeySpec(), (BCDHPrivateKey) pPrivateKey);
        }

        @Override
        protected JcaPublicKey createPublic(final PublicKey pPublicKey) {
            return new JcaDHPublicKey(getKeySpec(), (BCDHPublicKey) pPublicKey);
        }
    }

    /**
     * Jca SLHDSA KeyPair generator.
     */
    public static class JcaSLHDSAKeyPairGenerator
            extends JcaKeyPairGenerator {
        /**
         * SLHDSA algorithm.
         */
        private static final String SLHDSA_ALGO = "SLH-DSA";

        /**
         * HASH indication.
         */
        private static final String SLHDSA_HASH = "HASH-" + SLHDSA_ALGO;

        /**
         * Generator.
         */
        private final KeyPairGenerator theGenerator;

        /**
         * Constructor.
         *
         * @param pFactory the Security Factory
         * @param pKeySpec the keySpec
         * @throws GordianException on error
         */
        JcaSLHDSAKeyPairGenerator(final GordianBaseFactory pFactory,
                                  final GordianKeyPairSpec pKeySpec) throws GordianException {
            /* initialize underlying class */
            super(pFactory, pKeySpec);

            /* Protect against exceptions */
            try {
                /* Determine algorithm */
                final String myAlgo = pKeySpec.getSLHDSAKeySpec().isHash() ? SLHDSA_HASH : SLHDSA_ALGO;

                /* Create and initialize the generator */
                theGenerator = getJavaKeyPairGenerator(myAlgo, false);
                final SLHDSAParameterSpec myParms = pKeySpec.getSLHDSAKeySpec().getParameterSpec();
                theGenerator.initialize(myParms, getRandom());

                /* Create the factory */
                setKeyFactory(getJavaKeyFactory(myAlgo, false));

            } catch (InvalidAlgorithmParameterException e) {
                throw new GordianCryptoException("Failed to create SLHDSAgenerator", e);
            }
        }

        @Override
        public JcaKeyPair generateKeyPair() {
            /* Generate and return the keyPair */
            final KeyPair myPair = theGenerator.generateKeyPair();
            final JcaPublicKey myPublic = createPublic(myPair.getPublic());
            final JcaPrivateKey myPrivate = createPrivate(myPair.getPrivate());
            return new JcaKeyPair(myPublic, myPrivate);
        }
    }

    /**
     * Jca CMCE KeyPair generator.
     */
    public static class JcaCMCEKeyPairGenerator
            extends JcaKeyPairGenerator {
        /**
         * FRODO algorithm.
         */
        private static final String CMCE_ALGO = "CMCE";

        /**
         * Generator.
         */
        private final KeyPairGenerator theGenerator;

        /**
         * Constructor.
         *
         * @param pFactory the Security Factory
         * @param pKeySpec the keySpec
         * @throws GordianException on error
         */
        JcaCMCEKeyPairGenerator(final GordianBaseFactory pFactory,
                                final GordianKeyPairSpec pKeySpec) throws GordianException {
            /* initialize underlying class */
            super(pFactory, pKeySpec);

            /* Protect against exceptions */
            try {
                /* Create and initialize the generator */
                theGenerator = getJavaKeyPairGenerator(CMCE_ALGO, true);
                final CMCEParameterSpec myParms = pKeySpec.getCMCEKeySpec().getParameterSpec();
                theGenerator.initialize(myParms, getRandom());

                /* Create the factory */
                setKeyFactory(getJavaKeyFactory(CMCE_ALGO, true));

            } catch (InvalidAlgorithmParameterException e) {
                throw new GordianCryptoException("Failed to create CMCEgenerator", e);
            }
        }

        @Override
        public JcaKeyPair generateKeyPair() {
            /* Generate and return the keyPair */
            final KeyPair myPair = theGenerator.generateKeyPair();
            final JcaPublicKey myPublic = createPublic(myPair.getPublic());
            final JcaPrivateKey myPrivate = createPrivate(myPair.getPrivate());
            return new JcaKeyPair(myPublic, myPrivate);
        }
    }

    /**
     * Jca Frodo KeyPair generator.
     */
    public static class JcaFrodoKeyPairGenerator
            extends JcaKeyPairGenerator {
        /**
         * FRODO algorithm.
         */
        private static final String FRODO_ALGO = "FRODO";

        /**
         * Generator.
         */
        private final KeyPairGenerator theGenerator;

        /**
         * Constructor.
         *
         * @param pFactory the Security Factory
         * @param pKeySpec the keySpec
         * @throws GordianException on error
         */
        JcaFrodoKeyPairGenerator(final GordianBaseFactory pFactory,
                                 final GordianKeyPairSpec pKeySpec) throws GordianException {
            /* initialize underlying class */
            super(pFactory, pKeySpec);

            /* Protect against exceptions */
            try {
                /* Create and initialize the generator */
                theGenerator = getJavaKeyPairGenerator(FRODO_ALGO, true);
                final FrodoParameterSpec myParms = pKeySpec.getFRODOKeySpec().getParameterSpec();
                theGenerator.initialize(myParms, getRandom());

                /* Create the factory */
                setKeyFactory(getJavaKeyFactory(FRODO_ALGO, true));

            } catch (InvalidAlgorithmParameterException e) {
                throw new GordianCryptoException("Failed to create FRODOgenerator", e);
            }
        }

        @Override
        public JcaKeyPair generateKeyPair() {
            /* Generate and return the keyPair */
            final KeyPair myPair = theGenerator.generateKeyPair();
            final JcaPublicKey myPublic = createPublic(myPair.getPublic());
            final JcaPrivateKey myPrivate = createPrivate(myPair.getPrivate());
            return new JcaKeyPair(myPublic, myPrivate);
        }
    }

    /**
     * Jca SABER KeyPair generator.
     */
    public static class JcaSABERKeyPairGenerator
            extends JcaKeyPairGenerator {
        /**
         * SABER algorithm.
         */
        private static final String SABER_ALGO = "SABER";

        /**
         * Generator.
         */
        private final KeyPairGenerator theGenerator;

        /**
         * Constructor.
         *
         * @param pFactory the Security Factory
         * @param pKeySpec the keySpec
         * @throws GordianException on error
         */
        JcaSABERKeyPairGenerator(final GordianBaseFactory pFactory,
                                 final GordianKeyPairSpec pKeySpec) throws GordianException {
            /* initialize underlying class */
            super(pFactory, pKeySpec);

            /* Protect against exceptions */
            try {
                /* Create and initialize the generator */
                theGenerator = getJavaKeyPairGenerator(SABER_ALGO, true);
                final SABERParameterSpec myParms = pKeySpec.getSABERKeySpec().getParameterSpec();
                theGenerator.initialize(myParms, getRandom());

                /* Create the factory */
                setKeyFactory(getJavaKeyFactory(SABER_ALGO, true));

            } catch (InvalidAlgorithmParameterException e) {
                throw new GordianCryptoException("Failed to create SABERgenerator", e);
            }
        }

        @Override
        public JcaKeyPair generateKeyPair() {
            /* Generate and return the keyPair */
            final KeyPair myPair = theGenerator.generateKeyPair();
            final JcaPublicKey myPublic = createPublic(myPair.getPublic());
            final JcaPrivateKey myPrivate = createPrivate(myPair.getPrivate());
            return new JcaKeyPair(myPublic, myPrivate);
        }
    }

    /**
     * Jca MLKEM KeyPair generator.
     */
    public static class JcaMLKEMKeyPairGenerator
            extends JcaKeyPairGenerator {
        /**
         * MLKEM algorithm.
         */
        private static final String MLKEM_ALGO = "MLKEM";

        /**
         * Generator.
         */
        private final KeyPairGenerator theGenerator;

        /**
         * Constructor.
         *
         * @param pFactory the Security Factory
         * @param pKeySpec the keySpec
         * @throws GordianException on error
         */
        JcaMLKEMKeyPairGenerator(final GordianBaseFactory pFactory,
                                 final GordianKeyPairSpec pKeySpec) throws GordianException {
            /* initialize underlying class */
            super(pFactory, pKeySpec);

            /* Protect against exceptions */
            try {
                /* Create and initialize the generator */
                theGenerator = getJavaKeyPairGenerator(MLKEM_ALGO, false);
                final MLKEMParameterSpec myParms = pKeySpec.getMLKEMKeySpec().getParameterSpec();
                theGenerator.initialize(myParms, getRandom());

                /* Create the factory */
                setKeyFactory(getJavaKeyFactory(MLKEM_ALGO, false));

            } catch (InvalidAlgorithmParameterException e) {
                throw new GordianCryptoException("Failed to create MLKEMgenerator", e);
            }
        }

        @Override
        public JcaKeyPair generateKeyPair() {
            /* Generate and return the keyPair */
            final KeyPair myPair = theGenerator.generateKeyPair();
            final JcaPublicKey myPublic = createPublic(myPair.getPublic());
            final JcaPrivateKey myPrivate = createPrivate(myPair.getPrivate());
            return new JcaKeyPair(myPublic, myPrivate);
        }
    }

    /**
     * Jca MLDSA KeyPair generator.
     */
    public static class JcaMLDSAKeyPairGenerator
            extends JcaKeyPairGenerator {
        /**
         * MLDSA algorithm.
         */
        private static final String MLDSA_ALGO = "ML-DSA";

        /**
         * HASH indication.
         */
        private static final String MLDSA_HASH = "HASH-" + MLDSA_ALGO;

        /**
         * Generator.
         */
        private final KeyPairGenerator theGenerator;

        /**
         * Constructor.
         *
         * @param pFactory the Security Factory
         * @param pKeySpec the keySpec
         * @throws GordianException on error
         */
        JcaMLDSAKeyPairGenerator(final GordianBaseFactory pFactory,
                                 final GordianKeyPairSpec pKeySpec) throws GordianException {
            /* initialize underlying class */
            super(pFactory, pKeySpec);

            /* Protect against exceptions */
            try {
                /* Determine algorithm */
                final String myAlgo = pKeySpec.getMLDSAKeySpec().isHash() ? MLDSA_HASH : MLDSA_ALGO;

                /* Create and initialize the generator */
                theGenerator = getJavaKeyPairGenerator(myAlgo, false);
                final MLDSAParameterSpec myParms = pKeySpec.getMLDSAKeySpec().getParameterSpec();
                theGenerator.initialize(myParms, getRandom());

                /* Create the factory */
                setKeyFactory(getJavaKeyFactory(myAlgo, false));

            } catch (InvalidAlgorithmParameterException e) {
                throw new GordianCryptoException("Failed to create MLDSAGenerator", e);
            }
        }

        @Override
        public JcaKeyPair generateKeyPair() {
            /* Generate and return the keyPair */
            final KeyPair myPair = theGenerator.generateKeyPair();
            final JcaPublicKey myPublic = createPublic(myPair.getPublic());
            final JcaPrivateKey myPrivate = createPrivate(myPair.getPrivate());
            return new JcaKeyPair(myPublic, myPrivate);
        }
    }

    /**
     * Jca HQC KeyPair generator.
     */
    public static class JcaHQCKeyPairGenerator
            extends JcaKeyPairGenerator {
        /**
         * BIKE algorithm.
         */
        private static final String HQC_ALGO = "HQC";

        /**
         * Generator.
         */
        private final KeyPairGenerator theGenerator;

        /**
         * Constructor.
         *
         * @param pFactory the Security Factory
         * @param pKeySpec the keySpec
         * @throws GordianException on error
         */
        JcaHQCKeyPairGenerator(final GordianBaseFactory pFactory,
                               final GordianKeyPairSpec pKeySpec) throws GordianException {
            /* initialize underlying class */
            super(pFactory, pKeySpec);

            /* Protect against exceptions */
            try {
                /* Create and initialize the generator */
                theGenerator = getJavaKeyPairGenerator(HQC_ALGO, true);
                final HQCParameterSpec myParms = pKeySpec.getHQCKeySpec().getParameterSpec();
                theGenerator.initialize(myParms, getRandom());

                /* Create the factory */
                setKeyFactory(getJavaKeyFactory(HQC_ALGO, true));

            } catch (InvalidAlgorithmParameterException e) {
                throw new GordianCryptoException("Failed to create HQCgenerator", e);
            }
        }

        @Override
        public JcaKeyPair generateKeyPair() {
            /* Generate and return the keyPair */
            final KeyPair myPair = theGenerator.generateKeyPair();
            final JcaPublicKey myPublic = createPublic(myPair.getPublic());
            final JcaPrivateKey myPrivate = createPrivate(myPair.getPrivate());
            return new JcaKeyPair(myPublic, myPrivate);
        }
    }


    /**
     * Jca BIKE KeyPair generator.
     */
    public static class JcaBIKEKeyPairGenerator
            extends JcaKeyPairGenerator {
        /**
         * BIKE algorithm.
         */
        private static final String BIKE_ALGO = "BIKE";

        /**
         * Generator.
         */
        private final KeyPairGenerator theGenerator;

        /**
         * Constructor.
         *
         * @param pFactory the Security Factory
         * @param pKeySpec the keySpec
         * @throws GordianException on error
         */
        JcaBIKEKeyPairGenerator(final GordianBaseFactory pFactory,
                                final GordianKeyPairSpec pKeySpec) throws GordianException {
            /* initialize underlying class */
            super(pFactory, pKeySpec);

            /* Protect against exceptions */
            try {
                /* Create and initialize the generator */
                theGenerator = getJavaKeyPairGenerator(BIKE_ALGO, true);
                final BIKEParameterSpec myParms = pKeySpec.getBIKEKeySpec().getParameterSpec();
                theGenerator.initialize(myParms, getRandom());

                /* Create the factory */
                setKeyFactory(getJavaKeyFactory(BIKE_ALGO, true));

            } catch (InvalidAlgorithmParameterException e) {
                throw new GordianCryptoException("Failed to create BIKEgenerator", e);
            }
        }

        @Override
        public JcaKeyPair generateKeyPair() {
            /* Generate and return the keyPair */
            final KeyPair myPair = theGenerator.generateKeyPair();
            final JcaPublicKey myPublic = createPublic(myPair.getPublic());
            final JcaPrivateKey myPrivate = createPrivate(myPair.getPrivate());
            return new JcaKeyPair(myPublic, myPrivate);
        }
    }

    /**
     * Jca NTRU KeyPair generator.
     */
    public static class JcaNTRUKeyPairGenerator
            extends JcaKeyPairGenerator {
        /**
         * NTRU algorithm.
         */
        private static final String NTRU_ALGO = "NTRU";

        /**
         * Generator.
         */
        private final KeyPairGenerator theGenerator;

        /**
         * Constructor.
         *
         * @param pFactory the Security Factory
         * @param pKeySpec the keySpec
         * @throws GordianException on error
         */
        JcaNTRUKeyPairGenerator(final GordianBaseFactory pFactory,
                                final GordianKeyPairSpec pKeySpec) throws GordianException {
            /* initialize underlying class */
            super(pFactory, pKeySpec);

            /* Protect against exceptions */
            try {
                /* Create and initialize the generator */
                theGenerator = getJavaKeyPairGenerator(NTRU_ALGO, true);
                final NTRUParameterSpec myParms = pKeySpec.getNTRUKeySpec().getParameterSpec();
                theGenerator.initialize(myParms, getRandom());

                /* Create the factory */
                setKeyFactory(getJavaKeyFactory(NTRU_ALGO, true));

            } catch (InvalidAlgorithmParameterException e) {
                throw new GordianCryptoException("Failed to create NTRUgenerator", e);
            }
        }

        @Override
        public JcaKeyPair generateKeyPair() {
            /* Generate and return the keyPair */
            final KeyPair myPair = theGenerator.generateKeyPair();
            final JcaPublicKey myPublic = createPublic(myPair.getPublic());
            final JcaPrivateKey myPrivate = createPrivate(myPair.getPrivate());
            return new JcaKeyPair(myPublic, myPrivate);
        }
    }

    /**
     * Jca Falcon KeyPair generator.
     */
    public static class JcaFalconKeyPairGenerator
            extends JcaKeyPairGenerator {
        /**
         * FALCON algorithm.
         */
        private static final String FALCON_ALGO = "FALCON";

        /**
         * Generator.
         */
        private final KeyPairGenerator theGenerator;

        /**
         * Constructor.
         *
         * @param pFactory the Security Factory
         * @param pKeySpec the keySpec
         * @throws GordianException on error
         */
        JcaFalconKeyPairGenerator(final GordianBaseFactory pFactory,
                                  final GordianKeyPairSpec pKeySpec) throws GordianException {
            /* initialize underlying class */
            super(pFactory, pKeySpec);

            /* Protect against exceptions */
            try {
                /* Create and initialize the generator */
                theGenerator = getJavaKeyPairGenerator(FALCON_ALGO, true);
                final FalconParameterSpec myParms = pKeySpec.getFalconKeySpec().getParameterSpec();
                theGenerator.initialize(myParms, getRandom());

                /* Create the factory */
                setKeyFactory(getJavaKeyFactory(FALCON_ALGO, true));

            } catch (InvalidAlgorithmParameterException e) {
                throw new GordianCryptoException("Failed to create FALCONgenerator", e);
            }
        }

        @Override
        public JcaKeyPair generateKeyPair() {
            /* Generate and return the keyPair */
            final KeyPair myPair = theGenerator.generateKeyPair();
            final JcaPublicKey myPublic = createPublic(myPair.getPublic());
            final JcaPrivateKey myPrivate = createPrivate(myPair.getPrivate());
            return new JcaKeyPair(myPublic, myPrivate);
        }
    }

    /**
     * Jca Mayo KeyPair generator.
     */
    public static class JcaMayoKeyPairGenerator
            extends JcaKeyPairGenerator {
        /**
         * Mayo algorithm.
         */
        private static final String MAYO_ALGO = "MAYO";

        /**
         * Generator.
         */
        private final KeyPairGenerator theGenerator;

        /**
         * Constructor.
         *
         * @param pFactory the Security Factory
         * @param pKeySpec the keySpec
         * @throws GordianException on error
         */
        JcaMayoKeyPairGenerator(final GordianBaseFactory pFactory,
                                final GordianKeyPairSpec pKeySpec) throws GordianException {
            /* initialize underlying class */
            super(pFactory, pKeySpec);

            /* Protect against exceptions */
            try {
                /* Create and initialize the generator */
                theGenerator = getJavaKeyPairGenerator(MAYO_ALGO, true);
                final MayoParameterSpec myParms = pKeySpec.getMayoKeySpec().getParameterSpec();
                theGenerator.initialize(myParms, getRandom());

                /* Create the factory */
                setKeyFactory(getJavaKeyFactory(MAYO_ALGO, true));

            } catch (InvalidAlgorithmParameterException e) {
                throw new GordianCryptoException("Failed to create MayoGenerator", e);
            }
        }

        @Override
        public JcaKeyPair generateKeyPair() {
            /* Generate and return the keyPair */
            final KeyPair myPair = theGenerator.generateKeyPair();
            final JcaPublicKey myPublic = createPublic(myPair.getPublic());
            final JcaPrivateKey myPrivate = createPrivate(myPair.getPrivate());
            return new JcaKeyPair(myPublic, myPrivate);
        }
    }

    /**
     * Jca Snova KeyPair generator.
     */
    public static class JcaSnovaKeyPairGenerator
            extends JcaKeyPairGenerator {
        /**
         * Snova algorithm.
         */
        private static final String SNOVA_ALGO = "SNOVA";

        /**
         * Generator.
         */
        private final KeyPairGenerator theGenerator;

        /**
         * Constructor.
         *
         * @param pFactory the Security Factory
         * @param pKeySpec the keySpec
         * @throws GordianException on error
         */
        JcaSnovaKeyPairGenerator(final GordianBaseFactory pFactory,
                                 final GordianKeyPairSpec pKeySpec) throws GordianException {
            /* initialize underlying class */
            super(pFactory, pKeySpec);

            /* Protect against exceptions */
            try {
                /* Create and initialize the generator */
                theGenerator = getJavaKeyPairGenerator(SNOVA_ALGO, true);
                final SnovaParameterSpec myParms = pKeySpec.getSnovaKeySpec().getParameterSpec();
                theGenerator.initialize(myParms, getRandom());

                /* Create the factory */
                setKeyFactory(getJavaKeyFactory(SNOVA_ALGO, true));

            } catch (InvalidAlgorithmParameterException e) {
                throw new GordianCryptoException("Failed to create SnovaGenerator", e);
            }
        }

        @Override
        public JcaKeyPair generateKeyPair() {
            /* Generate and return the keyPair */
            final KeyPair myPair = theGenerator.generateKeyPair();
            final JcaPublicKey myPublic = createPublic(myPair.getPublic());
            final JcaPrivateKey myPrivate = createPrivate(myPair.getPrivate());
            return new JcaKeyPair(myPublic, myPrivate);
        }
    }

    /**
     * Jca NTRULPrime KeyPair generator.
     */
    public static class JcaNTRULPrimeKeyPairGenerator
            extends JcaKeyPairGenerator {
        /**
         * NTRULPrime algorithm.
         */
        private static final String NTRU_ALGO = "NTRULPRIME";

        /**
         * Generator.
         */
        private final KeyPairGenerator theGenerator;

        /**
         * Constructor.
         *
         * @param pFactory the Security Factory
         * @param pKeySpec the keySpec
         * @throws GordianException on error
         */
        JcaNTRULPrimeKeyPairGenerator(final GordianBaseFactory pFactory,
                                      final GordianKeyPairSpec pKeySpec) throws GordianException {
            /* initialize underlying class */
            super(pFactory, pKeySpec);

            /* Protect against exceptions */
            try {
                /* Create and initialize the generator */
                theGenerator = getJavaKeyPairGenerator(NTRU_ALGO, true);
                final NTRULPRimeParameterSpec myParms = pKeySpec.getNTRUPrimeKeySpec().getParams().getNTRULParameterSpec();
                theGenerator.initialize(myParms, getRandom());

                /* Create the factory */
                setKeyFactory(getJavaKeyFactory(NTRU_ALGO, true));

            } catch (InvalidAlgorithmParameterException e) {
                throw new GordianCryptoException("Failed to create NTRULPrimeGenerator", e);
            }
        }

        @Override
        public JcaKeyPair generateKeyPair() {
            /* Generate and return the keyPair */
            final KeyPair myPair = theGenerator.generateKeyPair();
            final JcaPublicKey myPublic = createPublic(myPair.getPublic());
            final JcaPrivateKey myPrivate = createPrivate(myPair.getPrivate());
            return new JcaKeyPair(myPublic, myPrivate);
        }
    }

    /**
     * Jca SNTRUPrime KeyPair generator.
     */
    public static class JcaSNTRUPrimeKeyPairGenerator
            extends JcaKeyPairGenerator {
        /**
         * NTRULPrime algorithm.
         */
        private static final String NTRU_ALGO = "SNTRUPRIME";

        /**
         * Generator.
         */
        private final KeyPairGenerator theGenerator;

        /**
         * Constructor.
         *
         * @param pFactory the Security Factory
         * @param pKeySpec the keySpec
         * @throws GordianException on error
         */
        JcaSNTRUPrimeKeyPairGenerator(final GordianBaseFactory pFactory,
                                      final GordianKeyPairSpec pKeySpec) throws GordianException {
            /* initialize underlying class */
            super(pFactory, pKeySpec);

            /* Protect against exceptions */
            try {
                /* Create and initialize the generator */
                theGenerator = getJavaKeyPairGenerator(NTRU_ALGO, true);
                final SNTRUPrimeParameterSpec myParms = pKeySpec.getNTRUPrimeKeySpec().getParams().getSNTRUParameterSpec();
                theGenerator.initialize(myParms, getRandom());

                /* Create the factory */
                setKeyFactory(getJavaKeyFactory(NTRU_ALGO, true));

            } catch (InvalidAlgorithmParameterException e) {
                throw new GordianCryptoException("Failed to create SNTRUPrimeGenerator", e);
            }
        }

        @Override
        public JcaKeyPair generateKeyPair() {
            /* Generate and return the keyPair */
            final KeyPair myPair = theGenerator.generateKeyPair();
            final JcaPublicKey myPublic = createPublic(myPair.getPublic());
            final JcaPrivateKey myPrivate = createPrivate(myPair.getPrivate());
            return new JcaKeyPair(myPublic, myPrivate);
        }
    }

    /**
     * Jca Picnic KeyPair generator.
     */
    public static class JcaPicnicKeyPairGenerator
            extends JcaKeyPairGenerator {
        /**
         * Picnic algorithm.
         */
        private static final String PICNIC_ALGO = "PICNIC";

        /**
         * Generator.
         */
        private final KeyPairGenerator theGenerator;

        /**
         * Constructor.
         *
         * @param pFactory the Security Factory
         * @param pKeySpec the keySpec
         * @throws GordianException on error
         */
        JcaPicnicKeyPairGenerator(final GordianBaseFactory pFactory,
                                  final GordianKeyPairSpec pKeySpec) throws GordianException {
            /* initialize underlying class */
            super(pFactory, pKeySpec);

            /* Protect against exceptions */
            try {
                /* Create and initialize the generator */
                theGenerator = getJavaKeyPairGenerator(PICNIC_ALGO, true);
                final PicnicParameterSpec myParms = pKeySpec.getPicnicKeySpec().getParameterSpec();
                theGenerator.initialize(myParms, getRandom());

                /* Create the factory */
                setKeyFactory(getJavaKeyFactory(PICNIC_ALGO, true));

            } catch (InvalidAlgorithmParameterException e) {
                throw new GordianCryptoException("Failed to create PICNICgenerator", e);
            }
        }

        @Override
        public JcaKeyPair generateKeyPair() {
            /* Generate and return the keyPair */
            final KeyPair myPair = theGenerator.generateKeyPair();
            final JcaPublicKey myPublic = createPublic(myPair.getPublic());
            final JcaPrivateKey myPrivate = createPrivate(myPair.getPrivate());
            return new JcaKeyPair(myPublic, myPrivate);
        }
    }

    /**
     * Jca XMSS KeyPair generator.
     */
    public static class JcaXMSSKeyPairGenerator
            extends JcaKeyPairGenerator {
        /**
         * Generator.
         */
        private final KeyPairGenerator theGenerator;

        /**
         * Constructor.
         *
         * @param pFactory the Security Factory
         * @param pKeySpec the keySpec
         * @throws GordianException on error
         */
        JcaXMSSKeyPairGenerator(final GordianBaseFactory pFactory,
                                final GordianKeyPairSpec pKeySpec) throws GordianException {
            /* initialize underlying class */
            super(pFactory, pKeySpec);

            /* Protect against exceptions */
            try {
                /* Access the algorithm */
                final GordianXMSSKeySpec myXMSSKeySpec = pKeySpec.getXMSSKeySpec();
                final boolean isXMSSMT = myXMSSKeySpec.isMT();
                final GordianXMSSDigestType myType = myXMSSKeySpec.getDigestType();

                /* Create the parameters */
                final AlgorithmParameterSpec myAlgo = isXMSSMT
                        ? new XMSSMTParameterSpec(myXMSSKeySpec.getHeight().getHeight(),
                        myXMSSKeySpec.getLayers().getLayers(), myType.name())
                        : new XMSSParameterSpec(myXMSSKeySpec.getHeight().getHeight(), myType.name());

                /* Create and initialize the generator */
                final String myJavaType = myXMSSKeySpec.getKeyType().name();
                theGenerator = getJavaKeyPairGenerator(myJavaType, true);
                theGenerator.initialize(myAlgo, getRandom());

                /* Create the factory */
                setKeyFactory(getJavaKeyFactory(myJavaType, true));

            } catch (InvalidAlgorithmParameterException e) {
                throw new GordianCryptoException("Failed to create XMSSgenerator", e);
            }
        }

        @Override
        public JcaKeyPair generateKeyPair() {
            /* Generate and return the keyPair */
            final KeyPair myPair = theGenerator.generateKeyPair();
            final JcaPublicKey myPublic = createPublic(myPair.getPublic());
            final JcaStateAwarePrivateKey myPrivate = createPrivate(myPair.getPrivate());
            return new JcaStateAwareKeyPair(myPublic, myPrivate);
        }

        @Override
        protected JcaStateAwarePrivateKey createPrivate(final PrivateKey pPrivateKey) {
            return new JcaStateAwarePrivateKey(getKeySpec(), pPrivateKey);
        }

        @Override
        public JcaKeyPair deriveKeyPair(final X509EncodedKeySpec pPublicKey,
                                        final PKCS8EncodedKeySpec pPrivateKey) throws GordianException {
            /* Protect against exceptions */
            try {
                /* Check the keySpecs */
                checkKeySpec(pPrivateKey);

                /* derive keyPair */
                final JcaPublicKey myPublic = derivePublicKey(pPublicKey);
                JcaStateAwarePrivateKey myPrivate = createPrivate(getKeyFactory().generatePrivate(pPrivateKey));
                final JcaKeyPair myPair = new JcaStateAwareKeyPair(myPublic, myPrivate);

                /* Check that we have a matching pair */
                GordianKeyPairValidity.checkValidity(getFactory(), myPair);

                /* Rebuild and return the keyPair to avoid incrementing usage count */
                myPrivate = createPrivate(getKeyFactory().generatePrivate(pPrivateKey));
                return new JcaStateAwareKeyPair(myPublic, myPrivate);

            } catch (InvalidKeySpecException e) {
                throw new GordianCryptoException(PARSE_ERROR, e);
            }
        }
    }

    /**
     * Jca Edwards KeyPair generator.
     */
    public static class JcaEdKeyPairGenerator
            extends JcaKeyPairGenerator {
        /**
         * Generator.
         */
        private final KeyPairGenerator theGenerator;

        /**
         * Constructor.
         *
         * @param pFactory the Security Factory
         * @param pKeySpec the keySpec
         * @throws GordianException on error
         */
        protected JcaEdKeyPairGenerator(final GordianBaseFactory pFactory,
                                        final GordianKeyPairSpec pKeySpec) throws GordianException {
            /* initialize underlying class */
            super(pFactory, pKeySpec);

            /* Protect against exceptions */
            try {
                /* Create the parameters */
                final AlgorithmParameterSpec myAlgo;
                final boolean is25519 = pKeySpec.getEdwardsElliptic().is25519();
                switch (pKeySpec.getKeyPairType()) {
                    case XDH:
                        myAlgo = is25519
                                ? new XDHParameterSpec(XDHParameterSpec.X25519)
                                : new XDHParameterSpec(XDHParameterSpec.X448);
                        break;
                    case EDDSA:
                        myAlgo = is25519
                                ? new EdDSAParameterSpec(EdDSAParameterSpec.Ed25519)
                                : new EdDSAParameterSpec(EdDSAParameterSpec.Ed448);
                        break;
                    default:
                        throw new GordianLogicException("Invalid KeySpec" + pKeySpec);
                }

                /* Create and initialize the generator */
                final String myJavaType = pKeySpec.toString();
                theGenerator = getJavaKeyPairGenerator(myJavaType, false);
                theGenerator.initialize(myAlgo, getRandom());

                /* Create the factory */
                setKeyFactory(getJavaKeyFactory(myJavaType, false));

            } catch (InvalidAlgorithmParameterException e) {
                throw new GordianCryptoException("Failed to create EdwardsGenerator", e);
            }
        }

        @Override
        public JcaKeyPair generateKeyPair() {
            /* Generate and return the keyPair */
            final KeyPair myPair = theGenerator.generateKeyPair();
            final JcaPublicKey myPublic = createPublic(myPair.getPublic());
            final JcaPrivateKey myPrivate = createPrivate(myPair.getPrivate());
            return new JcaKeyPair(myPublic, myPrivate);
        }
    }

    /**
     * Jca NewHope KeyPair generator.
     */
    public static class JcaNewHopeKeyPairGenerator
            extends JcaKeyPairGenerator {
        /**
         * NewHope algorithm.
         */
        private static final String NEWHOPE_ALGO = "NH";

        /**
         * Generator.
         */
        private final KeyPairGenerator theGenerator;

        /**
         * Constructor.
         *
         * @param pFactory the Security Factory
         * @param pKeySpec the keySpec
         * @throws GordianException on error
         */
        JcaNewHopeKeyPairGenerator(final GordianBaseFactory pFactory,
                                   final GordianKeyPairSpec pKeySpec) throws GordianException {
            /* initialize underlying class */
            super(pFactory, pKeySpec);

            /* Create and initialize the generator */
            theGenerator = getJavaKeyPairGenerator(NEWHOPE_ALGO, true);
            theGenerator.initialize(GordianRSAModulus.MOD1024.getLength(), getRandom());

            /* Create the factory */
            setKeyFactory(getJavaKeyFactory(NEWHOPE_ALGO, true));
        }

        @Override
        public JcaKeyPair generateKeyPair() {
            /* Generate and return the keyPair */
            final KeyPair myPair = theGenerator.generateKeyPair();
            final JcaPublicKey myPublic = createPublic(myPair.getPublic());
            final JcaPrivateKey myPrivate = createPrivate(myPair.getPrivate());
            return new JcaKeyPair(myPublic, myPrivate);
        }
    }

    /**
     * Jca LMS KeyPair generator.
     */
    public static class JcaLMSKeyPairGenerator
            extends JcaKeyPairGenerator {
        /**
         * Generator.
         */
        private final KeyPairGenerator theGenerator;

        /**
         * Constructor.
         *
         * @param pFactory the Security Factory
         * @param pKeySpec the keySpec
         * @throws GordianException on error
         */
        JcaLMSKeyPairGenerator(final GordianBaseFactory pFactory,
                               final GordianKeyPairSpec pKeySpec) throws GordianException {
            /* initialize underlying class */
            super(pFactory, pKeySpec);

            /* Create and initialize the generator */
            final String myJavaType = pKeySpec.getKeyPairType().toString();
            theGenerator = getJavaKeyPairGenerator(myJavaType, true);

            /* Protect against exceptions */
            try {
                final AlgorithmParameterSpec myParms = pKeySpec.getSubKeyType() instanceof GordianHSSKeySpec
                        ? deriveParameters(pKeySpec.getHSSKeySpec())
                        : deriveParameters(pKeySpec.getLMSKeySpec());
                theGenerator.initialize(myParms, getRandom());

            } catch (InvalidAlgorithmParameterException e) {
                throw new GordianCryptoException("Failed to initialize generator", e);
            }

            /* Create the factory */
            setKeyFactory(getJavaKeyFactory(myJavaType, true));
        }

        /**
         * Derive the parameters.
         *
         * @param pKeySpec the keySPec
         * @return the parameters.
         */
        private static LMSHSSKeyGenParameterSpec deriveParameters(final GordianHSSKeySpec pKeySpec) {
            /* Generate and return the keyPair */
            final GordianLMSKeySpec myKeySpec = pKeySpec.getKeySpec();
            final LMSKeyGenParameterSpec[] myParams = new LMSKeyGenParameterSpec[pKeySpec.getTreeDepth()];
            Arrays.fill(myParams, deriveParameters(myKeySpec));
            return new LMSHSSKeyGenParameterSpec(myParams);
        }

        /**
         * Derive the parameters.
         *
         * @param pKeySpec the keySpec
         * @return the parameters.
         */
        private static LMSKeyGenParameterSpec deriveParameters(final GordianLMSKeySpec pKeySpec) {
            final LMSParameters myParms = pKeySpec.getParameters();
            return new LMSKeyGenParameterSpec(myParms.getLMSigParam(), myParms.getLMOTSParam());
        }

        @Override
        public JcaKeyPair generateKeyPair() {
            /* Generate and return the keyPair */
            final KeyPair myPair = theGenerator.generateKeyPair();
            final JcaPublicKey myPublic = createPublic(myPair.getPublic());
            final JcaStateAwarePrivateKey myPrivate = createPrivate(myPair.getPrivate());
            return new JcaStateAwareKeyPair(myPublic, myPrivate);
        }

        @Override
        protected JcaStateAwarePrivateKey createPrivate(final PrivateKey pPrivateKey) {
            return new JcaStateAwarePrivateKey(getKeySpec(), pPrivateKey);
        }

        @Override
        public JcaKeyPair deriveKeyPair(final X509EncodedKeySpec pPublicKey,
                                        final PKCS8EncodedKeySpec pPrivateKey) throws GordianException {
            /* Protect against exceptions */
            try {
                /* Check the keySpecs */
                checkKeySpec(pPrivateKey);

                /* derive keyPair */
                final JcaPublicKey myPublic = derivePublicKey(pPublicKey);
                JcaStateAwarePrivateKey myPrivate = createPrivate(getKeyFactory().generatePrivate(pPrivateKey));
                final JcaKeyPair myPair = new JcaStateAwareKeyPair(myPublic, myPrivate);

                /* Check that we have a matching pair */
                GordianKeyPairValidity.checkValidity(getFactory(), myPair);

                /* Rebuild and return the keyPair to avoid incrementing usage count */
                myPrivate = createPrivate(getKeyFactory().generatePrivate(pPrivateKey));
                return new JcaStateAwareKeyPair(myPublic, myPrivate);

            } catch (InvalidKeySpecException e) {
                throw new GordianCryptoException(PARSE_ERROR, e);
            }
        }
    }

    /**
     * Create the BouncyCastle KeyFactory via JCA.
     *
     * @param pAlgorithm  the Algorithm
     * @param postQuantum is this a postQuantum algorithm?
     * @return the KeyFactory
     * @throws GordianException on error
     */
    static KeyFactory getJavaKeyFactory(final String pAlgorithm,
                                        final boolean postQuantum) throws GordianException {
        /* Protect against exceptions */
        try {
            /* Return a KeyFactory for the algorithm */
            return KeyFactory.getInstance(pAlgorithm, postQuantum
                    ? JcaProvider.BCPQPROV
                    : JcaProvider.BCPROV);

            /* Catch exceptions */
        } catch (NoSuchAlgorithmException e) {
            /* Throw the exception */
            throw new GordianCryptoException("Failed to create KeyFactory", e);
        }
    }

    /**
     * Create the BouncyCastle KeyPairGenerator via JCA.
     *
     * @param pAlgorithm  the Algorithm
     * @param postQuantum is this a postQuantum algorithm?
     * @return the KeyPairGenerator
     * @throws GordianException on error
     */
    static KeyPairGenerator getJavaKeyPairGenerator(final String pAlgorithm,
                                                    final boolean postQuantum) throws GordianException {
        /* Protect against exceptions */
        try {
            /* Return a KeyPairGenerator for the algorithm */
            return KeyPairGenerator.getInstance(pAlgorithm, postQuantum
                    ? JcaProvider.BCPQPROV
                    : JcaProvider.BCPROV);

            /* Catch exceptions */
        } catch (NoSuchAlgorithmException e) {
            /* Throw the exception */
            throw new GordianCryptoException("Failed to create KeyPairGenerator", e);
        }
    }
}