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

org.bouncycastle.crypto.general.ElGamal Maven / Gradle / Ivy

Go to download

The FIPS 140-3 Bouncy Castle Crypto package is a Java implementation of cryptographic algorithms certified to FIPS 140-3 level 1. This jar contains JCE provider and low-level API for the BC-FJA version 2.0.0, FIPS Certificate #4743. Please see certificate for certified platform details.

There is a newer version: 2.0.0
Show newest version
package org.bouncycastle.crypto.general;

import java.security.SecureRandom;

import org.bouncycastle.crypto.Algorithm;
import org.bouncycastle.crypto.AsymmetricKey;
import org.bouncycastle.crypto.CryptoServicesRegistrar;
import org.bouncycastle.crypto.DigestAlgorithm;
import org.bouncycastle.crypto.InvalidCipherTextException;
import org.bouncycastle.crypto.InvalidWrappingException;
import org.bouncycastle.crypto.KeyUnwrapperUsingSecureRandom;
import org.bouncycastle.crypto.KeyWrapperUsingSecureRandom;
import org.bouncycastle.crypto.PlainInputProcessingException;
import org.bouncycastle.crypto.SingleBlockDecryptor;
import org.bouncycastle.crypto.asymmetric.AsymmetricDHKey;
import org.bouncycastle.crypto.asymmetric.AsymmetricDHPrivateKey;
import org.bouncycastle.crypto.asymmetric.AsymmetricDHPublicKey;
import org.bouncycastle.crypto.asymmetric.AsymmetricKeyPair;
import org.bouncycastle.crypto.asymmetric.DHDomainParameters;
import org.bouncycastle.crypto.fips.FipsDH;
import org.bouncycastle.crypto.fips.FipsSHS;
import org.bouncycastle.crypto.fips.FipsStatus;
import org.bouncycastle.crypto.fips.FipsUnapprovedOperationError;
import org.bouncycastle.crypto.internal.AsymmetricBlockCipher;
import org.bouncycastle.crypto.internal.CipherParameters;
import org.bouncycastle.crypto.internal.encodings.OAEPEncoding;
import org.bouncycastle.crypto.internal.encodings.PKCS1Encoding;
import org.bouncycastle.crypto.internal.params.ParametersWithRandom;
import org.bouncycastle.util.Arrays;

/**
 * Source class for implementations of ElGamal based algorithms.
 */
public final class ElGamal
{
    private ElGamal()
    {

    }

    private enum Variations
    {
        RAW,
        PKCS1v1_5,
        OAEP
    }

    /**
     * Basic ElGamal key marker, can be used for creating general purpose ElGamal keys.
     */
    public static final GeneralAlgorithm ALGORITHM = new GeneralAlgorithm("ELGAMAL", Variations.RAW);

    private static final GeneralAlgorithm PKCS1v1_5 = new GeneralAlgorithm("ELGAMAL/PKCS1V1.5", Variations.PKCS1v1_5);
    private static final GeneralAlgorithm ALGORITHM_OAEP = new GeneralAlgorithm("ELGAMAL/OAEP", Variations.OAEP);

    /**
     * RAW ElGamal algorithm parameter source.
     */
    public static final RawParameters RAW = new RawParameters();

    /**
     * PKCS#1 v1.5 ElGamal algorithm parameter source.
     */
    public static final OAEPParameters WRAP_OAEP = new OAEPParameters();

    /**
     * OAEP ElGamal algorithm parameter source - default digest is SHA-1
     */
    public static final PKCS1v15Parameters WRAP_PKCS1v1_5 = new PKCS1v15Parameters();

    /**
     * Base class for ElGamal encryption/decryption and key wrap/unwrap parameters.
     */
    public static class Parameters
        extends GeneralParameters
    {
        Parameters(GeneralAlgorithm algorithm)
        {
            super(algorithm);
        }
    }

    /**
     * Parameters for use with unformatted encryption/decryption.
     */
    public static final class RawParameters
        extends Parameters
    {
        RawParameters()
        {
            super(ALGORITHM);
        }
    }

