org.alfresco.encryption.KeyStoreTests.java Source code

Java tutorial

Introduction

Here is the source code for org.alfresco.encryption.KeyStoreTests.java

Source

/*
 * #%L
 * Alfresco Repository
 * %%
 * Copyright (C) 2005 - 2016 Alfresco Software Limited
 * %%
 * This file is part of the Alfresco software. 
 * If the software was purchased under a paid Alfresco license, the terms of 
 * the paid license agreement will prevail.  Otherwise, the software is 
 * provided under the following open source license terms:
 * 
 * Alfresco is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * Alfresco is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
 * #L%
 */
package org.alfresco.encryption;

import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESedeKeySpec;
import javax.transaction.NotSupportedException;
import javax.transaction.SystemException;
import javax.transaction.UserTransaction;

import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.service.transaction.TransactionService;
import org.alfresco.util.ApplicationContextHelper;
import org.alfresco.util.GUID;
import org.apache.commons.codec.binary.Base64;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.ApplicationContext;

/**
 * 
 * @since 4.0
 *
 */
public class KeyStoreTests {
    private static ApplicationContext ctx = ApplicationContextHelper.getApplicationContext();

    private TransactionService transactionService;
    private KeyStoreChecker keyStoreChecker;
    private EncryptionKeysRegistry encryptionKeysRegistry;
    private UserTransaction txn = null;
    private KeyResourceLoader keyResourceLoader;
    private List<String> toDelete;
    private DefaultEncryptor backupEncryptor;

    @Before
    public void setup() throws SystemException, NotSupportedException {
        transactionService = (TransactionService) ctx.getBean("transactionService");
        keyStoreChecker = (KeyStoreChecker) ctx.getBean("keyStoreChecker");
        encryptionKeysRegistry = (EncryptionKeysRegistry) ctx.getBean("encryptionKeysRegistry");
        keyResourceLoader = (KeyResourceLoader) ctx.getBean("springKeyResourceLoader");
        backupEncryptor = (DefaultEncryptor) ctx.getBean("backupEncryptor");

        toDelete = new ArrayList<String>(10);

        AuthenticationUtil.setRunAsUserSystem();
        UserTransaction txn = transactionService.getUserTransaction();
        txn.begin();
    }

    @After
    public void teardown() throws IllegalStateException, SecurityException, SystemException {
        if (txn != null) {
            txn.rollback();
        }

        for (String guid : toDelete) {
            File file = new File(guid);
            if (file.exists()) {
                file.delete();
            }
        }
    }

    public String generateEncodedKey() {
        try {
            return Base64.encodeBase64String(generateKeyData());
        } catch (Throwable e) {
            fail("Unexpected exception: " + e.getMessage());
            return null;
        }
    }

    public byte[] generateKeyData() throws NoSuchAlgorithmException {
        SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
        random.setSeed(System.currentTimeMillis());
        byte bytes[] = new byte[DESedeKeySpec.DES_EDE_KEY_LEN];
        random.nextBytes(bytes);
        return bytes;
    }

    protected String generateKeystoreName() {
        String guid = GUID.generate();
        toDelete.add(guid);
        return guid;
    }

    protected Key generateSecretKey(String keyAlgorithm) {
        try {
            DESedeKeySpec keySpec = new DESedeKeySpec(generateKeyData());
            SecretKeyFactory kf = SecretKeyFactory.getInstance(keyAlgorithm);
            SecretKey secretKey = kf.generateSecret(keySpec);
            return secretKey;
        } catch (Throwable e) {
            fail("Unexpected exception: " + e.getMessage());
            return null;
        }
    }

