diff options
Diffstat (limited to 'prov/src/main/java/org/spongycastle/jcajce/provider/asymmetric/dh')
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)); + } +} |