package org.hive2hive.core.security; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import java.io.IOException; import java.math.BigInteger; import java.security.GeneralSecurityException; import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.KeyPair; import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; import java.security.SecureRandom; import java.security.Security; import java.security.SignatureException; import java.util.Arrays; import java.util.Random; import javax.crypto.BadPaddingException; import javax.crypto.IllegalBlockSizeException; import javax.crypto.NoSuchPaddingException; import javax.crypto.SecretKey; import org.bouncycastle.crypto.AsymmetricBlockCipher; import org.bouncycastle.crypto.AsymmetricCipherKeyPair; import org.bouncycastle.crypto.DataLengthException; import org.bouncycastle.crypto.InvalidCipherTextException; import org.bouncycastle.crypto.encodings.PKCS1Encoding; import org.bouncycastle.crypto.engines.RSAEngine; import org.bouncycastle.crypto.generators.RSAKeyPairGenerator; import org.bouncycastle.crypto.params.RSAKeyGenerationParameters; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.hive2hive.core.H2HJUnitTest; import org.hive2hive.core.model.versioned.HybridEncryptedContent; import org.hive2hive.core.security.EncryptionUtil.AES_KEYLENGTH; import org.hive2hive.core.security.EncryptionUtil.RSA_KEYLENGTH; import org.junit.AfterClass; import org.junit.Assert; import org.junit.BeforeClass; import org.junit.Ignore; import org.junit.Test; public class EncryptionUtilTest extends H2HJUnitTest { private static String SECURITY_PROVIDER = "BC"; private static IStrongAESEncryption STRONG_AES = new BCStrongAESEncryption(); @BeforeClass public static void initTest() throws Exception { testClass = EncryptionUtilTest.class; beforeClass(); if (Security.getProvider(SECURITY_PROVIDER) == null) { Security.addProvider(new BouncyCastleProvider()); } } @Test public void generateAESKeyTest() { // test all key sizes for (AES_KEYLENGTH keylength : AES_KEYLENGTH.values()) { logger.debug("Testing AES {}-bit key generation.", keylength.value()); // generate AES key long start = System.currentTimeMillis(); SecretKey aesKey = EncryptionUtil.generateAESKey(keylength, SECURITY_PROVIDER); long stop = System.currentTimeMillis(); logger.debug("Generated AES key: {}.", EncryptionUtil.byteToHex(aesKey.getEncoded())); logger.debug("Time: {} ms.", stop - start); assertNotNull(aesKey); assertTrue(aesKey.getAlgorithm().equals("AES")); } } @Test public void generateRSAKeyPairTest() { // test all key sizes for (RSA_KEYLENGTH keyLength : RSA_KEYLENGTH.values()) { logger.debug("Testing RSA {}-bit key pair generation.", keyLength.value()); // generate RSA key pair long start = System.currentTimeMillis(); KeyPair rsaKeyPair = generateRSAKeyPair(keyLength); long stop = System.currentTimeMillis(); assertNotNull(rsaKeyPair); assertNotNull(rsaKeyPair.getPrivate()); assertNotNull(rsaKeyPair.getPublic()); logger.debug("Private Key: {}.", EncryptionUtil.byteToHex(rsaKeyPair.getPrivate().getEncoded())); logger.debug("Public Key: {}.", EncryptionUtil.byteToHex(rsaKeyPair.getPublic().getEncoded())); logger.debug("Time: {} ms.", stop - start); } } @Test public void encryptionAESTest() { // test all key sizes for (AES_KEYLENGTH keylength : AES_KEYLENGTH.values()) { logger.debug("Testing AES {}-bit encryption and decryption.", keylength.value()); // generate random sized content (max. 2MB) byte[] data = generateRandomContent(2097152); // generate AES key SecretKey aesKey = EncryptionUtil.generateAESKey(keylength, SECURITY_PROVIDER); // generate IV byte[] initVector = EncryptionUtil.generateIV(); // encrypt data byte[] encryptedData = null; try { encryptedData = EncryptionUtil.encryptAES(data, aesKey, initVector, SECURITY_PROVIDER, STRONG_AES); } catch (IllegalStateException | GeneralSecurityException e) { logger.error("Exception while testing AES encryption:", e); e.printStackTrace(); } assertNotNull(encryptedData); assertFalse(Arrays.equals(data, encryptedData)); // decrypt data byte[] decryptedData = null; try { decryptedData = EncryptionUtil.decryptAES(encryptedData, aesKey, initVector, SECURITY_PROVIDER, STRONG_AES); } catch (IllegalStateException | GeneralSecurityException e) { logger.error("Exception while testing AES decryption:", e); e.printStackTrace(); } assertNotNull(decryptedData); assertFalse(Arrays.equals(encryptedData, decryptedData)); assertTrue(Arrays.equals(data, decryptedData)); } } @Test public void encryptionRSATest() { // test all key sizes for (RSA_KEYLENGTH keyLength : RSA_KEYLENGTH.values()) { logger.debug("Testing RSA {}-bit encryption and decryption.", keyLength.value()); // generate random sized content (max. (key size / 8) - 11 bytes) byte[] data = generateRandomContent((keyLength.value() / 8) - 11); logger.debug("Testing RSA encryption of a sample {} byte file with a {} bit key.", data.length, keyLength.value()); printBytes("Original Data", data); // generate RSA key pair KeyPair rsaKeyPair = generateRSAKeyPair(keyLength); // encrypt data with public key byte[] encryptedData = null; try { encryptedData = EncryptionUtil.encryptRSA(data, rsaKeyPair.getPublic(), SECURITY_PROVIDER); } catch (InvalidKeyException | IllegalBlockSizeException | BadPaddingException e) { logger.error("Exception while testing RSA encryption:", e); e.printStackTrace(); e.printStackTrace(); } assertNotNull(encryptedData); assertNotEquals(0, encryptedData.length); assertFalse(Arrays.equals(data, encryptedData)); printBytes("Encrypted Data:", encryptedData); // decrypt data with private key byte[] decryptedData = null; try { decryptedData = EncryptionUtil.decryptRSA(encryptedData, rsaKeyPair.getPrivate(), SECURITY_PROVIDER); } catch (InvalidKeyException | IllegalBlockSizeException | BadPaddingException e) { logger.error("Exception while testing RSA decryption:", e); e.printStackTrace(); } assertNotNull(decryptedData); assertNotEquals(0, decryptedData.length); assertTrue(Arrays.equals(data, decryptedData)); printBytes("Decrypted Data:", decryptedData); } } @Test public void encryptionHybridTest() { Random rnd = new Random(); // test all RSA key sizes for (RSA_KEYLENGTH rsaLength : RSA_KEYLENGTH.values()) { // test all AES key sizes for (AES_KEYLENGTH aesLength : AES_KEYLENGTH.values()) { // generate random content (0-10 MB) byte[] data = generateFixedContent((int) (rnd.nextDouble() * 10 * 1024 * 1024)); logger.debug( "Testing hybrid encryption and decryption of a sample {} byte file with a {} bit RSA and a {} bit AES key.", data.length, rsaLength.value(), aesLength.value()); // generate RSA key pair long start = System.currentTimeMillis(); KeyPair rsaKeyPair = generateRSAKeyPair(rsaLength); long stop = System.currentTimeMillis(); logger.debug("RSA Key Generation Time: {} ms.", stop - start); // encrypt data with public key HybridEncryptedContent encryptedData = null; try { start = System.currentTimeMillis(); encryptedData = EncryptionUtil.encryptHybrid(data, rsaKeyPair.getPublic(), aesLength, SECURITY_PROVIDER, STRONG_AES); stop = System.currentTimeMillis(); logger.debug("Hybrid Encryption Time: {} ms.", stop - start); } catch (GeneralSecurityException e) { logger.error("Exception while testing hybrid encryption:", e); e.printStackTrace(); } assertNotNull(encryptedData); assertNotNull(encryptedData.getEncryptedData()); assertNotNull(encryptedData.getEncryptedParameters()); assertFalse(Arrays.equals(data, encryptedData.getEncryptedData())); // decrypt data with private key byte[] decryptedData = null; try { start = System.currentTimeMillis(); decryptedData = EncryptionUtil.decryptHybrid(encryptedData, rsaKeyPair.getPrivate(), SECURITY_PROVIDER, STRONG_AES); stop = System.currentTimeMillis(); logger.debug("Hybrid Decryption Time: {} ms.", stop - start); } catch (GeneralSecurityException e) { logger.error("Exception while testing hybrid decryption:", e); e.printStackTrace(); } assertNotNull(decryptedData); assertTrue(Arrays.equals(data, decryptedData)); } } } @Test public void signatureTest() { // test all key sizes for (RSA_KEYLENGTH keyLength : RSA_KEYLENGTH.values()) { logger.debug("Testing SHA-1 with RSA {}-bit signing and verificiation.", keyLength.value()); // generate random sized content (max. 100 bytes) byte[] data = generateRandomContent(100); printBytes("Original Data:", data); // generate RSA key pair KeyPair rsaKeyPair = generateRSAKeyPair(keyLength); // sign data with private key byte[] signature = null; try { signature = EncryptionUtil.sign(data, rsaKeyPair.getPrivate(), SECURITY_PROVIDER); } catch (InvalidKeyException | SignatureException e) { logger.error("Exception while testing signing:", e); e.printStackTrace(); } assertNotNull(signature); printBytes("Signature:", signature); // verify data with public key boolean isVerified = false; try { isVerified = EncryptionUtil.verify(data, signature, rsaKeyPair.getPublic(), SECURITY_PROVIDER); } catch (InvalidKeyException | SignatureException e) { logger.error("Exception while testing verification:", e); e.printStackTrace(); } assertTrue(isVerified); } } @Test public void testIVGeneration() { for (int i = 0; i < 100000; i++) Assert.assertNotEquals(0, EncryptionUtil.generateIV()[0]); } @Test @Ignore public void testPureLightweightBouncyCastle() throws IOException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException, DataLengthException, IllegalStateException, InvalidCipherTextException, NoSuchAlgorithmException, NoSuchProviderException, NoSuchPaddingException, InvalidAlgorithmParameterException { long startTime = System.currentTimeMillis(); Security.addProvider(new BouncyCastleProvider()); // generate RSA keys RSAKeyPairGenerator gen = new RSAKeyPairGenerator(); gen.init(new RSAKeyGenerationParameters(new BigInteger("10001", 16), new SecureRandom(), 2048, 80)); AsymmetricCipherKeyPair keyPair = gen.generateKeyPair(); // some data where first entry is 0 byte[] data = { 10, 122, 12, 127, 35, 58, 87, 56, -6, 73, 10, -13, -78, 4, -122, -61 }; // encrypt data asymmetrically AsymmetricBlockCipher cipher = new RSAEngine(); cipher = new PKCS1Encoding(cipher); cipher.init(true, keyPair.getPublic()); byte[] rsaEncryptedData = cipher.processBlock(data, 0, data.length); Assert.assertFalse(Arrays.equals(data, rsaEncryptedData)); // decrypt data asymmetrically cipher.init(false, keyPair.getPrivate()); byte[] dataBack = cipher.processBlock(rsaEncryptedData, 0, rsaEncryptedData.length); assertTrue(Arrays.equals(data, dataBack)); long stopTime = System.currentTimeMillis(); long elapsedTime = stopTime - startTime; logger.debug("elapsed time = {}", elapsedTime); } @AfterClass public static void endTest() throws Exception { afterClass(); } }