    /**
     * Marker interface for parameters that can also be used for key wrapping.
     */
    public interface WrapParameters extends org.bouncycastle.crypto.Parameters
    {

    }

    /**
     * Parameters for use with PKCS#1 v1.5 formatted key wrapping/unwrapping and encryption/decryption.
     */
    public static final class PKCS1v15Parameters
        extends Parameters implements WrapParameters
    {
        PKCS1v15Parameters()
        {
            super(PKCS1v1_5);
        }
    }

    /**
     * Parameters for use with OAEP formatted key wrapping/unwrapping and encryption/decryption.
     */
    public static final class OAEPParameters
        extends Parameters implements WrapParameters
    {
        private final DigestAlgorithm digestAlgorithm;
        private final DigestAlgorithm mgfDigestAlgorithm;
        private final byte[] encodingParams;

        OAEPParameters()
        {
            this(FipsSHS.Algorithm.SHA1, FipsSHS.Algorithm.SHA1, null);
        }

        private OAEPParameters(DigestAlgorithm digestAlgorithm, DigestAlgorithm mgfDigestAlgorithm, byte[] encodingParams)
        {
            super(ALGORITHM_OAEP);

            this.digestAlgorithm = digestAlgorithm;
            this.mgfDigestAlgorithm = mgfDigestAlgorithm;
            this.encodingParams = Arrays.clone(encodingParams);
        }

        /**
         * Specify the digest algorithm to use. This also sets the MGF digest.
         *
         * @param digestAlgorithm  a digest algorithm.
         * @return a new parameter set.
         */
        public OAEPParameters withDigest(DigestAlgorithm digestAlgorithm)
        {
            return new OAEPParameters(digestAlgorithm, digestAlgorithm, encodingParams);
        }

        /**
         * Specify the digest algorithm to use for the MGF.
         *
         * @param mgfDigestAlgorithm  a digest algorithm for the MGF.
         * @return a new parameter set.
         */
        public OAEPParameters withMGFDigest(DigestAlgorithm mgfDigestAlgorithm)
        {
            return new OAEPParameters(digestAlgorithm, mgfDigestAlgorithm, encodingParams);
        }

        /**
         * Set the encoding parameters.
         *
         * @param encodingParams encoding params to include.
         * @return a new parameter set.
         */
        public OAEPParameters withEncodingParams(byte[] encodingParams)
        {
            return new OAEPParameters(digestAlgorithm, mgfDigestAlgorithm, Arrays.clone(encodingParams));
        }

        public DigestAlgorithm getDigest()
        {
            return digestAlgorithm;
        }

        public DigestAlgorithm getMGFDigest()
        {
            return mgfDigestAlgorithm;
        }

        public byte[] getEncodingParams()
        {
            return Arrays.clone(encodingParams);
        }
    }

    /**
     * ElGamal key pair generation parameters.
     */
    public static final class KeyGenParameters
        extends GeneralParameters
    {
        private DHDomainParameters domainParameters;

        /**
         * Base constructor for specific domain parameters.
         *
         * @param domainParameters the DH domain parameters.
         */
        public KeyGenParameters(DHDomainParameters domainParameters)
        {
            super(ALGORITHM);
            this.domainParameters = domainParameters;
        }

        /**
         * Constructor for specifying the ElGamal algorithm explicitly.
         *
         * @param parameters the particular parameter set to generate keys for.
         * @param domainParameters DH domain parameters representing the curve any generated keys will be for.
         */
        public KeyGenParameters(Parameters parameters, DHDomainParameters domainParameters)
        {
            super(parameters.getAlgorithm());
            this.domainParameters = domainParameters;
        }

        public DHDomainParameters getDomainParameters()
        {
            return domainParameters;
        }
    }