    protected TestAlfrescoKeyStore getKeyStore(String name, String type, final Map<String, String> passwords,
            final Map<String, String> encodedKeyData, String keyStoreLocation, String backupKeyStoreLocation) {
        KeyResourceLoader testKeyResourceLoader = new KeyResourceLoader() {
            @Override
            public InputStream getKeyStore(String keyStoreLocation) throws FileNotFoundException {
                return keyResourceLoader.getKeyStore(keyStoreLocation);
            }

            @Override
            public Properties loadKeyMetaData(String keyMetaDataFileLocation)
                    throws IOException, FileNotFoundException {
                Properties p = new Properties();
                p.put("keystore.password", "password");
                StringBuilder aliases = new StringBuilder();
                for (String keyAlias : passwords.keySet()) {
                    p.put(keyAlias + ".password", passwords.get(keyAlias));
                    if (encodedKeyData != null && encodedKeyData.get(keyAlias) != null) {
                        p.put(keyAlias + ".keyData", encodedKeyData.get(keyAlias));
                    }
                    p.put(keyAlias + ".algorithm", "DESede");
                    aliases.append(keyAlias);
                    aliases.append(",");
                }
                if (aliases.length() > 0) {
                    // remove trailing comma
                    aliases.delete(aliases.length() - 1, aliases.length());
                }
                p.put("aliases", aliases.toString());
                return p;
            }
        };

        KeyStoreParameters keyStoreParameters = new KeyStoreParameters(name, type, null, "", keyStoreLocation);
        KeyStoreParameters backupKeyStoreParameters = new KeyStoreParameters(name + ".backup", type, null, "",
                backupKeyStoreLocation);
        TestAlfrescoKeyStore keyStore = new TestAlfrescoKeyStore();
        keyStore.setKeyStoreParameters(keyStoreParameters);
        keyStore.setBackupKeyStoreParameters(backupKeyStoreParameters);
        keyStore.setKeyResourceLoader(testKeyResourceLoader);
        keyStore.setValidateKeyChanges(true);
        keyStore.setEncryptionKeysRegistry(encryptionKeysRegistry);
        return keyStore;
    }

    @Test
    public void test1() {
        // missing keystore, missing backup keystore, no registered keys -> create key store with metadata key and register key

        TestAlfrescoKeyStore missingMainKeyStore = getKeyStore("main", "JCEKS",
                Collections.singletonMap(KeyProvider.ALIAS_METADATA, "metadata"),
                Collections.singletonMap(KeyProvider.ALIAS_METADATA, generateEncodedKey()), generateKeystoreName(),
                generateKeystoreName());

        encryptionKeysRegistry.unregisterKey(KeyProvider.ALIAS_METADATA);
        keyStoreChecker.setMainKeyStore(missingMainKeyStore);

        try {
            keyStoreChecker.validateKeyStores();
        } catch (InvalidKeystoreException e) {
            fail("Unexpected exception: " + e.getMessage());
        } catch (MissingKeyException e) {
            fail("Unexpected exception : " + e.getMessage());
        }

        assertTrue("", encryptionKeysRegistry.getRegisteredKeys(missingMainKeyStore.getKeyAliases())
                .contains(KeyProvider.ALIAS_METADATA));
        assertTrue("", missingMainKeyStore.exists());
        assertTrue("", missingMainKeyStore.getKey(KeyProvider.ALIAS_METADATA) != null);
    }

    @Test
    public void test2() {
        // missing main keystore, missing backup keystore, metadata registered key -> error, re-instate the keystore
        TestAlfrescoKeyStore missingMainKeyStore = getKeyStore("main", "JCEKS",
                Collections.singletonMap(KeyProvider.ALIAS_METADATA, "metadata"), null, generateKeystoreName(),
                generateKeystoreName());

        assertTrue("", encryptionKeysRegistry.isKeyRegistered("metadata"));

        keyStoreChecker.setMainKeyStore(missingMainKeyStore);

        try {
            keyStoreChecker.validateKeyStores();
            fail("Should have caught missing main keystore");
        } catch (InvalidKeystoreException e) {
            fail("Unexpected exception : " + e.getMessage());
        } catch (MissingKeyException e) {
            // ok, expected
        }
    }

    @Test
    public void test3() {
        // main keystore exists, no registered metadata key -> register key

        // create main keystore
        TestAlfrescoKeyStore mainKeyStore = getKeyStore("main", "JCEKS",
                Collections.singletonMap(KeyProvider.ALIAS_METADATA, "metadata"), null, generateKeystoreName(),
                generateKeystoreName());
        createAndPopulateKeyStore(mainKeyStore);

        // de-register metadata key
        encryptionKeysRegistry.unregisterKey(KeyProvider.ALIAS_METADATA);

        // check keys
        keyStoreChecker.setMainKeyStore(mainKeyStore);

        try {
            keyStoreChecker.validateKeyStores();
        } catch (InvalidKeystoreException e) {
            fail("Unexpected exception: " + e.getMessage());
        } catch (MissingKeyException e) {
            fail("Unexpected exception : " + e.getMessage());
        }

        assertTrue("", encryptionKeysRegistry.isKeyRegistered(KeyProvider.ALIAS_METADATA));
    }

