/*
* Copyright 2010-2017 Norwegian Agency for Public Management and eGovernment (Difi)
*
* Licensed under the EUPL, Version 1.1 or – as soon they
* will be approved by the European Commission - subsequent
* versions of the EUPL (the "Licence");
*
* You may not use this work except in compliance with the Licence.
*
* You may obtain a copy of the Licence at:
*
* https://joinup.ec.europa.eu/community/eupl/og_page/eupl
*
* Unless required by applicable law or agreed to in
* writing, software distributed under the Licence is
* distributed on an "AS IS" basis,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied.
* See the Licence for the specific language governing
* permissions and limitations under the Licence.
*/
package no.difi.oxalis.statistics.security;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.SecretKey;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PublicKey;
import java.util.Arrays;
import static org.testng.Assert.*;
/**
* @author steinar
* Date: 06.05.13
* Time: 22:34
*/
public class OxalisCipherTest {
private StatisticsKeyTool statisticsKeyTool;
private OxalisCipher oxalisCipher;
private OxalisCipherConverter oxalisCipherConverter;
@BeforeMethod
public void setUp() throws IOException {
oxalisCipher = new OxalisCipher();
oxalisCipherConverter = new OxalisCipherConverter();
Path tempDirectory = Files.createTempDirectory("unit-test");
statisticsKeyTool = new StatisticsKeyTool();
assertNotNull(statisticsKeyTool);
}
/**
* Verifies understanding of converting a string between bytes and a string
*/
@Test
public void convertBetweenCharAndBytes() throws Exception {
String s = "Hello World!" + "\u00e6" + "\u00f8" + "\u00e5" + "\u00c6" + "\u00d8" + "\u00c5";
byte[] bytes = s.getBytes("UTF-8");
String s1 = new String(bytes, "UTF-8");
assertEquals(s, s1);
}
@Test
public void encryptAndDecryptAString() throws Exception {
String s = "Hello World! " + "\u00e6" + "\u00f8" + "\u00e5" + "\u00c6" + "\u00d8" + "\u00c5";
// Encrypt, decrypt and compare using the simple methods
byte[] encryptedBytes = oxalisCipher.encrypt(s.getBytes("UTF-8"));
byte[] decryptedBytes = oxalisCipher.decrypt(encryptedBytes);
// array of bytes should be equal
assertTrue(Arrays.equals(s.getBytes("UTF-8"), decryptedBytes));
// Converting back to a string should still equal our initial string
String s2 = new String(decryptedBytes, "UTF-8");
assertEquals(s, s2, "Oops decrypted string does not compare to encrypted string. Perhaps an encoding problem?");
}
@Test
public void encryptAndDecryptSomeXml() throws Exception {
String s = "<?xml version='1.0' encoding='UTF-8'?>\n" +
"<peppol-ap-statistics start=\"2013-11-30 23:00\" end=\"2013-11-30 23:00\"></peppol-ap-statistics>";
// Encrypt, decrypt and compare using the simple methods
byte[] encryptedBytes = oxalisCipher.encrypt(s.getBytes("UTF-8"));
byte[] decryptedBytes = oxalisCipher.decrypt(encryptedBytes);
// array of bytes should be equal
assertTrue(Arrays.equals(s.getBytes("UTF-8"), decryptedBytes));
// Converting back to a string should still equal our initial string
String s2 = new String(decryptedBytes, "UTF-8");
assertEquals(s, s2);
}
/**
* Mimics how a servlet will respond with an encrypted entity for which the key is encrypted and placed
* in a header.
*/
@Test
public void testEncryptAndDecrypt() throws IOException, BadPaddingException, IllegalBlockSizeException {
String plainText = "Hello World! " + "\u00e6" + "\u00f8" + "\u00e5";
byte[] encryptedBytes = encryptString(plainText, Charset.forName("ISO-8859-1"));
String s = decryptToString(oxalisCipher, encryptedBytes, Charset.forName("ISO-8859-1"));
assertEquals(s, plainText);
}
/**
* Decrypts bytes using the symmetric key held in the OxalisCipher instance.
* Uses Cipher streams.
*
* @param cipher
* @param encryptedBytes
* @param charset the character set encoding to use
* @return
* @throws IOException
*/
private String decryptToString(OxalisCipher cipher, byte[] encryptedBytes, Charset charset)
throws IOException, BadPaddingException, IllegalBlockSizeException {
byte[] decryptedBytes = oxalisCipher.decrypt(encryptedBytes);
return new String(decryptedBytes, charset);
}
/**
* Encrypts bytes using the symmetric key held in the OxalisCipher instance.
*/
private byte[] encryptString(String plainText, Charset charset)
throws IOException, BadPaddingException, IllegalBlockSizeException {
return oxalisCipher.encrypt(plainText.getBytes(charset));
}
/**
* Encrypts data using our symmetric secret key obtained from the instance of OxalisCipher, after which
* the secret key is encrypted (wrapped) using the public asymmetric RSA keys loaded from disk.
* Finally the secret key is decrypted (unwrapped) and the encrypted data is decrypted.
* <p>
* NOTE! If this goes belly up, you should verify that the public and private key loaded by
* StatisticsKeyTool is actually a pair.
*
* @param publicKey
* @throws Exception
*/
@Test(groups = {"integration"}, dataProvider = "publicKey", enabled = true)
public void encryptDataEncryptKeyAndReverse(PublicKey publicKey) throws Exception {
String plainText = "Sample data for testing purposes æøå";
byte[] encryptedBytes = encryptString(plainText, Charset.forName("UTF-8"));
String encodedSymmetricKey = oxalisCipherConverter.getWrappedSymmetricKeyAsString(publicKey, oxalisCipher);
assertNotNull(encodedSymmetricKey);
}
/**
* Proves that a symmetric AES key, which we obtain from the OxalisCipher instance,
* can be wrapped and unwrapped using an asymmetric RSA key.
*
* @throws Exception
*/
@Test
public void testWrapKey() throws Exception {
// Generates our asymmetric key pair
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
KeyPair keyPair = keyPairGenerator.generateKeyPair();
// Wraps the symmetric secret key
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.WRAP_MODE, keyPair.getPublic());
byte[] wrappedSecretKey = cipher.wrap(oxalisCipher.getSecretKey());
// Unwraps the symmetric secret key
Cipher cipher1 = Cipher.getInstance("RSA");
cipher1.init(Cipher.UNWRAP_MODE, keyPair.getPrivate());
SecretKey aes = (SecretKey) cipher1.unwrap(wrappedSecretKey, "AES", Cipher.SECRET_KEY);
}
@DataProvider(name = "publicKey")
public Object[][] createKeyPair() {
return new Object[][]{
{statisticsKeyTool.loadPublicKeyFromClassPath()}
};
}
}