    /**
     * ElGamal key pair generator class.
     */
    public static final class KeyPairGenerator
        extends GuardedAsymmetricKeyPairGenerator
    {
        private final FipsDH.KeyPairGenerator kpGen;

        public KeyPairGenerator(KeyGenParameters keyGenParameters, SecureRandom random)
        {
            super(keyGenParameters);

            this.kpGen = new FipsDH.KeyPairGenerator(new FipsDH.KeyGenParameters(keyGenParameters.domainParameters), random);
        }

        @Override
        protected AsymmetricKeyPair doGenerateKeyPair()
        {
            AsymmetricKeyPair kp = kpGen.generateKeyPair();
            Algorithm algorithm = this.getParameters().getAlgorithm();

            AsymmetricDHPublicKey pubK = (AsymmetricDHPublicKey)kp.getPublicKey();
            AsymmetricDHPrivateKey priK = (AsymmetricDHPrivateKey)kp.getPrivateKey();

            return new AsymmetricKeyPair(new AsymmetricDHPublicKey(algorithm, pubK.getDomainParameters(), pubK.getY()), new AsymmetricDHPrivateKey(algorithm, priK.getDomainParameters(), priK.getX()));
        }
    }

    /**
     * Factory for creating ElGamal encryption/decryption operators.
     */
    public static final class OperatorFactory
        extends GuardedAsymmetricOperatorFactory
    {
        public SingleBlockDecryptor createBlockDecryptor(AsymmetricKey key, final Parameters parameters)
        {
            if (CryptoServicesRegistrar.isInApprovedOnlyMode())
            {
                throw new FipsUnapprovedOperationError("Attempt to create unapproved algorithm in approved only mode", parameters.getAlgorithm());
            }

            final AsymmetricBlockCipher engine = createCipher(false, key, parameters, null);

            return new SingleBlockDecryptor()
            {
                public byte[] decryptBlock(byte[] bytes, int offSet, int length)
                    throws InvalidCipherTextException
                {
                    try
                    {
                        Utils.approveModeCheck(parameters.getAlgorithm());

                        return engine.processBlock(bytes, offSet, length);
                    }
                    catch (org.bouncycastle.crypto.internal.InvalidCipherTextException e)
                    {
                        throw new InvalidCipherTextException(e.getMessage(), e);
                    }
                }

                public Parameters getParameters()
                {
                    Utils.approveModeCheck(parameters.getAlgorithm());

                    return parameters;
                }

                public int getInputSize()
                {
                    Utils.approveModeCheck(parameters.getAlgorithm());

                    // we allow one extra byte for raw engines
                    if (isRawEngine(engine))
                    {
                        return engine.getInputBlockSize() + 1;
                    }
                    else
                    {
                        return engine.getInputBlockSize();
                    }
                }

                public int getOutputSize()
                {
                    Utils.approveModeCheck(parameters.getAlgorithm());

                    return engine.getOutputBlockSize();
                }
            };
        }

        @Override
        protected AsymmetricBlockCipher createCipher(boolean forEncryption, AsymmetricKey key, Parameters parameters, SecureRandom random)
        {
            return ElGamal.createCipher(forEncryption, key, parameters, random);
        }
    }

