org.bouncycastle.crypto.util.PublicKeyFactory.java Source code

Java tutorial

Introduction

Here is the source code for org.bouncycastle.crypto.util.PublicKeyFactory.java

Source

package org.bouncycastle.crypto.util;

import java.io.IOException;
import java.io.InputStream;
import java.math.BigInteger;
import java.util.HashMap;
import java.util.Map;

import org.bouncycastle.asn1.ASN1Encodable;
import org.bouncycastle.asn1.ASN1InputStream;
import org.bouncycastle.asn1.ASN1Integer;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.ASN1OctetString;
import org.bouncycastle.asn1.ASN1Primitive;
import org.bouncycastle.asn1.DERBitString;
import org.bouncycastle.asn1.DEROctetString;
import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers;
import org.bouncycastle.asn1.cryptopro.ECGOST3410NamedCurves;
import org.bouncycastle.asn1.cryptopro.GOST3410PublicKeyAlgParameters;
import org.bouncycastle.asn1.edec.EdECObjectIdentifiers;
import org.bouncycastle.asn1.oiw.ElGamalParameter;
import org.bouncycastle.asn1.oiw.OIWObjectIdentifiers;
import org.bouncycastle.asn1.pkcs.DHParameter;
import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
import org.bouncycastle.asn1.pkcs.RSAPublicKey;
import org.bouncycastle.asn1.rosstandart.RosstandartObjectIdentifiers;
import org.bouncycastle.asn1.ua.DSTU4145BinaryField;
import org.bouncycastle.asn1.ua.DSTU4145ECBinary;
import org.bouncycastle.asn1.ua.DSTU4145NamedCurves;
import org.bouncycastle.asn1.ua.DSTU4145Params;
import org.bouncycastle.asn1.ua.DSTU4145PointEncoder;
import org.bouncycastle.asn1.ua.UAObjectIdentifiers;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.asn1.x509.DSAParameter;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import org.bouncycastle.asn1.x509.X509ObjectIdentifiers;
import org.bouncycastle.asn1.x9.DHPublicKey;
import org.bouncycastle.asn1.x9.DomainParameters;
import org.bouncycastle.asn1.x9.ECNamedCurveTable;
import org.bouncycastle.asn1.x9.ValidationParams;
import org.bouncycastle.asn1.x9.X962Parameters;
import org.bouncycastle.asn1.x9.X9ECParameters;
import org.bouncycastle.asn1.x9.X9ECPoint;
import org.bouncycastle.asn1.x9.X9IntegerConverter;
import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
import org.bouncycastle.crypto.ec.CustomNamedCurves;
import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
import org.bouncycastle.crypto.params.DHParameters;
import org.bouncycastle.crypto.params.DHPublicKeyParameters;
import org.bouncycastle.crypto.params.DHValidationParameters;
import org.bouncycastle.crypto.params.DSAParameters;
import org.bouncycastle.crypto.params.DSAPublicKeyParameters;
import org.bouncycastle.crypto.params.ECDomainParameters;
import org.bouncycastle.crypto.params.ECGOST3410Parameters;
import org.bouncycastle.crypto.params.ECNamedDomainParameters;
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
import org.bouncycastle.crypto.params.Ed25519PublicKeyParameters;
import org.bouncycastle.crypto.params.Ed448PublicKeyParameters;
import org.bouncycastle.crypto.params.ElGamalParameters;
import org.bouncycastle.crypto.params.ElGamalPublicKeyParameters;
import org.bouncycastle.crypto.params.RSAKeyParameters;
import org.bouncycastle.crypto.params.X25519PublicKeyParameters;
import org.bouncycastle.crypto.params.X448PublicKeyParameters;
import org.bouncycastle.math.ec.ECCurve;
import org.bouncycastle.math.ec.ECPoint;
import org.bouncycastle.util.Arrays;

/**
 * Factory to create asymmetric public key parameters for asymmetric ciphers from range of
 * ASN.1 encoded SubjectPublicKeyInfo objects.
 */
public class PublicKeyFactory {
    private static Map converters = new HashMap();

