org.bouncycastle.crypto.util.PublicKeyFactory Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of bouncycastle Show documentation
Show all versions of bouncycastle Show documentation
The Bouncy Castle Java APIs for CMS, PKCS, EAC, TSP, CMP, CRMF, OCSP, and certificate generation. This jar
contains APIs for JDK 1.5 and up. The APIs can be used in conjunction with a JCE/JCA provider such as the one
provided with the Bouncy Castle Cryptography APIs.
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);
}
else if (params.isImplicitlyCA())
{
dParams = (ECDomainParameters)defaultParams;
}
else
{
X9ECParameters x9 = X9ECParameters.getInstance(params.getParameters());
dParams = new ECDomainParameters(x9);
}
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.getByOIDX9(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.getByOIDX9(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;
}
}