blob: 059fa19a501856ddb0179bde40f333b5b36e4977 [file] [log] [blame]
package org.bouncycastle.jce.provider.test;
import java.security.InvalidAlgorithmParameterException;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.SecureRandom;
import java.security.Security;
import java.security.spec.ECGenParameterSpec;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.SealedObject;
import org.bouncycastle.crypto.agreement.ECDHBasicAgreement;
import org.bouncycastle.crypto.digests.SHA1Digest;
import org.bouncycastle.crypto.engines.DESEngine;
import org.bouncycastle.crypto.engines.IESEngine;
import org.bouncycastle.crypto.generators.KDF2BytesGenerator;
import org.bouncycastle.crypto.macs.HMac;
import org.bouncycastle.crypto.paddings.PaddedBufferedBlockCipher;
import org.bouncycastle.jcajce.provider.asymmetric.ec.IESCipher;
import org.bouncycastle.jce.interfaces.ECPrivateKey;
import org.bouncycastle.jce.interfaces.ECPublicKey;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.jce.spec.IESParameterSpec;
import org.bouncycastle.util.Arrays;
import org.bouncycastle.util.encoders.Hex;
import org.bouncycastle.util.test.SimpleTest;
/**
* Test for ECIES - Elliptic Curve Integrated Encryption Scheme
*/
public class ECIESTest
extends SimpleTest
{
ECIESTest()
{
}
public String getName()
{
return "ECIES";
}
public void performTest()
throws Exception
{
byte[] derivation = Hex.decode("202122232425262728292a2b2c2d2e2f");
byte[] encoding = Hex.decode("303132333435363738393a3b3c3d3e3f");
IESCipher c1 = new org.bouncycastle.jcajce.provider.asymmetric.ec.IESCipher.ECIES();
IESCipher c2 = new org.bouncycastle.jcajce.provider.asymmetric.ec.IESCipher.ECIES();
IESParameterSpec params = new IESParameterSpec(derivation,encoding,128);
// Testing ECIES with default curve in streaming mode
KeyPairGenerator g = KeyPairGenerator.getInstance("EC", "BC");
doTest("ECIES with default", g, "ECIES", params);
// Testing ECIES with 192-bit curve in streaming mode
g.initialize(192, new SecureRandom());
doTest("ECIES with 192-bit", g, "ECIES", params);
// Testing ECIES with 256-bit curve in streaming mode
g.initialize(256, new SecureRandom());
doTest("ECIES with 256-bit", g, "ECIES", params);
c1 = new IESCipher(new IESEngine(new ECDHBasicAgreement(),
new KDF2BytesGenerator(new SHA1Digest()),
new HMac(new SHA1Digest()),
new PaddedBufferedBlockCipher(new DESEngine())));
c2 = new IESCipher(new IESEngine(new ECDHBasicAgreement(),
new KDF2BytesGenerator(new SHA1Digest()),
new HMac(new SHA1Digest()),
new PaddedBufferedBlockCipher(new DESEngine())));
params = new IESParameterSpec(derivation, encoding, 128, 128, Hex.decode("0001020304050607"));
// Testing ECIES with default curve using DES
g = KeyPairGenerator.getInstance("EC", "BC");
// Testing ECIES with 256-bit curve using DES-CBC
g.initialize(256, new SecureRandom());
doTest("256-bit", g, "ECIESwithDESEDE-CBC", params);
params = new IESParameterSpec(derivation, encoding, 128, 128, Hex.decode("0001020304050607"));
g.initialize(256, new SecureRandom());
doTest("256-bit", g, "ECIESwithDESEDE-CBC", params);
try
{
params = new IESParameterSpec(derivation, encoding, 128, 128, new byte[10]);
g.initialize(256, new SecureRandom());
doTest("256-bit", g, "ECIESwithDESEDE-CBC", params);
fail("DESEDE no exception!");
}
catch (InvalidAlgorithmParameterException e)
{
if (!e.getMessage().equals("NONCE in IES Parameters needs to be 8 bytes long"))
{
fail("DESEDE wrong message!");
}
}
c1 = new org.bouncycastle.jcajce.provider.asymmetric.ec.IESCipher.ECIESwithAESCBC();
c2 = new org.bouncycastle.jcajce.provider.asymmetric.ec.IESCipher.ECIESwithAESCBC();
params = new IESParameterSpec(derivation, encoding, 128, 128, Hex.decode("000102030405060708090a0b0c0d0e0f"));
// Testing ECIES with 256-bit curve using AES-CBC
g.initialize(256, new SecureRandom());
doTest("256-bit", g, "ECIESwithAES-CBC", params);
params = new IESParameterSpec(derivation, encoding, 128, 128, Hex.decode("000102030405060708090a0b0c0d0e0f"));
g.initialize(256, new SecureRandom());
doTest("256-bit", g, "ECIESwithAES-CBC", params);
try
{
params = new IESParameterSpec(derivation, encoding, 128, 128, new byte[10]);
g.initialize(256, new SecureRandom());
doTest("256-bit", g, "ECIESwithAES-CBC", params);
fail("AES no exception!");
}
catch (InvalidAlgorithmParameterException e)
{
if (!e.getMessage().equals("NONCE in IES Parameters needs to be 16 bytes long"))
{
fail("AES wrong message!");
}
}
KeyPair keyPair = g.generateKeyPair();
ECPublicKey pub = (ECPublicKey)keyPair.getPublic();
ECPrivateKey priv = (ECPrivateKey)keyPair.getPrivate();
Cipher c = Cipher.getInstance("ECIESwithAES-CBC", "BC");
try
{
c.init(Cipher.ENCRYPT_MODE, pub, new IESParameterSpec(derivation, encoding, 128, 128, null));
fail("no exception");
}
catch (InvalidAlgorithmParameterException e)
{
isTrue("message ", "NONCE in IES Parameters needs to be 16 bytes long".equals(e.getMessage()));
}
try
{
c.init(Cipher.DECRYPT_MODE, priv);
fail("no exception");
}
catch (IllegalArgumentException e)
{
isTrue("message ", "cannot handle supplied parameter spec: NONCE in IES Parameters needs to be 16 bytes long".equals(e.getMessage()));
}
try
{
c.init(Cipher.DECRYPT_MODE, priv, new IESParameterSpec(derivation, encoding, 128, 128, null));
fail("no exception");
}
catch (InvalidAlgorithmParameterException e)
{
isTrue("message ", "NONCE in IES Parameters needs to be 16 bytes long".equals(e.getMessage()));
}
sealedObjectTest();
}
private void sealedObjectTest()
throws Exception
{
KeyPairGenerator kpg = KeyPairGenerator.getInstance("ECIES");
kpg.initialize(new ECGenParameterSpec("secp256r1"));
KeyPair keyPair = kpg.generateKeyPair();
Cipher cipher = Cipher.getInstance("ECIES");
cipher.init(Cipher.ENCRYPT_MODE, keyPair.getPublic());
String toEncrypt = "Hello";
// Check that cipher works ok
cipher.doFinal(toEncrypt.getBytes());
// Using a SealedObject to encrypt the same string fails with a NullPointerException
SealedObject sealedObject = new SealedObject(toEncrypt, cipher);
cipher.init(Cipher.DECRYPT_MODE, keyPair.getPrivate());
String result = (String)sealedObject.getObject(cipher);
isTrue("result wrong", result.equals(toEncrypt));
result = (String)sealedObject.getObject(keyPair.getPrivate());
isTrue("result wrong", result.equals(toEncrypt));
}
public void doTest(
String testname,
KeyPairGenerator g,
String cipher,
IESParameterSpec p)
throws Exception
{
byte[] message = Hex.decode("0102030405060708090a0b0c0d0e0f10111213141516");
byte[] out1, out2;
// Generate static key pair
KeyPair KeyPair = g.generateKeyPair();
ECPublicKey Pub = (ECPublicKey) KeyPair.getPublic();
ECPrivateKey Priv = (ECPrivateKey) KeyPair.getPrivate();
Cipher c1 = Cipher.getInstance(cipher);
Cipher c2 = Cipher.getInstance(cipher);
// Testing with null parameters and DHAES mode off
c1.init(Cipher.ENCRYPT_MODE, Pub, new SecureRandom());
c2.init(Cipher.DECRYPT_MODE, Priv, c1.getParameters());
isTrue("nonce mismatch", Arrays.areEqual(c1.getIV(), c2.getIV()));
out1 = c1.doFinal(message, 0, message.length);
out2 = c2.doFinal(out1, 0, out1.length);
if (!areEqual(out2, message))
fail(testname + " test failed with null parameters, DHAES mode false.");
// Testing with given parameters and DHAES mode off
c1.init(Cipher.ENCRYPT_MODE, Pub, p, new SecureRandom());
c2.init(Cipher.DECRYPT_MODE, Priv, p);
out1 = c1.doFinal(message, 0, message.length);
out2 = c2.doFinal(out1, 0, out1.length);
if (!areEqual(out2, message))
fail(testname + " test failed with non-null parameters, DHAES mode false.");
//
// corrupted data test
//
int offset = out1.length - (message.length + 8);
byte[] tmp = new byte[out1.length];
for (int i = offset; i != out1.length; i++)
{
System.arraycopy(out1, 0, tmp, 0, tmp.length);
tmp[i] = (byte)~tmp[i];
try
{
c2.doFinal(tmp, 0, tmp.length);
fail("decrypted corrupted data");
}
catch (BadPaddingException e)
{
isTrue("wrong message: " + e.getMessage(), "unable to process block".equals(e.getMessage()));
}
}
// TODO: DHAES mode is not currently implemented, perhaps it shouldn't be...
// c1 = Cipher.getInstance(cipher + "/DHAES/PKCS7Padding","BC");
// c2 = Cipher.getInstance(cipher + "/DHAES/PKCS7Padding","BC");
//
// // Testing with null parameters and DHAES mode on
// c1.init(Cipher.ENCRYPT_MODE, Pub, new SecureRandom());
// c2.init(Cipher.DECRYPT_MODE, Priv, new SecureRandom());
//
// out1 = c1.doFinal(message, 0, message.length);
// out2 = c2.doFinal(out1, 0, out1.length);
// if (!areEqual(out2, message))
// fail(testname + " test failed with null parameters, DHAES mode true.");
//
// c1 = Cipher.getInstance(cipher + "/DHAES/PKCS7Padding");
// c2 = Cipher.getInstance(cipher + "/DHAES/PKCS7Padding");
//
// // Testing with given parameters and DHAES mode on
// c1.init(Cipher.ENCRYPT_MODE, Pub, p, new SecureRandom());
// c2.init(Cipher.DECRYPT_MODE, Priv, p, new SecureRandom());
//
// out1 = c1.doFinal(message, 0, message.length);
// out2 = c2.doFinal(out1, 0, out1.length);
// if (!areEqual(out2, message))
// fail(testname + " test failed with non-null parameters, DHAES mode true.");
}
public static void main(
String[] args)
{
Security.addProvider(new BouncyCastleProvider());
runTest(new ECIESTest());
}
}