    static {
        converters.put(PKCSObjectIdentifiers.rsaEncryption, new RSAConverter());
        converters.put(PKCSObjectIdentifiers.id_RSASSA_PSS, new RSAConverter());
        converters.put(X509ObjectIdentifiers.id_ea_rsa, new RSAConverter());
        converters.put(X9ObjectIdentifiers.dhpublicnumber, new DHPublicNumberConverter());
        converters.put(PKCSObjectIdentifiers.dhKeyAgreement, new DHAgreementConverter());
        converters.put(X9ObjectIdentifiers.id_dsa, new DSAConverter());
        converters.put(OIWObjectIdentifiers.dsaWithSHA1, new DSAConverter());
        converters.put(OIWObjectIdentifiers.elGamalAlgorithm, new ElGamalConverter());
        converters.put(X9ObjectIdentifiers.id_ecPublicKey, new ECConverter());
        converters.put(CryptoProObjectIdentifiers.gostR3410_2001, new GOST3410_2001Converter());
        converters.put(RosstandartObjectIdentifiers.id_tc26_gost_3410_12_256, new GOST3410_2012Converter());
        converters.put(RosstandartObjectIdentifiers.id_tc26_gost_3410_12_512, new GOST3410_2012Converter());
        converters.put(UAObjectIdentifiers.dstu4145be, new DSTUConverter());
        converters.put(UAObjectIdentifiers.dstu4145le, new DSTUConverter());
        converters.put(EdECObjectIdentifiers.id_X25519, new X25519Converter());
        converters.put(EdECObjectIdentifiers.id_X448, new X448Converter());
        converters.put(EdECObjectIdentifiers.id_Ed25519, new Ed25519Converter());
        converters.put(EdECObjectIdentifiers.id_Ed448, new Ed448Converter());
    }

    /**
     * Create a public key from a SubjectPublicKeyInfo encoding
     *
     * @param keyInfoData the SubjectPublicKeyInfo encoding
     * @return the appropriate key parameter
     * @throws IOException on an error decoding the key
     */
    public static AsymmetricKeyParameter createKey(byte[] keyInfoData) throws IOException {
        return createKey(SubjectPublicKeyInfo.getInstance(ASN1Primitive.fromByteArray(keyInfoData)));
    }

    /**
     * Create a public key from a SubjectPublicKeyInfo encoding read from a stream
     *
     * @param inStr the stream to read the SubjectPublicKeyInfo encoding from
     * @return the appropriate key parameter
     * @throws IOException on an error decoding the key
     */
    public static AsymmetricKeyParameter createKey(InputStream inStr) throws IOException {
        return createKey(SubjectPublicKeyInfo.getInstance(new ASN1InputStream(inStr).readObject()));
    }

    /**
     * Create a public key from the passed in SubjectPublicKeyInfo
     *
     * @param keyInfo the SubjectPublicKeyInfo containing the key data
     * @return the appropriate key parameter
     * @throws IOException on an error decoding the key
     */
    public static AsymmetricKeyParameter createKey(SubjectPublicKeyInfo keyInfo) throws IOException {
        return createKey(keyInfo, null);
    }

    /**
     * Create a public key from the passed in SubjectPublicKeyInfo
     *
     * @param keyInfo       the SubjectPublicKeyInfo containing the key data
     * @param defaultParams default parameters that might be needed.
     * @return the appropriate key parameter
     * @throws IOException on an error decoding the key
     */
    public static AsymmetricKeyParameter createKey(SubjectPublicKeyInfo keyInfo, Object defaultParams)
            throws IOException {
        AlgorithmIdentifier algID = keyInfo.getAlgorithm();

        SubjectPublicKeyInfoConverter converter = (SubjectPublicKeyInfoConverter) converters
                .get(algID.getAlgorithm());
        if (null == converter) {
            throw new IOException("algorithm identifier in public key not recognised: " + algID.getAlgorithm());
        }

        return converter.getPublicKeyParameters(keyInfo, defaultParams);
    }