    /**
     * Factory for creating ElGamal key wrap/unwrap operators.
     */
    public static final class KeyWrapOperatorFactory
        implements org.bouncycastle.crypto.KeyWrapOperatorFactory
    {
        public KeyWrapOperatorFactory()
        {
            FipsStatus.isReady();
            if (CryptoServicesRegistrar.isInApprovedOnlyMode())
            {
                throw new FipsUnapprovedOperationError("Attempt to create unapproved factory in approved only mode");
            }
        }

        public KeyWrapperUsingSecureRandom createKeyWrapper(AsymmetricDHKey key, WrapParameters parameters)
        {
            return new KeyWrapper(key, parameters, null);
        }

        public KeyUnwrapperUsingSecureRandom createKeyUnwrapper(AsymmetricDHKey key, WrapParameters parameters)
        {
            return new KeyUnwrapper(key, parameters, null);
        }

        private class KeyWrapper
            implements KeyWrapperUsingSecureRandom
        {
            private final AsymmetricBlockCipher keyWrapper;
            private final AsymmetricDHKey key;
            private final WrapParameters parameters;

            public KeyWrapper(AsymmetricDHKey key, WrapParameters parameters, SecureRandom random)
            {
                if (!(parameters instanceof Parameters))
                {
                    throw new IllegalArgumentException("Unknown parameters object: " + parameters.getClass().getName());
                }
                this.key = key;
                this.parameters = parameters;

                if (random != null)
                {
                    keyWrapper = createCipher(true, key, (Parameters)parameters, random);
                }
                else
                {
                    keyWrapper = null;
                }
            }

            public WrapParameters getParameters()
            {
                return parameters;
            }

            public byte[] wrap(byte[] in, int inOff, int inLen)
                throws PlainInputProcessingException
            {
                if (keyWrapper == null)
                {
                    throw new IllegalStateException("KeyWrapper requires a SecureRandom");
                }

                Utils.approveModeCheck(parameters.getAlgorithm());

                try
                {
                    return keyWrapper.processBlock(in, inOff, inLen);
                }
                catch (Exception e)
                {
                    throw new PlainInputProcessingException("Unable to wrap key: " + e.getMessage(), e);
                }
            }

            public KeyWrapperUsingSecureRandom withSecureRandom(SecureRandom random)
            {
                return new KeyWrapper(this.key, this.parameters, random);
            }
        }

        private class KeyUnwrapper
            implements KeyUnwrapperUsingSecureRandom
        {
            private final AsymmetricBlockCipher keyWrapper;
            private final AsymmetricDHKey key;
            private final WrapParameters parameters;

            public KeyUnwrapper(AsymmetricDHKey key, WrapParameters parameters, SecureRandom random)
            {
                if (!(parameters instanceof Parameters))
                {
                    throw new IllegalArgumentException("Unknown parameters object: " + parameters.getClass().getName());
                }
                this.key = key;
                this.parameters = parameters;

                if (random != null)
                {
                    keyWrapper = createCipher(false, key, (Parameters)parameters, random);
                }
                else
                {
                    keyWrapper = null;
                }
            }

            public WrapParameters getParameters()
            {
                return parameters;
            }

            public byte[] unwrap(byte[] in, int inOff, int inLen)
                throws InvalidWrappingException
            {
                if (keyWrapper == null)
                {
                    throw new IllegalStateException("KeyUnwrapper requires a SecureRandom");
                }

                Utils.approveModeCheck(parameters.getAlgorithm());

                try
                {
                    return keyWrapper.processBlock(in, inOff, inLen);
                }
                catch (Exception e)
                {
                    throw new InvalidWrappingException("Unable to unwrap key: " + e.getMessage(), e);
                }
            }

            public KeyUnwrapperUsingSecureRandom withSecureRandom(SecureRandom random)
            {
                return new KeyUnwrapper(this.key, this.parameters, random);
            }
        }
    }

    private static AsymmetricBlockCipher createCipher(boolean forEncryption, AsymmetricKey key, Parameters parameters, SecureRandom random)
    {
        AsymmetricBlockCipher engine = new ElGamalEngine();

        CipherParameters params;

        if (key instanceof AsymmetricDHPublicKey)
        {
            AsymmetricDHPublicKey k = (AsymmetricDHPublicKey)key;

            params = new ElGamalPublicKeyParameters(k.getY(), new ElGamalParameters(k.getDomainParameters().getP(), k.getDomainParameters().getG(), k.getDomainParameters().getL()));
        }
        else
        {
            AsymmetricDHPrivateKey k = (AsymmetricDHPrivateKey)key;

            params = new ElGamalPrivateKeyParameters(k.getX(), new ElGamalParameters(k.getDomainParameters().getP(), k.getDomainParameters().getG(), k.getDomainParameters().getL()));
        }

        if (parameters.getAlgorithm().equals(PKCS1v1_5))
        {
            engine = new PKCS1Encoding(engine);
        }
        else if (parameters.getAlgorithm().equals(ALGORITHM_OAEP))
        {
            OAEPParameters oeapParams = (OAEPParameters)parameters;

            engine = new OAEPEncoding(engine, Register.createDigest(oeapParams.digestAlgorithm), Register.createDigest(oeapParams.mgfDigestAlgorithm), oeapParams.encodingParams);
        }

        if (random != null)
        {
            params = new ParametersWithRandom(params, random);
        }

        engine.init(forEncryption, params);

        return engine;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy