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()); } }