    private static abstract class SubjectPublicKeyInfoConverter {
        abstract AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Object defaultParams)
                throws IOException;
    }

    private static class RSAConverter extends SubjectPublicKeyInfoConverter {
        AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Object defaultParams)
                throws IOException {
            RSAPublicKey pubKey = RSAPublicKey.getInstance(keyInfo.parsePublicKey());

            return new RSAKeyParameters(false, pubKey.getModulus(), pubKey.getPublicExponent());
        }
    }

    private static class DHPublicNumberConverter extends SubjectPublicKeyInfoConverter {
        AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Object defaultParams)
                throws IOException {
            DHPublicKey dhPublicKey = DHPublicKey.getInstance(keyInfo.parsePublicKey());

            BigInteger y = dhPublicKey.getY();

            DomainParameters dhParams = DomainParameters.getInstance(keyInfo.getAlgorithm().getParameters());

            BigInteger p = dhParams.getP();
            BigInteger g = dhParams.getG();
            BigInteger q = dhParams.getQ();

            BigInteger j = null;
            if (dhParams.getJ() != null) {
                j = dhParams.getJ();
            }

            DHValidationParameters validation = null;
            ValidationParams dhValidationParms = dhParams.getValidationParams();
            if (dhValidationParms != null) {
                byte[] seed = dhValidationParms.getSeed();
                BigInteger pgenCounter = dhValidationParms.getPgenCounter();

                // TODO Check pgenCounter size?

                validation = new DHValidationParameters(seed, pgenCounter.intValue());
            }

            return new DHPublicKeyParameters(y, new DHParameters(p, g, q, j, validation));
        }
    }

    private static class DHAgreementConverter extends SubjectPublicKeyInfoConverter {
        AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Object defaultParams)
                throws IOException {
            DHParameter params = DHParameter.getInstance(keyInfo.getAlgorithm().getParameters());
            ASN1Integer derY = (ASN1Integer) keyInfo.parsePublicKey();

            BigInteger lVal = params.getL();
            int l = lVal == null ? 0 : lVal.intValue();
            DHParameters dhParams = new DHParameters(params.getP(), params.getG(), null, l);

            return new DHPublicKeyParameters(derY.getValue(), dhParams);
        }
    }

    private static class ElGamalConverter extends SubjectPublicKeyInfoConverter {
        AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Object defaultParams)
                throws IOException {
            ElGamalParameter params = ElGamalParameter.getInstance(keyInfo.getAlgorithm().getParameters());
            ASN1Integer derY = (ASN1Integer) keyInfo.parsePublicKey();

            return new ElGamalPublicKeyParameters(derY.getValue(),
                    new ElGamalParameters(params.getP(), params.getG()));
        }
    }

    private static class DSAConverter extends SubjectPublicKeyInfoConverter {
        AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Object defaultParams)
                throws IOException {
            ASN1Integer derY = (ASN1Integer) keyInfo.parsePublicKey();
            ASN1Encodable de = keyInfo.getAlgorithm().getParameters();

            DSAParameters parameters = null;
            if (de != null) {
                DSAParameter params = DSAParameter.getInstance(de.toASN1Primitive());
                parameters = new DSAParameters(params.getP(), params.getQ(), params.getG());
            }

            return new DSAPublicKeyParameters(derY.getValue(), parameters);
        }
    }

    private static class ECConverter extends SubjectPublicKeyInfoConverter {
        AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Object defaultParams) {
            X962Parameters params = X962Parameters.getInstance(keyInfo.getAlgorithm().getParameters());
            ECDomainParameters dParams;

            if (params.isNamedCurve()) {
                ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier) params.getParameters();

                X9ECParameters x9 = CustomNamedCurves.getByOID(oid);
                if (x9 == null) {
                    x9 = ECNamedCurveTable.getByOID(oid);
                }
                dParams = new ECNamedDomainParameters(oid, x9.getCurve(), x9.getG(), x9.getN(), x9.getH(),
                        x9.getSeed());
            } else if (params.isImplicitlyCA()) {
                dParams = (ECDomainParameters) defaultParams;
            } else {
                X9ECParameters x9 = X9ECParameters.getInstance(params.getParameters());
                dParams = new ECDomainParameters(x9.getCurve(), x9.getG(), x9.getN(), x9.getH(), x9.getSeed());
            }

            DERBitString bits = keyInfo.getPublicKeyData();
            byte[] data = bits.getBytes();
            ASN1OctetString key = new DEROctetString(data);

            //
            // extra octet string - the old extra embedded octet string
            //
            if (data[0] == 0x04 && data[1] == data.length - 2 && (data[2] == 0x02 || data[2] == 0x03)) {
                int qLength = new X9IntegerConverter().getByteLength(dParams.getCurve());

                if (qLength >= data.length - 3) {
                    try {
                        key = (ASN1OctetString) ASN1Primitive.fromByteArray(data);
                    } catch (IOException ex) {
                        throw new IllegalArgumentException("error recovering public key");
                    }
                }
            }

            X9ECPoint derQ = new X9ECPoint(dParams.getCurve(), key);

            return new ECPublicKeyParameters(derQ.getPoint(), dParams);
        }
    }

    private static class GOST3410_2001Converter extends SubjectPublicKeyInfoConverter {
        AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Object defaultParams) {
            AlgorithmIdentifier algID = keyInfo.getAlgorithm();
            //            ASN1ObjectIdentifier algOid = algID.getAlgorithm();
            GOST3410PublicKeyAlgParameters gostParams = GOST3410PublicKeyAlgParameters
                    .getInstance(algID.getParameters());
            ASN1ObjectIdentifier publicKeyParamSet = gostParams.getPublicKeyParamSet();

            ECGOST3410Parameters ecDomainParameters = new ECGOST3410Parameters(
                    new ECNamedDomainParameters(publicKeyParamSet,
                            ECGOST3410NamedCurves.getByOID(publicKeyParamSet)),
                    publicKeyParamSet, gostParams.getDigestParamSet(), gostParams.getEncryptionParamSet());

            ASN1OctetString key;
            try {
                key = (ASN1OctetString) keyInfo.parsePublicKey();
            } catch (IOException ex) {
                throw new IllegalArgumentException("error recovering GOST3410_2001 public key");
            }

            int fieldSize = 32;
            int keySize = 2 * fieldSize;

            byte[] keyEnc = key.getOctets();
            if (keyEnc.length != keySize) {
                throw new IllegalArgumentException("invalid length for GOST3410_2001 public key");
            }

            byte[] x9Encoding = new byte[1 + keySize];
            x9Encoding[0] = 0x04;
            for (int i = 1; i <= fieldSize; ++i) {
                x9Encoding[i] = keyEnc[fieldSize - i];
                x9Encoding[i + fieldSize] = keyEnc[keySize - i];
            }

            ECPoint q = ecDomainParameters.getCurve().decodePoint(x9Encoding);

            return new ECPublicKeyParameters(q, ecDomainParameters);
        }
    }

    private static class GOST3410_2012Converter extends SubjectPublicKeyInfoConverter {
        AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Object defaultParams) {
            AlgorithmIdentifier algID = keyInfo.getAlgorithm();
            ASN1ObjectIdentifier algOid = algID.getAlgorithm();
            GOST3410PublicKeyAlgParameters gostParams = GOST3410PublicKeyAlgParameters
                    .getInstance(algID.getParameters());
            ASN1ObjectIdentifier publicKeyParamSet = gostParams.getPublicKeyParamSet();

            ECGOST3410Parameters ecDomainParameters = new ECGOST3410Parameters(
                    new ECNamedDomainParameters(publicKeyParamSet,
                            ECGOST3410NamedCurves.getByOID(publicKeyParamSet)),
                    publicKeyParamSet, gostParams.getDigestParamSet(), gostParams.getEncryptionParamSet());

            ASN1OctetString key;
            try {
                key = (ASN1OctetString) keyInfo.parsePublicKey();
            } catch (IOException ex) {
                throw new IllegalArgumentException("error recovering GOST3410_2012 public key");
            }

            int fieldSize = 32;
            if (algOid.equals(RosstandartObjectIdentifiers.id_tc26_gost_3410_12_512)) {
                fieldSize = 64;
            }
            int keySize = 2 * fieldSize;

            byte[] keyEnc = key.getOctets();
            if (keyEnc.length != keySize) {
                throw new IllegalArgumentException("invalid length for GOST3410_2012 public key");
            }

            byte[] x9Encoding = new byte[1 + keySize];
            x9Encoding[0] = 0x04;
            for (int i = 1; i <= fieldSize; ++i) {
                x9Encoding[i] = keyEnc[fieldSize - i];
                x9Encoding[i + fieldSize] = keyEnc[keySize - i];
            }

            ECPoint q = ecDomainParameters.getCurve().decodePoint(x9Encoding);

            return new ECPublicKeyParameters(q, ecDomainParameters);
        }
    }

    private static class DSTUConverter extends SubjectPublicKeyInfoConverter {
        AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Object defaultParams)
                throws IOException {
            AlgorithmIdentifier algID = keyInfo.getAlgorithm();
            ASN1ObjectIdentifier algOid = algID.getAlgorithm();
            DSTU4145Params dstuParams = DSTU4145Params.getInstance(algID.getParameters());

            ASN1OctetString key;
            try {
                key = (ASN1OctetString) keyInfo.parsePublicKey();
            } catch (IOException ex) {
                throw new IllegalArgumentException("error recovering DSTU public key");
            }

            byte[] keyEnc = Arrays.clone(key.getOctets());

            if (algOid.equals(UAObjectIdentifiers.dstu4145le)) {
                reverseBytes(keyEnc);
            }

            ECDomainParameters ecDomain;
            if (dstuParams.isNamedCurve()) {
                ecDomain = DSTU4145NamedCurves.getByOID(dstuParams.getNamedCurve());
            } else {
                DSTU4145ECBinary binary = dstuParams.getECBinary();
                byte[] b_bytes = binary.getB();
                if (algOid.equals(UAObjectIdentifiers.dstu4145le)) {
                    reverseBytes(b_bytes);
                }
                BigInteger b = new BigInteger(1, b_bytes);
                DSTU4145BinaryField field = binary.getField();
                ECCurve curve = new ECCurve.F2m(field.getM(), field.getK1(), field.getK2(), field.getK3(),
                        binary.getA(), b);
                byte[] g_bytes = binary.getG();
                if (algOid.equals(UAObjectIdentifiers.dstu4145le)) {
                    reverseBytes(g_bytes);
                }
                ECPoint g = DSTU4145PointEncoder.decodePoint(curve, g_bytes);
                ecDomain = new ECDomainParameters(curve, g, binary.getN());
            }

            ECPoint q = DSTU4145PointEncoder.decodePoint(ecDomain.getCurve(), keyEnc);

            return new ECPublicKeyParameters(q, ecDomain);
        }

        private void reverseBytes(byte[] bytes) {
            byte tmp;

            for (int i = 0; i < bytes.length / 2; i++) {
                tmp = bytes[i];
                bytes[i] = bytes[bytes.length - 1 - i];
                bytes[bytes.length - 1 - i] = tmp;
            }
        }
    }

    private static class X25519Converter extends SubjectPublicKeyInfoConverter {
        AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Object defaultParams) {
            return new X25519PublicKeyParameters(
                    getRawKey(keyInfo, defaultParams, X25519PublicKeyParameters.KEY_SIZE), 0);
        }
    }

    private static class X448Converter extends SubjectPublicKeyInfoConverter {
        AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Object defaultParams) {
            return new X448PublicKeyParameters(getRawKey(keyInfo, defaultParams, X448PublicKeyParameters.KEY_SIZE),
                    0);
        }
    }

    private static class Ed25519Converter extends SubjectPublicKeyInfoConverter {
        AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Object defaultParams) {
            return new Ed25519PublicKeyParameters(
                    getRawKey(keyInfo, defaultParams, Ed25519PublicKeyParameters.KEY_SIZE), 0);
        }
    }

    private static class Ed448Converter extends SubjectPublicKeyInfoConverter {
        AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Object defaultParams) {
            return new Ed448PublicKeyParameters(
                    getRawKey(keyInfo, defaultParams, Ed448PublicKeyParameters.KEY_SIZE), 0);
        }
    }

    private static byte[] getRawKey(SubjectPublicKeyInfo keyInfo, Object defaultParams, int expectedSize) {
        /*
         * TODO[RFC 8422]
         * - Require defaultParams == null?
         * - Require keyInfo.getAlgorithm().getParameters() == null?
         */
        byte[] result = keyInfo.getPublicKeyData().getOctets();
        if (expectedSize != result.length) {
            throw new RuntimeException("public key encoding has incorrect length");
        }
        return result;
    }
}