package org.bouncycastle.crypto.agreement;
import java.math.BigInteger;
import java.security.SecureRandom;
import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
import org.bouncycastle.crypto.CipherParameters;
import org.bouncycastle.crypto.CryptoServicesRegistrar;
import org.bouncycastle.crypto.generators.DHKeyPairGenerator;
import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
import org.bouncycastle.crypto.params.DHKeyGenerationParameters;
import org.bouncycastle.crypto.params.DHParameters;
import org.bouncycastle.crypto.params.DHPrivateKeyParameters;
import org.bouncycastle.crypto.params.DHPublicKeyParameters;
import org.bouncycastle.crypto.params.ParametersWithRandom;
a Diffie-Hellman key exchange engine.
note: This uses MTI/A0 key agreement in order to make the key agreement
secure against passive attacks. If you're doing Diffie-Hellman and both
parties have long term public keys you should look at using this. For
further information have a look at RFC 2631.
It's possible to extend this to more than two parties as well, for the moment
that is left as an exercise for the reader.
/**
* a Diffie-Hellman key exchange engine.
* <p>
* note: This uses MTI/A0 key agreement in order to make the key agreement
* secure against passive attacks. If you're doing Diffie-Hellman and both
* parties have long term public keys you should look at using this. For
* further information have a look at RFC 2631.
* <p>
* It's possible to extend this to more than two parties as well, for the moment
* that is left as an exercise for the reader.
*/
public class DHAgreement
{
private static final BigInteger ONE = BigInteger.valueOf(1);
private DHPrivateKeyParameters key;
private DHParameters dhParams;
private BigInteger privateValue;
private SecureRandom random;
public void init(
CipherParameters param)
{
AsymmetricKeyParameter kParam;
if (param instanceof ParametersWithRandom)
{
ParametersWithRandom rParam = (ParametersWithRandom)param;
this.random = rParam.getRandom();
kParam = (AsymmetricKeyParameter)rParam.getParameters();
}
else
{
this.random = CryptoServicesRegistrar.getSecureRandom();
kParam = (AsymmetricKeyParameter)param;
}
if (!(kParam instanceof DHPrivateKeyParameters))
{
throw new IllegalArgumentException("DHEngine expects DHPrivateKeyParameters");
}
this.key = (DHPrivateKeyParameters)kParam;
this.dhParams = key.getParameters();
}
calculate our initial message.
/**
* calculate our initial message.
*/
public BigInteger calculateMessage()
{
DHKeyPairGenerator dhGen = new DHKeyPairGenerator();
dhGen.init(new DHKeyGenerationParameters(random, dhParams));
AsymmetricCipherKeyPair dhPair = dhGen.generateKeyPair();
this.privateValue = ((DHPrivateKeyParameters)dhPair.getPrivate()).getX();
return ((DHPublicKeyParameters)dhPair.getPublic()).getY();
}
given a message from a given party and the corresponding public key,
calculate the next message in the agreement sequence. In this case
this will represent the shared secret.
/**
* given a message from a given party and the corresponding public key,
* calculate the next message in the agreement sequence. In this case
* this will represent the shared secret.
*/
public BigInteger calculateAgreement(
DHPublicKeyParameters pub,
BigInteger message)
{
if (!pub.getParameters().equals(dhParams))
{
throw new IllegalArgumentException("Diffie-Hellman public key has wrong parameters.");
}
BigInteger p = dhParams.getP();
BigInteger peerY = pub.getY();
if (peerY == null || peerY.compareTo(ONE) <= 0 || peerY.compareTo(p.subtract(ONE)) >= 0)
{
throw new IllegalArgumentException("Diffie-Hellman public key is weak");
}
BigInteger result = peerY.modPow(privateValue, p);
if (result.equals(ONE))
{
throw new IllegalStateException("Shared key can't be 1");
}
return message.modPow(key.getX(), p).multiply(result).mod(p);
}
}