/*
* (C) Copyright 2006-2016 Nuxeo SA (http://nuxeo.com/) and others.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Contributors:
* Florent Guillaume
*/
package org.nuxeo.ecm.core.blob.binary;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assume.assumeTrue;
import static org.nuxeo.ecm.core.blob.binary.AESBinaryManager.PARAM_KEY_ALIAS;
import static org.nuxeo.ecm.core.blob.binary.AESBinaryManager.PARAM_KEY_PASSWORD;
import static org.nuxeo.ecm.core.blob.binary.AESBinaryManager.PARAM_KEY_STORE_FILE;
import static org.nuxeo.ecm.core.blob.binary.AESBinaryManager.PARAM_KEY_STORE_PASSWORD;
import static org.nuxeo.ecm.core.blob.binary.AESBinaryManager.PARAM_KEY_STORE_TYPE;
import static org.nuxeo.ecm.core.blob.binary.AESBinaryManager.PARAM_PASSWORD;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.GeneralSecurityException;
import java.security.Key;
import java.security.KeyStore;
import java.util.Collections;
import javax.crypto.KeyGenerator;
import org.apache.commons.io.IOUtils;
import org.junit.Before;
import org.junit.Test;
import org.nuxeo.ecm.core.api.NuxeoException;
import org.nuxeo.ecm.core.blob.binary.AESBinaryManager;
import org.nuxeo.ecm.core.blob.binary.Binary;
import org.nuxeo.runtime.api.Framework;
import org.nuxeo.runtime.test.NXRuntimeTestCase;
public class TestAESBinaryManager extends NXRuntimeTestCase {
private static final String KEY_STORE_TYPE = "JCEKS";
private static final String KEY_STORE_PASSWORD = "keystoresecret";
private static final String KEY_ALIAS = "myaeskey";
private static final String KEY_PASSWORD = "keysecret";
private static final String CONTENT = "this is a file au caf\u00e9";
private static final String CONTENT_MD5 = "d25ea4f4642073b7f218024d397dbaef";
private static final String UTF8 = "UTF-8";
@Before
public void check() {
assumeTrue("Cannot set Unlimited JCE Policy", AESBinaryManager.setUnlimitedJCEPolicy());
}
@Test
public void testEncryptDecryptWithPassword() throws Exception {
AESBinaryManager binaryManager = new AESBinaryManager();
binaryManager.digestAlgorithm = binaryManager.getDefaultDigestAlgorithm(); // MD5
String options = String.format("%s=%s", PARAM_PASSWORD, "mypassword");
binaryManager.initializeOptions(options);
// encrypt
ByteArrayOutputStream out = new ByteArrayOutputStream();
String digest = binaryManager.storeAndDigest(new ByteArrayInputStream(CONTENT.getBytes(UTF8)), out);
assertEquals(CONTENT_MD5, digest);
byte[] encrypted = out.toByteArray();
// decrypt
out = new ByteArrayOutputStream();
binaryManager.decrypt(new ByteArrayInputStream(encrypted), out);
assertEquals(CONTENT, new String(out.toByteArray(), UTF8));
// cannot decrypt with wrong password
options = String.format("%s=%s", PARAM_PASSWORD, "badpassword");
binaryManager.initializeOptions(options);
out = new ByteArrayOutputStream();
try {
binaryManager.decrypt(new ByteArrayInputStream(encrypted), out);
assertFalse(CONTENT.equals(new String(out.toByteArray(), UTF8)));
} catch (NuxeoException e) {
String message = e.getMessage();
assertTrue(message, message.contains("Given final block not properly padded"));
}
binaryManager.close();
}
@Test
public void testEncryptDecryptWithKeyStore() throws Exception {
File keyStoreFile = Framework.createTempFile("nuxeoKeyStore_", "");
keyStoreFile.delete();
createKeyStore(keyStoreFile);
String options = String.format("%s=%s,%s=%s,%s=%s,%s=%s,%s=%s", PARAM_KEY_STORE_TYPE, KEY_STORE_TYPE, //
PARAM_KEY_STORE_FILE, keyStoreFile.getPath(), //
PARAM_KEY_STORE_PASSWORD, KEY_STORE_PASSWORD, //
PARAM_KEY_ALIAS, KEY_ALIAS, //
PARAM_KEY_PASSWORD, KEY_PASSWORD);
AESBinaryManager binaryManager = new AESBinaryManager();
binaryManager.digestAlgorithm = binaryManager.getDefaultDigestAlgorithm(); // MD5
binaryManager.initializeOptions(options);
// encrypt
ByteArrayOutputStream out = new ByteArrayOutputStream();
String digest = binaryManager.storeAndDigest(new ByteArrayInputStream(CONTENT.getBytes(UTF8)), out);
assertEquals(CONTENT_MD5, digest);
byte[] encrypted = out.toByteArray();
// decrypt
out = new ByteArrayOutputStream();
binaryManager.decrypt(new ByteArrayInputStream(encrypted), out);
assertEquals(CONTENT, new String(out.toByteArray(), UTF8));
binaryManager.close();
}
protected void createKeyStore(File file) throws GeneralSecurityException, IOException {
AESBinaryManager.setUnlimitedJCEPolicy();
KeyGenerator kgen = KeyGenerator.getInstance("AES");
kgen.init(256);
Key skey = kgen.generateKey();
KeyStore keyStore = KeyStore.getInstance(KEY_STORE_TYPE);
// keyStore.load(null, KEY_STORE_PASSWORD.toCharArray());
keyStore.load(null, null);
keyStore.setKeyEntry(KEY_ALIAS, skey, KEY_PASSWORD.toCharArray(), null);
OutputStream out = new FileOutputStream(file);
keyStore.store(out, KEY_STORE_PASSWORD.toCharArray());
out.close();
}
@Test
public void testAESBinaryManager() throws Exception {
AESBinaryManager binaryManager = new AESBinaryManager();
String options = String.format("%s=%s", PARAM_PASSWORD, "mypassword");
binaryManager.initialize("repo", Collections.singletonMap(BinaryManager.PROP_KEY, options));
Binary binary = binaryManager.getBinary(CONTENT_MD5);
assertNull(binary);
// store binary
byte[] bytes = CONTENT.getBytes(UTF8);
binary = binaryManager.getBinary(new ByteArrayInputStream(bytes));
assertNotNull(binary);
assertEquals(CONTENT_MD5, binary.getDigest());
// get binary
binary = binaryManager.getBinary(CONTENT_MD5);
assertNotNull(binary);
try (InputStream stream = binary.getStream()) {
assertEquals(CONTENT, IOUtils.toString(stream, UTF8));
}
binaryManager.close();
}
}