    @Test
    public void test4() {
        // create keystore, change key -> check for exception InvalidKey

        // Firstly, create main keystore to register a well-known key

        encryptionKeysRegistry.unregisterKey(KeyProvider.ALIAS_METADATA);

        TestAlfrescoKeyStore keyStore = getKeyStore("main", "JCEKS",
                Collections.singletonMap(KeyProvider.ALIAS_METADATA, "metadata"), null, generateKeystoreName(),
                generateKeystoreName());
        createAndPopulateKeyStore(keyStore);

        keyStoreChecker.setMainKeyStore(keyStore);

        // check keys
        try {
            // should register the metadata key
            keyStoreChecker.validateKeyStores();
        } catch (InvalidKeystoreException e) {
            fail("Unexpected exception: " + e.getMessage());
        } catch (MissingKeyException e) {
            fail("Unexpected exception : " + e.getMessage());
        }

        // check that the metadata key has been registered
        assertTrue("", encryptionKeysRegistry.isKeyRegistered("metadata"));

        // a changed main keystore with a different metadata key
        //      TestAlfrescoKeyStore changedMainKeyStore = getKeyStore("main", "JCEKS", Collections.singletonMap(KeyProvider.ALIAS_METADATA, "metadata"),
        //            null, generateKeystoreName(), generateKeystoreName());
        //      createAndPopulateKeyStore(changedMainKeyStore);
        keyStore.changeKey(KeyProvider.ALIAS_METADATA, generateSecretKey("DESede"));

        //      keyStoreChecker.setMainKeyStore(changedMainKeyStore);
        try {
            keyStoreChecker.validateKeyStores();
            fail("Expected key store checker to detect changed metadata key");
        } catch (InvalidKeystoreException e) {
            // ok, expected
        } catch (MissingKeyException e) {
            fail("Unexpected exception : " + e.getMessage());
        }
    }

    @Test
    public void test5() {
        // create main keystore, backup main keystore, change main keystore -> check that backup keystore key is ok, re-register new main key
        // check that the new main keystore key has been re-registered

        // Firstly, re-install main keystore to register a well-known key
        encryptionKeysRegistry.unregisterKey(KeyProvider.ALIAS_METADATA);

        TestAlfrescoKeyStore keyStore = getKeyStore("main", "JCEKS",
                Collections.singletonMap(KeyProvider.ALIAS_METADATA, "metadata"), null, generateKeystoreName(),
                generateKeystoreName());
        createAndPopulateKeyStore(keyStore);

        try {
            keyStoreChecker.setMainKeyStore(keyStore);
            keyStoreChecker.validateKeyStores();
        } catch (InvalidKeystoreException e) {
            fail("Unexpected exception: " + e.getMessage());
        } catch (MissingKeyException e) {
            fail("Unexpected exception : " + e.getMessage());
        }

        keyStore.backup();

        // check that the metadata key has been registered
        assertTrue("", encryptionKeysRegistry.isKeyRegistered("metadata"));

        // change the metadata key
        keyStore.changeKey(KeyProvider.ALIAS_METADATA, generateSecretKey("DESede"));

        try {
            // should detect changed metadata key and re-register it
            keyStoreChecker.validateKeyStores();
        } catch (InvalidKeystoreException e) {
            fail("Unexpected exception: " + e.getMessage());
        } catch (MissingKeyException e) {
            fail("Unexpected exception : " + e.getMessage());
        }

        // check that the new metadata key has been successfully re-registered by encrypting and decrypting some content with it
        assertTrue("", EncryptionKeysRegistry.KEY_STATUS.OK == encryptionKeysRegistry
                .checkKey(KeyProvider.ALIAS_METADATA, keyStore.getKey(KeyProvider.ALIAS_METADATA)));
    }

    private void createAndPopulateKeyStore(TestAlfrescoKeyStore keyStore) {
        KeyMap keyMap = new KeyMap();
        keyMap.setKey(KeyProvider.ALIAS_METADATA, generateSecretKey("DESede"));
        keyStore.create(keyMap, null);
    }

    private static class TestAlfrescoKeyStore extends AlfrescoKeyStoreImpl {
        public void create(KeyMap keys, KeyMap backupKeys) {
            this.keys = (keys != null ? keys : new KeyMap());
            this.backupKeys = (backupKeys != null ? backupKeys : new KeyMap());
            super.create();
        }

        void changeKey(String keyAlias, Key key) {
            keys.setKey(KeyProvider.ALIAS_METADATA, key);
        }
    }
}