Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/quite/humla-spongycastle.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/dh')
-rw-r--r--prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/dh/AlgorithmParameterGeneratorSpi.java77
-rw-r--r--prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/dh/AlgorithmParametersSpi.java142
-rw-r--r--prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/dh/BCDHPrivateKey.java213
-rw-r--r--prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/dh/BCDHPublicKey.java204
-rw-r--r--prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/dh/IESCipher.java507
-rw-r--r--prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/dh/KeyAgreementSpi.java227
-rw-r--r--prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/dh/KeyFactorySpi.java128
-rw-r--r--prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/dh/KeyPairGeneratorSpi.java119
8 files changed, 1617 insertions, 0 deletions
diff --git a/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/dh/AlgorithmParameterGeneratorSpi.java b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/dh/AlgorithmParameterGeneratorSpi.java
new file mode 100644
index 00000000..0b394287
--- /dev/null
+++ b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/dh/AlgorithmParameterGeneratorSpi.java
@@ -0,0 +1,77 @@
+package org.spongycastle.jcajce.provider.asymmetric.dh;
+
+import java.security.AlgorithmParameters;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.SecureRandom;
+import java.security.spec.AlgorithmParameterSpec;
+
+import javax.crypto.spec.DHGenParameterSpec;
+import javax.crypto.spec.DHParameterSpec;
+
+import org.spongycastle.crypto.generators.DHParametersGenerator;
+import org.spongycastle.crypto.params.DHParameters;
+import org.spongycastle.jce.provider.BouncyCastleProvider;
+
+public class AlgorithmParameterGeneratorSpi
+ extends java.security.AlgorithmParameterGeneratorSpi
+{
+ protected SecureRandom random;
+ protected int strength = 1024;
+
+ private int l = 0;
+
+ protected void engineInit(
+ int strength,
+ SecureRandom random)
+ {
+ this.strength = strength;
+ this.random = random;
+ }
+
+ protected void engineInit(
+ AlgorithmParameterSpec genParamSpec,
+ SecureRandom random)
+ throws InvalidAlgorithmParameterException
+ {
+ if (!(genParamSpec instanceof DHGenParameterSpec))
+ {
+ throw new InvalidAlgorithmParameterException("DH parameter generator requires a DHGenParameterSpec for initialisation");
+ }
+ DHGenParameterSpec spec = (DHGenParameterSpec)genParamSpec;
+
+ this.strength = spec.getPrimeSize();
+ this.l = spec.getExponentSize();
+ this.random = random;
+ }
+
+ protected AlgorithmParameters engineGenerateParameters()
+ {
+ DHParametersGenerator pGen = new DHParametersGenerator();
+
+ if (random != null)
+ {
+ pGen.init(strength, 20, random);
+ }
+ else
+ {
+ pGen.init(strength, 20, new SecureRandom());
+ }
+
+ DHParameters p = pGen.generateParameters();
+
+ AlgorithmParameters params;
+
+ try
+ {
+ params = AlgorithmParameters.getInstance("DH", BouncyCastleProvider.PROVIDER_NAME);
+ params.init(new DHParameterSpec(p.getP(), p.getG(), l));
+ }
+ catch (Exception e)
+ {
+ throw new RuntimeException(e.getMessage());
+ }
+
+ return params;
+ }
+
+}
diff --git a/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/dh/AlgorithmParametersSpi.java b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/dh/AlgorithmParametersSpi.java
new file mode 100644
index 00000000..ae89fba9
--- /dev/null
+++ b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/dh/AlgorithmParametersSpi.java
@@ -0,0 +1,142 @@
+package org.spongycastle.jcajce.provider.asymmetric.dh;
+
+import java.io.IOException;
+import java.security.spec.AlgorithmParameterSpec;
+import java.security.spec.InvalidParameterSpecException;
+
+import javax.crypto.spec.DHParameterSpec;
+
+import org.spongycastle.asn1.ASN1Encoding;
+import org.spongycastle.asn1.pkcs.DHParameter;
+
+public class AlgorithmParametersSpi
+ extends java.security.AlgorithmParametersSpi
+{
+ DHParameterSpec currentSpec;
+
+ protected boolean isASN1FormatString(String format)
+ {
+ return format == null || format.equals("ASN.1");
+ }
+
+ protected AlgorithmParameterSpec engineGetParameterSpec(
+ Class paramSpec)
+ throws InvalidParameterSpecException
+ {
+ if (paramSpec == null)
+ {
+ throw new NullPointerException("argument to getParameterSpec must not be null");
+ }
+
+ return localEngineGetParameterSpec(paramSpec);
+ }
+
+
+
+
+ /**
+ * Return the PKCS#3 ASN.1 structure DHParameter.
+ * <p>
+ * <pre>
+ * DHParameter ::= SEQUENCE {
+ * prime INTEGER, -- p
+ * base INTEGER, -- g
+ * privateValueLength INTEGER OPTIONAL}
+ * </pre>
+ */
+ protected byte[] engineGetEncoded()
+ {
+ DHParameter dhP = new DHParameter(currentSpec.getP(), currentSpec.getG(), currentSpec.getL());
+
+ try
+ {
+ return dhP.getEncoded(ASN1Encoding.DER);
+ }
+ catch (IOException e)
+ {
+ throw new RuntimeException("Error encoding DHParameters");
+ }
+ }
+
+ protected byte[] engineGetEncoded(
+ String format)
+ {
+ if (isASN1FormatString(format))
+ {
+ return engineGetEncoded();
+ }
+
+ return null;
+ }
+
+ protected AlgorithmParameterSpec localEngineGetParameterSpec(
+ Class paramSpec)
+ throws InvalidParameterSpecException
+ {
+ if (paramSpec == DHParameterSpec.class)
+ {
+ return currentSpec;
+ }
+
+ throw new InvalidParameterSpecException("unknown parameter spec passed to DH parameters object.");
+ }
+
+ protected void engineInit(
+ AlgorithmParameterSpec paramSpec)
+ throws InvalidParameterSpecException
+ {
+ if (!(paramSpec instanceof DHParameterSpec))
+ {
+ throw new InvalidParameterSpecException("DHParameterSpec required to initialise a Diffie-Hellman algorithm parameters object");
+ }
+
+ this.currentSpec = (DHParameterSpec)paramSpec;
+ }
+
+ protected void engineInit(
+ byte[] params)
+ throws IOException
+ {
+ try
+ {
+ DHParameter dhP = DHParameter.getInstance(params);
+
+ if (dhP.getL() != null)
+ {
+ currentSpec = new DHParameterSpec(dhP.getP(), dhP.getG(), dhP.getL().intValue());
+ }
+ else
+ {
+ currentSpec = new DHParameterSpec(dhP.getP(), dhP.getG());
+ }
+ }
+ catch (ClassCastException e)
+ {
+ throw new IOException("Not a valid DH Parameter encoding.");
+ }
+ catch (ArrayIndexOutOfBoundsException e)
+ {
+ throw new IOException("Not a valid DH Parameter encoding.");
+ }
+ }
+
+ protected void engineInit(
+ byte[] params,
+ String format)
+ throws IOException
+ {
+ if (isASN1FormatString(format))
+ {
+ engineInit(params);
+ }
+ else
+ {
+ throw new IOException("Unknown parameter format " + format);
+ }
+ }
+
+ protected String engineToString()
+ {
+ return "Diffie-Hellman Parameters";
+ }
+}
diff --git a/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/dh/BCDHPrivateKey.java b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/dh/BCDHPrivateKey.java
new file mode 100644
index 00000000..9949a58d
--- /dev/null
+++ b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/dh/BCDHPrivateKey.java
@@ -0,0 +1,213 @@
+package org.spongycastle.jcajce.provider.asymmetric.dh;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.math.BigInteger;
+import java.util.Enumeration;
+
+import javax.crypto.interfaces.DHPrivateKey;
+import javax.crypto.spec.DHParameterSpec;
+import javax.crypto.spec.DHPrivateKeySpec;
+
+import org.spongycastle.asn1.ASN1Encodable;
+import org.spongycastle.asn1.ASN1Encoding;
+import org.spongycastle.asn1.ASN1Integer;
+import org.spongycastle.asn1.ASN1ObjectIdentifier;
+import org.spongycastle.asn1.ASN1Sequence;
+import org.spongycastle.asn1.pkcs.DHParameter;
+import org.spongycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import org.spongycastle.asn1.pkcs.PrivateKeyInfo;
+import org.spongycastle.asn1.x509.AlgorithmIdentifier;
+import org.spongycastle.asn1.x9.DHDomainParameters;
+import org.spongycastle.asn1.x9.X9ObjectIdentifiers;
+import org.spongycastle.crypto.params.DHPrivateKeyParameters;
+import org.spongycastle.jcajce.provider.asymmetric.util.PKCS12BagAttributeCarrierImpl;
+import org.spongycastle.jce.interfaces.PKCS12BagAttributeCarrier;
+
+
+public class BCDHPrivateKey
+ implements DHPrivateKey, PKCS12BagAttributeCarrier
+{
+ static final long serialVersionUID = 311058815616901812L;
+
+ private BigInteger x;
+
+ private transient DHParameterSpec dhSpec;
+ private transient PrivateKeyInfo info;
+
+ private transient PKCS12BagAttributeCarrierImpl attrCarrier = new PKCS12BagAttributeCarrierImpl();
+
+ protected BCDHPrivateKey()
+ {
+ }
+
+ BCDHPrivateKey(
+ DHPrivateKey key)
+ {
+ this.x = key.getX();
+ this.dhSpec = key.getParams();
+ }
+
+ BCDHPrivateKey(
+ DHPrivateKeySpec spec)
+ {
+ this.x = spec.getX();
+ this.dhSpec = new DHParameterSpec(spec.getP(), spec.getG());
+ }
+
+ public BCDHPrivateKey(
+ PrivateKeyInfo info)
+ throws IOException
+ {
+ ASN1Sequence seq = ASN1Sequence.getInstance(info.getPrivateKeyAlgorithm().getParameters());
+ ASN1Integer derX = (ASN1Integer)info.parsePrivateKey();
+ ASN1ObjectIdentifier id = info.getPrivateKeyAlgorithm().getAlgorithm();
+
+ this.info = info;
+ this.x = derX.getValue();
+
+ if (id.equals(PKCSObjectIdentifiers.dhKeyAgreement))
+ {
+ DHParameter params = DHParameter.getInstance(seq);
+
+ if (params.getL() != null)
+ {
+ this.dhSpec = new DHParameterSpec(params.getP(), params.getG(), params.getL().intValue());
+ }
+ else
+ {
+ this.dhSpec = new DHParameterSpec(params.getP(), params.getG());
+ }
+ }
+ else if (id.equals(X9ObjectIdentifiers.dhpublicnumber))
+ {
+ DHDomainParameters params = DHDomainParameters.getInstance(seq);
+
+ this.dhSpec = new DHParameterSpec(params.getP().getValue(), params.getG().getValue());
+ }
+ else
+ {
+ throw new IllegalArgumentException("unknown algorithm type: " + id);
+ }
+ }
+
+ BCDHPrivateKey(
+ DHPrivateKeyParameters params)
+ {
+ this.x = params.getX();
+ this.dhSpec = new DHParameterSpec(params.getParameters().getP(), params.getParameters().getG(), params.getParameters().getL());
+ }
+
+ public String getAlgorithm()
+ {
+ return "DH";
+ }
+
+ /**
+ * return the encoding format we produce in getEncoded().
+ *
+ * @return the string "PKCS#8"
+ */
+ public String getFormat()
+ {
+ return "PKCS#8";
+ }
+
+ /**
+ * Return a PKCS8 representation of the key. The sequence returned
+ * represents a full PrivateKeyInfo object.
+ *
+ * @return a PKCS8 representation of the key.
+ */
+ public byte[] getEncoded()
+ {
+ try
+ {
+ if (info != null)
+ {
+ return info.getEncoded(ASN1Encoding.DER);
+ }
+
+ PrivateKeyInfo info = new PrivateKeyInfo(new AlgorithmIdentifier(PKCSObjectIdentifiers.dhKeyAgreement, new DHParameter(dhSpec.getP(), dhSpec.getG(), dhSpec.getL()).toASN1Primitive()), new ASN1Integer(getX()));
+
+ return info.getEncoded(ASN1Encoding.DER);
+ }
+ catch (Exception e)
+ {
+ return null;
+ }
+ }
+
+ public DHParameterSpec getParams()
+ {
+ return dhSpec;
+ }
+
+ public BigInteger getX()
+ {
+ return x;
+ }
+
+ public boolean equals(
+ Object o)
+ {
+ if (!(o instanceof DHPrivateKey))
+ {
+ return false;
+ }
+
+ DHPrivateKey other = (DHPrivateKey)o;
+
+ return this.getX().equals(other.getX())
+ && this.getParams().getG().equals(other.getParams().getG())
+ && this.getParams().getP().equals(other.getParams().getP())
+ && this.getParams().getL() == other.getParams().getL();
+ }
+
+ public int hashCode()
+ {
+ return this.getX().hashCode() ^ this.getParams().getG().hashCode()
+ ^ this.getParams().getP().hashCode() ^ this.getParams().getL();
+ }
+
+ public void setBagAttribute(
+ ASN1ObjectIdentifier oid,
+ ASN1Encodable attribute)
+ {
+ attrCarrier.setBagAttribute(oid, attribute);
+ }
+
+ public ASN1Encodable getBagAttribute(
+ ASN1ObjectIdentifier oid)
+ {
+ return attrCarrier.getBagAttribute(oid);
+ }
+
+ public Enumeration getBagAttributeKeys()
+ {
+ return attrCarrier.getBagAttributeKeys();
+ }
+
+ private void readObject(
+ ObjectInputStream in)
+ throws IOException, ClassNotFoundException
+ {
+ in.defaultReadObject();
+
+ this.dhSpec = new DHParameterSpec((BigInteger)in.readObject(), (BigInteger)in.readObject(), in.readInt());
+ this.info = null;
+ this.attrCarrier = new PKCS12BagAttributeCarrierImpl();
+ }
+
+ private void writeObject(
+ ObjectOutputStream out)
+ throws IOException
+ {
+ out.defaultWriteObject();
+
+ out.writeObject(dhSpec.getP());
+ out.writeObject(dhSpec.getG());
+ out.writeInt(dhSpec.getL());
+ }
+}
diff --git a/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/dh/BCDHPublicKey.java b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/dh/BCDHPublicKey.java
new file mode 100644
index 00000000..26d261ab
--- /dev/null
+++ b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/dh/BCDHPublicKey.java
@@ -0,0 +1,204 @@
+package org.spongycastle.jcajce.provider.asymmetric.dh;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.math.BigInteger;
+
+import javax.crypto.interfaces.DHPublicKey;
+import javax.crypto.spec.DHParameterSpec;
+import javax.crypto.spec.DHPublicKeySpec;
+
+import org.spongycastle.asn1.ASN1Integer;
+import org.spongycastle.asn1.ASN1ObjectIdentifier;
+import org.spongycastle.asn1.ASN1Sequence;
+import org.spongycastle.asn1.pkcs.DHParameter;
+import org.spongycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import org.spongycastle.asn1.x509.AlgorithmIdentifier;
+import org.spongycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.spongycastle.asn1.x9.DHDomainParameters;
+import org.spongycastle.asn1.x9.X9ObjectIdentifiers;
+import org.spongycastle.crypto.params.DHPublicKeyParameters;
+import org.spongycastle.jcajce.provider.asymmetric.util.KeyUtil;
+
+public class BCDHPublicKey
+ implements DHPublicKey
+{
+ static final long serialVersionUID = -216691575254424324L;
+
+ private BigInteger y;
+
+ private transient DHParameterSpec dhSpec;
+ private transient SubjectPublicKeyInfo info;
+
+ BCDHPublicKey(
+ DHPublicKeySpec spec)
+ {
+ this.y = spec.getY();
+ this.dhSpec = new DHParameterSpec(spec.getP(), spec.getG());
+ }
+
+ BCDHPublicKey(
+ DHPublicKey key)
+ {
+ this.y = key.getY();
+ this.dhSpec = key.getParams();
+ }
+
+ BCDHPublicKey(
+ DHPublicKeyParameters params)
+ {
+ this.y = params.getY();
+ this.dhSpec = new DHParameterSpec(params.getParameters().getP(), params.getParameters().getG(), params.getParameters().getL());
+ }
+
+ BCDHPublicKey(
+ BigInteger y,
+ DHParameterSpec dhSpec)
+ {
+ this.y = y;
+ this.dhSpec = dhSpec;
+ }
+
+ public BCDHPublicKey(
+ SubjectPublicKeyInfo info)
+ {
+ this.info = info;
+
+ ASN1Integer derY;
+ try
+ {
+ derY = (ASN1Integer)info.parsePublicKey();
+ }
+ catch (IOException e)
+ {
+ throw new IllegalArgumentException("invalid info structure in DH public key");
+ }
+
+ this.y = derY.getValue();
+
+ ASN1Sequence seq = ASN1Sequence.getInstance(info.getAlgorithm().getParameters());
+ ASN1ObjectIdentifier id = info.getAlgorithm().getAlgorithm();
+
+ // we need the PKCS check to handle older keys marked with the X9 oid.
+ if (id.equals(PKCSObjectIdentifiers.dhKeyAgreement) || isPKCSParam(seq))
+ {
+ DHParameter params = DHParameter.getInstance(seq);
+
+ if (params.getL() != null)
+ {
+ this.dhSpec = new DHParameterSpec(params.getP(), params.getG(), params.getL().intValue());
+ }
+ else
+ {
+ this.dhSpec = new DHParameterSpec(params.getP(), params.getG());
+ }
+ }
+ else if (id.equals(X9ObjectIdentifiers.dhpublicnumber))
+ {
+ DHDomainParameters params = DHDomainParameters.getInstance(seq);
+
+ this.dhSpec = new DHParameterSpec(params.getP().getValue(), params.getG().getValue());
+ }
+ else
+ {
+ throw new IllegalArgumentException("unknown algorithm type: " + id);
+ }
+ }
+
+ public String getAlgorithm()
+ {
+ return "DH";
+ }
+
+ public String getFormat()
+ {
+ return "X.509";
+ }
+
+ public byte[] getEncoded()
+ {
+ if (info != null)
+ {
+ return KeyUtil.getEncodedSubjectPublicKeyInfo(info);
+ }
+
+ return KeyUtil.getEncodedSubjectPublicKeyInfo(new AlgorithmIdentifier(PKCSObjectIdentifiers.dhKeyAgreement, new DHParameter(dhSpec.getP(), dhSpec.getG(), dhSpec.getL()).toASN1Primitive()), new ASN1Integer(y));
+ }
+
+ public DHParameterSpec getParams()
+ {
+ return dhSpec;
+ }
+
+ public BigInteger getY()
+ {
+ return y;
+ }
+
+ private boolean isPKCSParam(ASN1Sequence seq)
+ {
+ if (seq.size() == 2)
+ {
+ return true;
+ }
+
+ if (seq.size() > 3)
+ {
+ return false;
+ }
+
+ ASN1Integer l = ASN1Integer.getInstance(seq.getObjectAt(2));
+ ASN1Integer p = ASN1Integer.getInstance(seq.getObjectAt(0));
+
+ if (l.getValue().compareTo(BigInteger.valueOf(p.getValue().bitLength())) > 0)
+ {
+ return false;
+ }
+
+ return true;
+ }
+
+ public int hashCode()
+ {
+ return this.getY().hashCode() ^ this.getParams().getG().hashCode()
+ ^ this.getParams().getP().hashCode() ^ this.getParams().getL();
+ }
+
+ public boolean equals(
+ Object o)
+ {
+ if (!(o instanceof DHPublicKey))
+ {
+ return false;
+ }
+
+ DHPublicKey other = (DHPublicKey)o;
+
+ return this.getY().equals(other.getY())
+ && this.getParams().getG().equals(other.getParams().getG())
+ && this.getParams().getP().equals(other.getParams().getP())
+ && this.getParams().getL() == other.getParams().getL();
+ }
+
+ private void readObject(
+ ObjectInputStream in)
+ throws IOException, ClassNotFoundException
+ {
+ in.defaultReadObject();
+
+ this.dhSpec = new DHParameterSpec((BigInteger)in.readObject(), (BigInteger)in.readObject(), in.readInt());
+ this.info = null;
+ }
+
+ private void writeObject(
+ ObjectOutputStream out)
+ throws IOException
+ {
+ out.defaultWriteObject();
+
+ out.writeObject(dhSpec.getP());
+ out.writeObject(dhSpec.getG());
+ out.writeInt(dhSpec.getL());
+ }
+}
diff --git a/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/dh/IESCipher.java b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/dh/IESCipher.java
new file mode 100644
index 00000000..6857f24a
--- /dev/null
+++ b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/dh/IESCipher.java
@@ -0,0 +1,507 @@
+package org.spongycastle.jcajce.provider.asymmetric.dh;
+
+import java.io.ByteArrayOutputStream;
+import java.security.AlgorithmParameters;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.Key;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.SecureRandom;
+import java.security.spec.AlgorithmParameterSpec;
+
+import javax.crypto.BadPaddingException;
+import javax.crypto.Cipher;
+import javax.crypto.CipherSpi;
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.NoSuchPaddingException;
+import javax.crypto.ShortBufferException;
+import javax.crypto.interfaces.DHKey;
+import javax.crypto.interfaces.DHPrivateKey;
+import javax.crypto.interfaces.DHPublicKey;
+
+import org.spongycastle.crypto.InvalidCipherTextException;
+import org.spongycastle.crypto.KeyEncoder;
+import org.spongycastle.crypto.agreement.DHBasicAgreement;
+import org.spongycastle.crypto.digests.SHA1Digest;
+import org.spongycastle.crypto.engines.AESEngine;
+import org.spongycastle.crypto.engines.DESedeEngine;
+import org.spongycastle.crypto.engines.IESEngine;
+import org.spongycastle.crypto.generators.DHKeyPairGenerator;
+import org.spongycastle.crypto.generators.EphemeralKeyPairGenerator;
+import org.spongycastle.crypto.generators.KDF2BytesGenerator;
+import org.spongycastle.crypto.macs.HMac;
+import org.spongycastle.crypto.paddings.PaddedBufferedBlockCipher;
+import org.spongycastle.crypto.params.AsymmetricKeyParameter;
+import org.spongycastle.crypto.params.DHKeyGenerationParameters;
+import org.spongycastle.crypto.params.DHKeyParameters;
+import org.spongycastle.crypto.params.DHParameters;
+import org.spongycastle.crypto.params.DHPublicKeyParameters;
+import org.spongycastle.crypto.params.IESParameters;
+import org.spongycastle.crypto.params.IESWithCipherParameters;
+import org.spongycastle.crypto.parsers.DHIESPublicKeyParser;
+import org.spongycastle.jcajce.provider.asymmetric.util.DHUtil;
+import org.spongycastle.jcajce.provider.asymmetric.util.IESUtil;
+import org.spongycastle.jce.interfaces.IESKey;
+import org.spongycastle.jce.provider.BouncyCastleProvider;
+import org.spongycastle.jce.spec.IESParameterSpec;
+import org.spongycastle.util.BigIntegers;
+import org.spongycastle.util.Strings;
+
+
+public class IESCipher
+ extends CipherSpi
+{
+ private IESEngine engine;
+ private int state = -1;
+ private ByteArrayOutputStream buffer = new ByteArrayOutputStream();
+ private AlgorithmParameters engineParam = null;
+ private IESParameterSpec engineSpec = null;
+ private AsymmetricKeyParameter key;
+ private SecureRandom random;
+ private boolean dhaesMode = false;
+ private AsymmetricKeyParameter otherKeyParameter = null;
+
+ public IESCipher(IESEngine engine)
+ {
+ this.engine = engine;
+ }
+
+
+ public int engineGetBlockSize()
+ {
+ if (engine.getCipher() != null)
+ {
+ return engine.getCipher().getBlockSize();
+ }
+ else
+ {
+ return 0;
+ }
+ }
+
+
+ public int engineGetKeySize(Key key)
+ {
+ if (key instanceof DHKey)
+ {
+ return ((DHKey)key).getParams().getP().bitLength();
+ }
+ else
+ {
+ throw new IllegalArgumentException("not a DH key");
+ }
+ }
+
+
+ public byte[] engineGetIV()
+ {
+ return null;
+ }
+
+ public AlgorithmParameters engineGetParameters()
+ {
+ if (engineParam == null && engineSpec != null)
+ {
+ try
+ {
+ engineParam = AlgorithmParameters.getInstance("IES", BouncyCastleProvider.PROVIDER_NAME);
+ engineParam.init(engineSpec);
+ }
+ catch (Exception e)
+ {
+ throw new RuntimeException(e.toString());
+ }
+ }
+
+ return engineParam;
+ }
+
+
+ public void engineSetMode(String mode)
+ throws NoSuchAlgorithmException
+ {
+ String modeName = Strings.toUpperCase(mode);
+
+ if (modeName.equals("NONE"))
+ {
+ dhaesMode = false;
+ }
+ else if (modeName.equals("DHAES"))
+ {
+ dhaesMode = true;
+ }
+ else
+ {
+ throw new IllegalArgumentException("can't support mode " + mode);
+ }
+ }
+
+ public int engineGetOutputSize(int inputLen)
+ {
+ int len1, len2, len3;
+
+ len1 = engine.getMac().getMacSize();
+
+ if (key != null)
+ {
+ len2 = ((DHKey)key).getParams().getP().bitLength() / 8 + 1;
+ }
+ else
+ {
+ throw new IllegalStateException("cipher not initialised");
+ }
+
+ if (engine.getCipher() == null)
+ {
+ len3 = inputLen;
+ }
+ else if (state == Cipher.ENCRYPT_MODE || state == Cipher.WRAP_MODE)
+ {
+ len3 = engine.getCipher().getOutputSize(inputLen);
+ }
+ else if (state == Cipher.DECRYPT_MODE || state == Cipher.UNWRAP_MODE)
+ {
+ len3 = engine.getCipher().getOutputSize(inputLen - len1 - len2);
+ }
+ else
+ {
+ throw new IllegalStateException("cipher not initialised");
+ }
+
+ if (state == Cipher.ENCRYPT_MODE || state == Cipher.WRAP_MODE)
+ {
+ return buffer.size() + len1 + len2 + len3;
+ }
+ else if (state == Cipher.DECRYPT_MODE || state == Cipher.UNWRAP_MODE)
+ {
+ return buffer.size() - len1 - len2 + len3;
+ }
+ else
+ {
+ throw new IllegalStateException("IESCipher not initialised");
+ }
+
+ }
+
+ public void engineSetPadding(String padding)
+ throws NoSuchPaddingException
+ {
+ String paddingName = Strings.toUpperCase(padding);
+
+ // TDOD: make this meaningful...
+ if (paddingName.equals("NOPADDING"))
+ {
+
+ }
+ else if (paddingName.equals("PKCS5PADDING") || paddingName.equals("PKCS7PADDING"))
+ {
+
+ }
+ else
+ {
+ throw new NoSuchPaddingException("padding not available with IESCipher");
+ }
+ }
+
+ // Initialisation methods
+
+ public void engineInit(
+ int opmode,
+ Key key,
+ AlgorithmParameters params,
+ SecureRandom random)
+ throws InvalidKeyException, InvalidAlgorithmParameterException
+ {
+ AlgorithmParameterSpec paramSpec = null;
+
+ if (params != null)
+ {
+ try
+ {
+ paramSpec = params.getParameterSpec(IESParameterSpec.class);
+ }
+ catch (Exception e)
+ {
+ throw new InvalidAlgorithmParameterException("cannot recognise parameters: " + e.toString());
+ }
+ }
+
+ engineParam = params;
+ engineInit(opmode, key, paramSpec, random);
+ }
+
+
+ public void engineInit(
+ int opmode,
+ Key key,
+ AlgorithmParameterSpec engineSpec,
+ SecureRandom random)
+ throws InvalidAlgorithmParameterException, InvalidKeyException
+ {
+ // Use default parameters (including cipher key size) if none are specified
+ if (engineSpec == null)
+ {
+ this.engineSpec = IESUtil.guessParameterSpec(engine);
+ }
+ else if (engineSpec instanceof IESParameterSpec)
+ {
+ this.engineSpec = (IESParameterSpec)engineSpec;
+ }
+ else
+ {
+ throw new InvalidAlgorithmParameterException("must be passed IES parameters");
+ }
+
+ // Parse the recipient's key
+ if (opmode == Cipher.ENCRYPT_MODE || opmode == Cipher.WRAP_MODE)
+ {
+ if (key instanceof DHPublicKey)
+ {
+ this.key = DHUtil.generatePublicKeyParameter((PublicKey)key);
+ }
+ else if (key instanceof IESKey)
+ {
+ IESKey ieKey = (IESKey)key;
+
+ this.key = DHUtil.generatePublicKeyParameter(ieKey.getPublic());
+ this.otherKeyParameter = DHUtil.generatePrivateKeyParameter(ieKey.getPrivate());
+ }
+ else
+ {
+ throw new InvalidKeyException("must be passed recipient's public DH key for encryption");
+ }
+ }
+ else if (opmode == Cipher.DECRYPT_MODE || opmode == Cipher.UNWRAP_MODE)
+ {
+ if (key instanceof DHPrivateKey)
+ {
+ this.key = DHUtil.generatePrivateKeyParameter((PrivateKey)key);
+ }
+ else if (key instanceof IESKey)
+ {
+ IESKey ieKey = (IESKey)key;
+
+ this.otherKeyParameter = DHUtil.generatePublicKeyParameter(ieKey.getPublic());
+ this.key = DHUtil.generatePrivateKeyParameter(ieKey.getPrivate());
+ }
+ else
+ {
+ throw new InvalidKeyException("must be passed recipient's private DH key for decryption");
+ }
+ }
+ else
+ {
+ throw new InvalidKeyException("must be passed EC key");
+ }
+
+ this.random = random;
+ this.state = opmode;
+ buffer.reset();
+
+ }
+
+
+ public void engineInit(
+ int opmode,
+ Key key,
+ SecureRandom random)
+ throws InvalidKeyException
+ {
+ try
+ {
+ engineInit(opmode, key, (AlgorithmParameterSpec)null, random);
+ }
+ catch (InvalidAlgorithmParameterException e)
+ {
+ throw new IllegalArgumentException("can't handle supplied parameter spec");
+ }
+
+ }
+
+
+ // Update methods - buffer the input
+
+ public byte[] engineUpdate(
+ byte[] input,
+ int inputOffset,
+ int inputLen)
+ {
+ buffer.write(input, inputOffset, inputLen);
+ return null;
+ }
+
+
+ public int engineUpdate(
+ byte[] input,
+ int inputOffset,
+ int inputLen,
+ byte[] output,
+ int outputOffset)
+ {
+ buffer.write(input, inputOffset, inputLen);
+ return 0;
+ }
+
+
+ // Finalisation methods
+
+ public byte[] engineDoFinal(
+ byte[] input,
+ int inputOffset,
+ int inputLen)
+ throws IllegalBlockSizeException, BadPaddingException
+ {
+ if (inputLen != 0)
+ {
+ buffer.write(input, inputOffset, inputLen);
+ }
+
+ byte[] in = buffer.toByteArray();
+ buffer.reset();
+
+ // Convert parameters for use in IESEngine
+ IESParameters params = new IESWithCipherParameters(engineSpec.getDerivationV(),
+ engineSpec.getEncodingV(),
+ engineSpec.getMacKeySize(),
+ engineSpec.getCipherKeySize());
+
+ DHParameters dhParams = ((DHKeyParameters)key).getParameters();
+
+ byte[] V;
+ if (otherKeyParameter != null)
+ {
+ try
+ {
+ if (state == Cipher.ENCRYPT_MODE || state == Cipher.WRAP_MODE)
+ {
+ engine.init(true, otherKeyParameter, key, params);
+ }
+ else
+ {
+ engine.init(false, key, otherKeyParameter, params);
+ }
+ return engine.processBlock(in, 0, in.length);
+ }
+ catch (Exception e)
+ {
+ throw new BadPaddingException(e.getMessage());
+ }
+ }
+
+ if (state == Cipher.ENCRYPT_MODE || state == Cipher.WRAP_MODE)
+ {
+ // Generate the ephemeral key pair
+ DHKeyPairGenerator gen = new DHKeyPairGenerator();
+ gen.init(new DHKeyGenerationParameters(random, dhParams));
+
+ EphemeralKeyPairGenerator kGen = new EphemeralKeyPairGenerator(gen, new KeyEncoder()
+ {
+ public byte[] getEncoded(AsymmetricKeyParameter keyParameter)
+ {
+ byte[] Vloc = new byte[(((DHKeyParameters)keyParameter).getParameters().getP().bitLength() + 7) / 8];
+ byte[] Vtmp = BigIntegers.asUnsignedByteArray(((DHPublicKeyParameters)keyParameter).getY());
+
+ if (Vtmp.length > Vloc.length)
+ {
+ throw new IllegalArgumentException("Senders's public key longer than expected.");
+ }
+ else
+ {
+ System.arraycopy(Vtmp, 0, Vloc, Vloc.length - Vtmp.length, Vtmp.length);
+ }
+
+ return Vloc;
+ }
+ });
+
+ // Encrypt the buffer
+ try
+ {
+ engine.init(key, params, kGen);
+
+ return engine.processBlock(in, 0, in.length);
+ }
+ catch (Exception e)
+ {
+ throw new BadPaddingException(e.getMessage());
+ }
+ }
+ else if (state == Cipher.DECRYPT_MODE || state == Cipher.UNWRAP_MODE)
+ {
+ // Decrypt the buffer
+ try
+ {
+ engine.init(key, params, new DHIESPublicKeyParser(((DHKeyParameters)key).getParameters()));
+
+ return engine.processBlock(in, 0, in.length);
+ }
+ catch (InvalidCipherTextException e)
+ {
+ throw new BadPaddingException(e.getMessage());
+ }
+ }
+ else
+ {
+ throw new IllegalStateException("IESCipher not initialised");
+ }
+
+ }
+
+
+ public int engineDoFinal(
+ byte[] input,
+ int inputOffset,
+ int inputLength,
+ byte[] output,
+ int outputOffset)
+ throws ShortBufferException, IllegalBlockSizeException, BadPaddingException
+ {
+
+ byte[] buf = engineDoFinal(input, inputOffset, inputLength);
+ System.arraycopy(buf, 0, output, outputOffset, buf.length);
+ return buf.length;
+
+ }
+
+
+ /**
+ * Classes that inherit from us
+ */
+
+ static public class IES
+ extends IESCipher
+ {
+ public IES()
+ {
+ super(new IESEngine(new DHBasicAgreement(),
+ new KDF2BytesGenerator(new SHA1Digest()),
+ new HMac(new SHA1Digest())));
+ }
+ }
+
+ static public class IESwithDESede
+ extends IESCipher
+ {
+ public IESwithDESede()
+ {
+ super(new IESEngine(new DHBasicAgreement(),
+ new KDF2BytesGenerator(new SHA1Digest()),
+ new HMac(new SHA1Digest()),
+ new PaddedBufferedBlockCipher(new DESedeEngine())));
+ }
+ }
+
+ static public class IESwithAES
+ extends IESCipher
+ {
+ public IESwithAES()
+ {
+ super(new IESEngine(new DHBasicAgreement(),
+ new KDF2BytesGenerator(new SHA1Digest()),
+ new HMac(new SHA1Digest()),
+ new PaddedBufferedBlockCipher(new AESEngine())));
+ }
+ }
+}
diff --git a/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/dh/KeyAgreementSpi.java b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/dh/KeyAgreementSpi.java
new file mode 100644
index 00000000..63127236
--- /dev/null
+++ b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/dh/KeyAgreementSpi.java
@@ -0,0 +1,227 @@
+package org.spongycastle.jcajce.provider.asymmetric.dh;
+
+import java.math.BigInteger;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.Key;
+import java.security.SecureRandom;
+import java.security.spec.AlgorithmParameterSpec;
+import java.util.Hashtable;
+
+import javax.crypto.SecretKey;
+import javax.crypto.ShortBufferException;
+import javax.crypto.interfaces.DHPrivateKey;
+import javax.crypto.interfaces.DHPublicKey;
+import javax.crypto.spec.DHParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
+
+import org.spongycastle.crypto.params.DESParameters;
+import org.spongycastle.util.Integers;
+import org.spongycastle.util.Strings;
+
+/**
+ * Diffie-Hellman key agreement. There's actually a better way of doing this
+ * if you are using long term public keys, see the light-weight version for
+ * details.
+ */
+public class KeyAgreementSpi
+ extends javax.crypto.KeyAgreementSpi
+{
+ private BigInteger x;
+ private BigInteger p;
+ private BigInteger g;
+ private BigInteger result;
+
+ private static final Hashtable algorithms = new Hashtable();
+
+ static
+ {
+ Integer i64 = Integers.valueOf(64);
+ Integer i192 = Integers.valueOf(192);
+ Integer i128 = Integers.valueOf(128);
+ Integer i256 = Integers.valueOf(256);
+
+ algorithms.put("DES", i64);
+ algorithms.put("DESEDE", i192);
+ algorithms.put("BLOWFISH", i128);
+ algorithms.put("AES", i256);
+ }
+
+ private byte[] bigIntToBytes(
+ BigInteger r)
+ {
+ //
+ // RFC 2631 (2.1.2) specifies that the secret should be padded with leading zeros if necessary
+ // must be the same length as p
+ //
+ int expectedLength = (p.bitLength() + 7) / 8;
+
+ byte[] tmp = r.toByteArray();
+
+ if (tmp.length == expectedLength)
+ {
+ return tmp;
+ }
+
+ if (tmp[0] == 0 && tmp.length == expectedLength + 1)
+ {
+ byte[] rv = new byte[tmp.length - 1];
+
+ System.arraycopy(tmp, 1, rv, 0, rv.length);
+ return rv;
+ }
+
+ // tmp must be shorter than expectedLength
+ // pad to the left with zeros.
+ byte[] rv = new byte[expectedLength];
+
+ System.arraycopy(tmp, 0, rv, rv.length - tmp.length, tmp.length);
+
+ return rv;
+ }
+
+ protected Key engineDoPhase(
+ Key key,
+ boolean lastPhase)
+ throws InvalidKeyException, IllegalStateException
+ {
+ if (x == null)
+ {
+ throw new IllegalStateException("Diffie-Hellman not initialised.");
+ }
+
+ if (!(key instanceof DHPublicKey))
+ {
+ throw new InvalidKeyException("DHKeyAgreement doPhase requires DHPublicKey");
+ }
+ DHPublicKey pubKey = (DHPublicKey)key;
+
+ if (!pubKey.getParams().getG().equals(g) || !pubKey.getParams().getP().equals(p))
+ {
+ throw new InvalidKeyException("DHPublicKey not for this KeyAgreement!");
+ }
+
+ if (lastPhase)
+ {
+ result = ((DHPublicKey)key).getY().modPow(x, p);
+ return null;
+ }
+ else
+ {
+ result = ((DHPublicKey)key).getY().modPow(x, p);
+ }
+
+ return new BCDHPublicKey(result, pubKey.getParams());
+ }
+
+ protected byte[] engineGenerateSecret()
+ throws IllegalStateException
+ {
+ if (x == null)
+ {
+ throw new IllegalStateException("Diffie-Hellman not initialised.");
+ }
+
+ return bigIntToBytes(result);
+ }
+
+ protected int engineGenerateSecret(
+ byte[] sharedSecret,
+ int offset)
+ throws IllegalStateException, ShortBufferException
+ {
+ if (x == null)
+ {
+ throw new IllegalStateException("Diffie-Hellman not initialised.");
+ }
+
+ byte[] secret = bigIntToBytes(result);
+
+ if (sharedSecret.length - offset < secret.length)
+ {
+ throw new ShortBufferException("DHKeyAgreement - buffer too short");
+ }
+
+ System.arraycopy(secret, 0, sharedSecret, offset, secret.length);
+
+ return secret.length;
+ }
+
+ protected SecretKey engineGenerateSecret(
+ String algorithm)
+ {
+ if (x == null)
+ {
+ throw new IllegalStateException("Diffie-Hellman not initialised.");
+ }
+
+ String algKey = Strings.toUpperCase(algorithm);
+ byte[] res = bigIntToBytes(result);
+
+ if (algorithms.containsKey(algKey))
+ {
+ Integer length = (Integer)algorithms.get(algKey);
+
+ byte[] key = new byte[length.intValue() / 8];
+ System.arraycopy(res, 0, key, 0, key.length);
+
+ if (algKey.startsWith("DES"))
+ {
+ DESParameters.setOddParity(key);
+ }
+
+ return new SecretKeySpec(key, algorithm);
+ }
+
+ return new SecretKeySpec(res, algorithm);
+ }
+
+ protected void engineInit(
+ Key key,
+ AlgorithmParameterSpec params,
+ SecureRandom random)
+ throws InvalidKeyException, InvalidAlgorithmParameterException
+ {
+ if (!(key instanceof DHPrivateKey))
+ {
+ throw new InvalidKeyException("DHKeyAgreement requires DHPrivateKey for initialisation");
+ }
+ DHPrivateKey privKey = (DHPrivateKey)key;
+
+ if (params != null)
+ {
+ if (!(params instanceof DHParameterSpec))
+ {
+ throw new InvalidAlgorithmParameterException("DHKeyAgreement only accepts DHParameterSpec");
+ }
+ DHParameterSpec p = (DHParameterSpec)params;
+
+ this.p = p.getP();
+ this.g = p.getG();
+ }
+ else
+ {
+ this.p = privKey.getParams().getP();
+ this.g = privKey.getParams().getG();
+ }
+
+ this.x = this.result = privKey.getX();
+ }
+
+ protected void engineInit(
+ Key key,
+ SecureRandom random)
+ throws InvalidKeyException
+ {
+ if (!(key instanceof DHPrivateKey))
+ {
+ throw new InvalidKeyException("DHKeyAgreement requires DHPrivateKey");
+ }
+
+ DHPrivateKey privKey = (DHPrivateKey)key;
+
+ this.p = privKey.getParams().getP();
+ this.g = privKey.getParams().getG();
+ this.x = this.result = privKey.getX();
+ }
+}
diff --git a/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/dh/KeyFactorySpi.java b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/dh/KeyFactorySpi.java
new file mode 100644
index 00000000..92ccdd3e
--- /dev/null
+++ b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/dh/KeyFactorySpi.java
@@ -0,0 +1,128 @@
+package org.spongycastle.jcajce.provider.asymmetric.dh;
+
+import java.io.IOException;
+import java.security.InvalidKeyException;
+import java.security.Key;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.KeySpec;
+
+import javax.crypto.interfaces.DHPrivateKey;
+import javax.crypto.interfaces.DHPublicKey;
+import javax.crypto.spec.DHPrivateKeySpec;
+import javax.crypto.spec.DHPublicKeySpec;
+
+import org.spongycastle.asn1.ASN1ObjectIdentifier;
+import org.spongycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import org.spongycastle.asn1.pkcs.PrivateKeyInfo;
+import org.spongycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.spongycastle.asn1.x9.X9ObjectIdentifiers;
+import org.spongycastle.jcajce.provider.asymmetric.util.BaseKeyFactorySpi;
+
+public class KeyFactorySpi
+ extends BaseKeyFactorySpi
+{
+ public KeyFactorySpi()
+ {
+ }
+
+ protected KeySpec engineGetKeySpec(
+ Key key,
+ Class spec)
+ throws InvalidKeySpecException
+ {
+ if (spec.isAssignableFrom(DHPrivateKeySpec.class) && key instanceof DHPrivateKey)
+ {
+ DHPrivateKey k = (DHPrivateKey)key;
+
+ return new DHPrivateKeySpec(k.getX(), k.getParams().getP(), k.getParams().getG());
+ }
+ else if (spec.isAssignableFrom(DHPublicKeySpec.class) && key instanceof DHPublicKey)
+ {
+ DHPublicKey k = (DHPublicKey)key;
+
+ return new DHPublicKeySpec(k.getY(), k.getParams().getP(), k.getParams().getG());
+ }
+
+ return super.engineGetKeySpec(key, spec);
+ }
+
+ protected Key engineTranslateKey(
+ Key key)
+ throws InvalidKeyException
+ {
+ if (key instanceof DHPublicKey)
+ {
+ return new BCDHPublicKey((DHPublicKey)key);
+ }
+ else if (key instanceof DHPrivateKey)
+ {
+ return new BCDHPrivateKey((DHPrivateKey)key);
+ }
+
+ throw new InvalidKeyException("key type unknown");
+ }
+
+ protected PrivateKey engineGeneratePrivate(
+ KeySpec keySpec)
+ throws InvalidKeySpecException
+ {
+ if (keySpec instanceof DHPrivateKeySpec)
+ {
+ return new BCDHPrivateKey((DHPrivateKeySpec)keySpec);
+ }
+
+ return super.engineGeneratePrivate(keySpec);
+ }
+
+ protected PublicKey engineGeneratePublic(
+ KeySpec keySpec)
+ throws InvalidKeySpecException
+ {
+ if (keySpec instanceof DHPublicKeySpec)
+ {
+ return new BCDHPublicKey((DHPublicKeySpec)keySpec);
+ }
+
+ return super.engineGeneratePublic(keySpec);
+ }
+
+ public PrivateKey generatePrivate(PrivateKeyInfo keyInfo)
+ throws IOException
+ {
+ ASN1ObjectIdentifier algOid = keyInfo.getPrivateKeyAlgorithm().getAlgorithm();
+
+ if (algOid.equals(PKCSObjectIdentifiers.dhKeyAgreement))
+ {
+ return new BCDHPrivateKey(keyInfo);
+ }
+ else if (algOid.equals(X9ObjectIdentifiers.dhpublicnumber))
+ {
+ return new BCDHPrivateKey(keyInfo);
+ }
+ else
+ {
+ throw new IOException("algorithm identifier " + algOid + " in key not recognised");
+ }
+ }
+
+ public PublicKey generatePublic(SubjectPublicKeyInfo keyInfo)
+ throws IOException
+ {
+ ASN1ObjectIdentifier algOid = keyInfo.getAlgorithm().getAlgorithm();
+
+ if (algOid.equals(PKCSObjectIdentifiers.dhKeyAgreement))
+ {
+ return new BCDHPublicKey(keyInfo);
+ }
+ else if (algOid.equals(X9ObjectIdentifiers.dhpublicnumber))
+ {
+ return new BCDHPublicKey(keyInfo);
+ }
+ else
+ {
+ throw new IOException("algorithm identifier " + algOid + " in key not recognised");
+ }
+ }
+}
diff --git a/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/dh/KeyPairGeneratorSpi.java b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/dh/KeyPairGeneratorSpi.java
new file mode 100644
index 00000000..67b0d5dc
--- /dev/null
+++ b/prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/dh/KeyPairGeneratorSpi.java
@@ -0,0 +1,119 @@
+package org.spongycastle.jcajce.provider.asymmetric.dh;
+
+import java.security.InvalidAlgorithmParameterException;
+import java.security.KeyPair;
+import java.security.SecureRandom;
+import java.security.spec.AlgorithmParameterSpec;
+import java.util.Hashtable;
+
+import javax.crypto.spec.DHParameterSpec;
+
+import org.spongycastle.crypto.AsymmetricCipherKeyPair;
+import org.spongycastle.crypto.generators.DHBasicKeyPairGenerator;
+import org.spongycastle.crypto.generators.DHParametersGenerator;
+import org.spongycastle.crypto.params.DHKeyGenerationParameters;
+import org.spongycastle.crypto.params.DHParameters;
+import org.spongycastle.crypto.params.DHPrivateKeyParameters;
+import org.spongycastle.crypto.params.DHPublicKeyParameters;
+import org.spongycastle.jce.provider.BouncyCastleProvider;
+import org.spongycastle.util.Integers;
+
+public class KeyPairGeneratorSpi
+ extends java.security.KeyPairGenerator
+{
+ private static Hashtable params = new Hashtable();
+ private static Object lock = new Object();
+
+ DHKeyGenerationParameters param;
+ DHBasicKeyPairGenerator engine = new DHBasicKeyPairGenerator();
+ int strength = 1024;
+ int certainty = 20;
+ SecureRandom random = new SecureRandom();
+ boolean initialised = false;
+
+ public KeyPairGeneratorSpi()
+ {
+ super("DH");
+ }
+
+ public void initialize(
+ int strength,
+ SecureRandom random)
+ {
+ this.strength = strength;
+ this.random = random;
+ }
+
+ public void initialize(
+ AlgorithmParameterSpec params,
+ SecureRandom random)
+ throws InvalidAlgorithmParameterException
+ {
+ if (!(params instanceof DHParameterSpec))
+ {
+ throw new InvalidAlgorithmParameterException("parameter object not a DHParameterSpec");
+ }
+ DHParameterSpec dhParams = (DHParameterSpec)params;
+
+ param = new DHKeyGenerationParameters(random, new DHParameters(dhParams.getP(), dhParams.getG(), null, dhParams.getL()));
+
+ engine.init(param);
+ initialised = true;
+ }
+
+ public KeyPair generateKeyPair()
+ {
+ if (!initialised)
+ {
+ Integer paramStrength = Integers.valueOf(strength);
+
+ if (params.containsKey(paramStrength))
+ {
+ param = (DHKeyGenerationParameters)params.get(paramStrength);
+ }
+ else
+ {
+ DHParameterSpec dhParams = BouncyCastleProvider.CONFIGURATION.getDHDefaultParameters(strength);
+
+ if (dhParams != null)
+ {
+ param = new DHKeyGenerationParameters(random, new DHParameters(dhParams.getP(), dhParams.getG(), null, dhParams.getL()));
+ }
+ else
+ {
+ synchronized (lock)
+ {
+ // we do the check again in case we were blocked by a generator for
+ // our key size.
+ if (params.containsKey(paramStrength))
+ {
+ param = (DHKeyGenerationParameters)params.get(paramStrength);
+ }
+ else
+ {
+
+ DHParametersGenerator pGen = new DHParametersGenerator();
+
+ pGen.init(strength, certainty, random);
+
+ param = new DHKeyGenerationParameters(random, pGen.generateParameters());
+
+ params.put(paramStrength, param);
+ }
+ }
+ }
+ }
+
+ engine.init(param);
+
+ initialised = true;
+ }
+
+ AsymmetricCipherKeyPair pair = engine.generateKeyPair();
+ DHPublicKeyParameters pub = (DHPublicKeyParameters)pair.getPublic();
+ DHPrivateKeyParameters priv = (DHPrivateKeyParameters)pair.getPrivate();
+
+ return new KeyPair(new BCDHPublicKey(pub),
+ new BCDHPrivateKey(priv));
+ }
+}