mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-08-07 17:49:17 +00:00
Fix for ALF-10189:
o Alfresco key store manages keys and backup keys internally o moved key registration and checking into AlfrescoKeyStoreImpl o encryptor thread cache fix resulting from reload of key stores at runtime o more encryption and key store tests o tidy up + more comments o moved hard-coded values to properties file git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@30405 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
@@ -169,8 +169,7 @@
|
|||||||
</bean>
|
</bean>
|
||||||
|
|
||||||
<bean id="encryptionChecker" class="org.alfresco.encryption.EncryptionChecker">
|
<bean id="encryptionChecker" class="org.alfresco.encryption.EncryptionChecker">
|
||||||
<property name="keyStoreParameters" ref="keyStoreParameters"/>
|
<property name="transactionService" ref="transactionService"/>
|
||||||
<property name="keyResourceLoader" ref="springKeyResourceLoader"/>
|
|
||||||
<property name="keyStoreChecker" ref="keyStoreChecker"/>
|
<property name="keyStoreChecker" ref="keyStoreChecker"/>
|
||||||
</bean>
|
</bean>
|
||||||
|
|
||||||
|
@@ -11,8 +11,25 @@
|
|||||||
|
|
||||||
<!-- Beans to initilize encryption -->
|
<!-- Beans to initilize encryption -->
|
||||||
|
|
||||||
|
<bean id="springKeyResourceLoader" class="org.alfresco.encryption.SpringKeyResourceLoader">
|
||||||
|
</bean>
|
||||||
|
|
||||||
|
<bean id="keyStoreParameters" class="org.alfresco.encryption.KeyStoreParameters" init-method="init">
|
||||||
|
<property name="name" value="Key Store"/>
|
||||||
|
<property name="location" value="${encryption.keystore.location}"/>
|
||||||
|
<property name="provider" value="${encryption.keystore.provider}"/>
|
||||||
|
<property name="type" value="${encryption.keystore.type}"/>
|
||||||
|
<property name="keyMetaDataFileLocation" value="${encryption.keystore.keyMetaData.location}"/>
|
||||||
|
</bean>
|
||||||
|
|
||||||
|
<bean id="encryptionKeysRegistry" class="org.alfresco.encryption.EncryptionKeysRegistryImpl" init-method="init">
|
||||||
|
<property name="transactionService" ref="transactionService"/>
|
||||||
|
<property name="attributeService" ref="attributeService"/>
|
||||||
|
<property name="cipherAlgorithm" value="${encryption.cipherAlgorithm}" />
|
||||||
|
</bean>
|
||||||
|
|
||||||
<!-- TODO i18n for key store names -->
|
<!-- TODO i18n for key store names -->
|
||||||
<bean id="sslKeyStoreParameters" class="org.alfresco.encryption.KeyStoreParameters">
|
<bean id="sslKeyStoreParameters" class="org.alfresco.encryption.KeyStoreParameters" init-method="init">
|
||||||
<property name="name" value="SSL Key Store"/>
|
<property name="name" value="SSL Key Store"/>
|
||||||
<property name="location" value="${encryption.ssl.keystore.location}"/>
|
<property name="location" value="${encryption.ssl.keystore.location}"/>
|
||||||
<property name="type" value="${encryption.ssl.keystore.type}"/>
|
<property name="type" value="${encryption.ssl.keystore.type}"/>
|
||||||
@@ -20,7 +37,7 @@
|
|||||||
<property name="keyMetaDataFileLocation" value="${encryption.ssl.keystore.keyMetaData.location}"/>
|
<property name="keyMetaDataFileLocation" value="${encryption.ssl.keystore.keyMetaData.location}"/>
|
||||||
</bean>
|
</bean>
|
||||||
|
|
||||||
<bean id="sslTrustStoreParameters" class="org.alfresco.encryption.KeyStoreParameters">
|
<bean id="sslTrustStoreParameters" class="org.alfresco.encryption.KeyStoreParameters" init-method="init">
|
||||||
<property name="name" value="SSL Trust Store"/>
|
<property name="name" value="SSL Trust Store"/>
|
||||||
<property name="location" value="${encryption.ssl.truststore.location}"/>
|
<property name="location" value="${encryption.ssl.truststore.location}"/>
|
||||||
<property name="type" value="${encryption.ssl.truststore.type}"/>
|
<property name="type" value="${encryption.ssl.truststore.type}"/>
|
||||||
@@ -36,45 +53,24 @@
|
|||||||
<bean id="ssl.keyStore" class="org.alfresco.encryption.AlfrescoKeyStoreImpl" init-method="init">
|
<bean id="ssl.keyStore" class="org.alfresco.encryption.AlfrescoKeyStoreImpl" init-method="init">
|
||||||
<property name="keyStoreParameters" ref="sslKeyStoreParameters"/>
|
<property name="keyStoreParameters" ref="sslKeyStoreParameters"/>
|
||||||
<property name="keyResourceLoader" ref="springKeyResourceLoader"/>
|
<property name="keyResourceLoader" ref="springKeyResourceLoader"/>
|
||||||
|
<property name="validateKeyChanges" value="false"/>
|
||||||
|
<property name="encryptionKeysRegistry" ref="encryptionKeysRegistry"/>
|
||||||
</bean>
|
</bean>
|
||||||
|
|
||||||
<bean id="ssl.trustStore" class="org.alfresco.encryption.AlfrescoKeyStoreImpl" init-method="init">
|
<bean id="ssl.trustStore" class="org.alfresco.encryption.AlfrescoKeyStoreImpl" init-method="init">
|
||||||
<property name="keyStoreParameters" ref="sslTrustStoreParameters"/>
|
<property name="keyStoreParameters" ref="sslTrustStoreParameters"/>
|
||||||
<property name="keyResourceLoader" ref="springKeyResourceLoader"/>
|
<property name="keyResourceLoader" ref="springKeyResourceLoader"/>
|
||||||
|
<property name="validateKeyChanges" value="false"/>
|
||||||
|
<property name="encryptionKeysRegistry" ref="encryptionKeysRegistry"/>
|
||||||
</bean>
|
</bean>
|
||||||
<!--
|
|
||||||
<bean id="authSSLProtocolSocketFactory" class="org.alfresco.encryption.ssl.AuthSSLProtocolSocketFactory">
|
|
||||||
<property name="keyStore" ref="ssl.keyStore"/>
|
|
||||||
<property name="trustStore" ref="ssl.trustStore"/>
|
|
||||||
</bean>
|
|
||||||
-->
|
|
||||||
<bean id="md5EncryptionParameters" class="org.alfresco.httpclient.MD5EncryptionParameters">
|
<bean id="md5EncryptionParameters" class="org.alfresco.httpclient.MD5EncryptionParameters">
|
||||||
<property name="cipherAlgorithm" value="${encryption.cipherAlgorithm}"/>
|
<property name="cipherAlgorithm" value="${encryption.cipherAlgorithm}"/>
|
||||||
<property name="messageTimeout" value="${encryption.mac.messageTimeout}"/>
|
<property name="messageTimeout" value="${encryption.mac.messageTimeout}"/>
|
||||||
<property name="macAlgorithm" value="${encryption.mac.algorithm}"/>
|
<property name="macAlgorithm" value="${encryption.mac.algorithm}"/>
|
||||||
</bean>
|
</bean>
|
||||||
|
|
||||||
<bean id="springKeyResourceLoader" class="org.alfresco.encryption.SpringKeyResourceLoader">
|
<bean id="backupKeyStoreParameters" class="org.alfresco.encryption.KeyStoreParameters" init-method="init">
|
||||||
</bean>
|
|
||||||
|
|
||||||
<bean id="keyStoreParameters" class="org.alfresco.encryption.KeyStoreParameters">
|
|
||||||
<property name="name" value="Key Store"/>
|
|
||||||
<property name="location" value="${encryption.keystore.location}"/>
|
|
||||||
<property name="provider" value="${encryption.keystore.provider}"/>
|
|
||||||
<property name="type" value="${encryption.keystore.type}"/>
|
|
||||||
<property name="keyMetaDataFileLocation" value="${encryption.keystore.keyMetaData.location}"/>
|
|
||||||
</bean>
|
|
||||||
|
|
||||||
<bean id="keyStore" class="org.alfresco.encryption.AlfrescoKeyStoreImpl" init-method="init">
|
|
||||||
<property name="keyStoreParameters" ref="keyStoreParameters"/>
|
|
||||||
<property name="keyResourceLoader" ref="springKeyResourceLoader"/>
|
|
||||||
</bean>
|
|
||||||
|
|
||||||
<bean id="keyProvider" class="org.alfresco.encryption.KeystoreKeyProvider" init-method="init">
|
|
||||||
<property name="keyStore" ref="keyStore"/>
|
|
||||||
</bean>
|
|
||||||
|
|
||||||
<bean id="backupKeyStoreParameters" class="org.alfresco.encryption.KeyStoreParameters">
|
|
||||||
<property name="name" value="Backup Key Store"/>
|
<property name="name" value="Backup Key Store"/>
|
||||||
<property name="location" value="${encryption.keystore.backup.location}"/>
|
<property name="location" value="${encryption.keystore.backup.location}"/>
|
||||||
<property name="provider" value="${encryption.keystore.backup.provider}"/>
|
<property name="provider" value="${encryption.keystore.backup.provider}"/>
|
||||||
@@ -82,13 +78,21 @@
|
|||||||
<property name="keyMetaDataFileLocation" value="${encryption.keystore.backup.keyMetaData.location}"/>
|
<property name="keyMetaDataFileLocation" value="${encryption.keystore.backup.keyMetaData.location}"/>
|
||||||
</bean>
|
</bean>
|
||||||
|
|
||||||
<bean id="backupKeyStore" class="org.alfresco.encryption.AlfrescoKeyStoreImpl" init-method="init">
|
<bean id="keyStore" class="org.alfresco.encryption.AlfrescoKeyStoreImpl" init-method="init">
|
||||||
<property name="keyStoreParameters" ref="backupKeyStoreParameters"/>
|
<property name="keyStoreParameters" ref="keyStoreParameters"/>
|
||||||
|
<property name="backupKeyStoreParameters" ref="backupKeyStoreParameters"/>
|
||||||
<property name="keyResourceLoader" ref="springKeyResourceLoader"/>
|
<property name="keyResourceLoader" ref="springKeyResourceLoader"/>
|
||||||
|
<property name="validateKeyChanges" value="true"/>
|
||||||
|
<property name="encryptionKeysRegistry" ref="encryptionKeysRegistry"/>
|
||||||
|
</bean>
|
||||||
|
|
||||||
|
<bean id="keyProvider" class="org.alfresco.encryption.KeystoreKeyProvider" init-method="init">
|
||||||
|
<property name="keyStore" ref="keyStore"/>
|
||||||
</bean>
|
</bean>
|
||||||
|
|
||||||
<bean id="backupKeyProvider" class="org.alfresco.encryption.KeystoreKeyProvider" init-method="init">
|
<bean id="backupKeyProvider" class="org.alfresco.encryption.KeystoreKeyProvider" init-method="init">
|
||||||
<property name="keyStore" ref="backupKeyStore"/>
|
<property name="keyStore" ref="keyStore"/>
|
||||||
|
<property name="useBackupKeys" value="true"/>
|
||||||
</bean>
|
</bean>
|
||||||
|
|
||||||
<bean id="backupEncryptor" class="org.alfresco.encryption.DefaultEncryptor" init-method="init">
|
<bean id="backupEncryptor" class="org.alfresco.encryption.DefaultEncryptor" init-method="init">
|
||||||
@@ -96,24 +100,17 @@
|
|||||||
<property name="cipherAlgorithm" value="${encryption.cipherAlgorithm}" />
|
<property name="cipherAlgorithm" value="${encryption.cipherAlgorithm}" />
|
||||||
</bean>
|
</bean>
|
||||||
|
|
||||||
<bean id="encryptor" class="org.alfresco.encryption.DefaultEncryptor" init-method="init">
|
<bean id="mainEncryptor" class="org.alfresco.encryption.DefaultEncryptor" init-method="init">
|
||||||
<property name="keyProvider" ref="keyProvider"/>
|
<property name="keyProvider" ref="keyProvider"/>
|
||||||
<property name="cipherAlgorithm" value="${encryption.cipherAlgorithm}" />
|
<property name="cipherAlgorithm" value="${encryption.cipherAlgorithm}" />
|
||||||
</bean>
|
</bean>
|
||||||
|
|
||||||
<bean id="fallbackEncryptor" class="org.alfresco.encryption.DefaultFallbackEncryptor">
|
<bean id="encryptor" class="org.alfresco.encryption.DefaultFallbackEncryptor">
|
||||||
<property name="main" ref="encryptor"/>
|
<property name="main" ref="mainEncryptor"/>
|
||||||
<property name="fallback" ref="backupEncryptor" />
|
<property name="fallback" ref="backupEncryptor" />
|
||||||
</bean>
|
</bean>
|
||||||
|
|
||||||
<bean id="encryptionKeysRegistry" class="org.alfresco.encryption.EncryptionKeysRegistryImpl">
|
|
||||||
<property name="transactionService" ref="transactionService"/>
|
|
||||||
<property name="attributeService" ref="attributeService"/>
|
|
||||||
<property name="encryptor" ref="encryptor"/>
|
|
||||||
</bean>
|
|
||||||
|
|
||||||
<bean id="keyStoreChecker" class="org.alfresco.encryption.KeyStoreChecker">
|
<bean id="keyStoreChecker" class="org.alfresco.encryption.KeyStoreChecker">
|
||||||
<property name="transactionService" ref="transactionService"/>
|
<property name="mainKeyStore" ref="keyStore"/>
|
||||||
<property name="encryptionKeysRegistry" ref="encryptionKeysRegistry"/>
|
|
||||||
</bean>
|
</bean>
|
||||||
</beans>
|
</beans>
|
||||||
|
@@ -247,13 +247,16 @@
|
|||||||
<!-- Encryptor for node properties -->
|
<!-- Encryptor for node properties -->
|
||||||
<bean id="metadataEncryptor" class="org.alfresco.repo.node.encryption.MetadataEncryptor">
|
<bean id="metadataEncryptor" class="org.alfresco.repo.node.encryption.MetadataEncryptor">
|
||||||
<property name="dictionaryService" ref="dictionaryService" />
|
<property name="dictionaryService" ref="dictionaryService" />
|
||||||
<property name="encryptor" ref="fallbackEncryptor" />
|
<property name="encryptor" ref="encryptor" />
|
||||||
</bean>
|
</bean>
|
||||||
|
|
||||||
<bean id="reEncryptor" class="org.alfresco.encryption.ReEncryptor">
|
<bean id="reEncryptor" class="org.alfresco.encryption.ReEncryptor">
|
||||||
<property name="chunkSize" value="${encryption.reencryptor.chunkSize}"/>
|
<property name="chunkSize" value="${encryption.reencryptor.chunkSize}"/>
|
||||||
|
<property name="numThreads" value="${encryption.reencryptor.numThreads}"/>
|
||||||
|
<!--
|
||||||
<property name="backupKeyProvider" ref="backupKeyProvider"/>
|
<property name="backupKeyProvider" ref="backupKeyProvider"/>
|
||||||
<property name="keyProvider" ref="keyProvider"/>
|
<property name="keyProvider" ref="keyProvider"/>
|
||||||
|
-->
|
||||||
<property name="transactionService" ref="transactionService"/>
|
<property name="transactionService" ref="transactionService"/>
|
||||||
<property name="nodeDAO" ref="nodeDAO"/>
|
<property name="nodeDAO" ref="nodeDAO"/>
|
||||||
<property name="dictionaryDAO" ref="dictionaryDAO"/>
|
<property name="dictionaryDAO" ref="dictionaryDAO"/>
|
||||||
|
@@ -731,7 +731,9 @@ encryption.ssl.truststore.provider=
|
|||||||
encryption.ssl.truststore.type=JCEKS
|
encryption.ssl.truststore.type=JCEKS
|
||||||
encryption.ssl.truststore.keyMetaData.location=${dir.keystore}/ssl-truststore-passwords.properties
|
encryption.ssl.truststore.keyMetaData.location=${dir.keystore}/ssl-truststore-passwords.properties
|
||||||
|
|
||||||
encryption.reencryptor.chunkSize=50
|
# Re-encryptor properties
|
||||||
|
encryption.reencryptor.chunkSize=100
|
||||||
|
encryption.reencryptor.numThreads=2
|
||||||
|
|
||||||
# SOLR connection details (e.g. for JMX)
|
# SOLR connection details (e.g. for JMX)
|
||||||
solr.host=localhost
|
solr.host=localhost
|
||||||
|
@@ -52,7 +52,7 @@ public class BootstrapReEncryptor extends AbstractLifecycleBean
|
|||||||
{
|
{
|
||||||
return reEncryptor.bootstrapReEncrypt();
|
return reEncryptor.bootstrapReEncrypt();
|
||||||
}
|
}
|
||||||
catch(MissingKeyStoreException e)
|
catch(MissingKeyException e)
|
||||||
{
|
{
|
||||||
throw new AlfrescoRuntimeException("Bootstrap re-encryption failed", e);
|
throw new AlfrescoRuntimeException("Bootstrap re-encryption failed", e);
|
||||||
}
|
}
|
||||||
|
@@ -18,6 +18,9 @@
|
|||||||
*/
|
*/
|
||||||
package org.alfresco.encryption;
|
package org.alfresco.encryption;
|
||||||
|
|
||||||
|
import org.alfresco.error.AlfrescoRuntimeException;
|
||||||
|
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
|
||||||
|
import org.alfresco.service.transaction.TransactionService;
|
||||||
import org.apache.commons.logging.Log;
|
import org.apache.commons.logging.Log;
|
||||||
import org.apache.commons.logging.LogFactory;
|
import org.apache.commons.logging.LogFactory;
|
||||||
import org.springframework.context.ApplicationEvent;
|
import org.springframework.context.ApplicationEvent;
|
||||||
@@ -37,31 +40,39 @@ import org.springframework.extensions.surf.util.AbstractLifecycleBean;
|
|||||||
public class EncryptionChecker extends AbstractLifecycleBean
|
public class EncryptionChecker extends AbstractLifecycleBean
|
||||||
{
|
{
|
||||||
private static Log logger = LogFactory. getLog(EncryptionChecker.class);
|
private static Log logger = LogFactory. getLog(EncryptionChecker.class);
|
||||||
|
private TransactionService transactionService;
|
||||||
private KeyStoreChecker keyStoreChecker;
|
private KeyStoreChecker keyStoreChecker;
|
||||||
private KeyStoreParameters keyStoreParameters;
|
|
||||||
private KeyResourceLoader keyResourceLoader;
|
|
||||||
|
|
||||||
public void setkeyStoreParameters(KeyStoreParameters keyStoreParameters)
|
|
||||||
{
|
|
||||||
this.keyStoreParameters = keyStoreParameters;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setKeyStoreChecker(KeyStoreChecker keyStoreChecker)
|
public void setKeyStoreChecker(KeyStoreChecker keyStoreChecker)
|
||||||
{
|
{
|
||||||
this.keyStoreChecker = keyStoreChecker;
|
this.keyStoreChecker = keyStoreChecker;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setKeyResourceLoader(KeyResourceLoader keyResourceLoader)
|
public void setTransactionService(TransactionService transactionService)
|
||||||
{
|
{
|
||||||
this.keyResourceLoader = keyResourceLoader;
|
this.transactionService = transactionService;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onBootstrap(ApplicationEvent event)
|
protected void onBootstrap(ApplicationEvent event)
|
||||||
{
|
{
|
||||||
AlfrescoKeyStore mainKeyStore = new AlfrescoKeyStoreImpl(keyStoreParameters, keyResourceLoader);
|
transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionCallback<Void>()
|
||||||
keyStoreChecker.checkKeyStore(mainKeyStore);
|
{
|
||||||
|
public Void execute() throws Throwable
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
keyStoreChecker.validateKeyStores();
|
||||||
|
}
|
||||||
|
catch(Throwable e)
|
||||||
|
{
|
||||||
|
// Just throw as a runtime exception
|
||||||
|
throw new AlfrescoRuntimeException("Keystores are invalid", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@@ -20,8 +20,10 @@ package org.alfresco.encryption;
|
|||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.security.InvalidKeyException;
|
import java.security.InvalidKeyException;
|
||||||
|
import java.security.Key;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
import org.alfresco.repo.transaction.RetryingTransactionHelper;
|
import org.alfresco.repo.transaction.RetryingTransactionHelper;
|
||||||
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
|
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
|
||||||
@@ -34,6 +36,8 @@ import org.apache.commons.logging.Log;
|
|||||||
import org.apache.commons.logging.LogFactory;
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Registered Encryption Keys are stored in the AttributeService directly under a top level key defined by
|
||||||
|
* TOP_LEVEL_KEY (which means that all key aliases must be unique across however many keystores are being used).
|
||||||
*
|
*
|
||||||
* @since 4.0
|
* @since 4.0
|
||||||
*
|
*
|
||||||
@@ -46,16 +50,22 @@ public class EncryptionKeysRegistryImpl implements EncryptionKeysRegistry
|
|||||||
|
|
||||||
private TransactionService transactionService;
|
private TransactionService transactionService;
|
||||||
private AttributeService attributeService;
|
private AttributeService attributeService;
|
||||||
private Encryptor encryptor;
|
private String cipherAlgorithm;
|
||||||
|
private String cipherProvider;
|
||||||
|
|
||||||
public void setAttributeService(AttributeService attributeService)
|
public void setAttributeService(AttributeService attributeService)
|
||||||
{
|
{
|
||||||
this.attributeService = attributeService;
|
this.attributeService = attributeService;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setEncryptor(Encryptor encryptor)
|
public void setCipherAlgorithm(String cipherAlgorithm)
|
||||||
{
|
{
|
||||||
this.encryptor = encryptor;
|
this.cipherAlgorithm = cipherAlgorithm;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCipherProvider(String cipherProvider)
|
||||||
|
{
|
||||||
|
this.cipherProvider = cipherProvider;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setTransactionService(TransactionService transactionService)
|
public void setTransactionService(TransactionService transactionService)
|
||||||
@@ -63,55 +73,87 @@ public class EncryptionKeysRegistryImpl implements EncryptionKeysRegistry
|
|||||||
this.transactionService = transactionService;
|
this.transactionService = transactionService;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void registerKey(String keyAlias)
|
protected Encryptor getEncryptor(final KeyMap keys)
|
||||||
{
|
{
|
||||||
|
DefaultEncryptor encryptor = new DefaultEncryptor();
|
||||||
|
encryptor.setCipherAlgorithm(cipherAlgorithm);
|
||||||
|
encryptor.setCipherProvider(cipherProvider);
|
||||||
|
encryptor.setKeyProvider(new KeyProvider()
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public Key getKey(String keyAlias)
|
||||||
|
{
|
||||||
|
return keys.getCachedKey(keyAlias).getKey();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return encryptor;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void init()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public void registerKey(String keyAlias, Key key)
|
||||||
|
{
|
||||||
|
if(isKeyRegistered(keyAlias))
|
||||||
|
{
|
||||||
|
throw new IllegalArgumentException("Key " + keyAlias + " is already registered");
|
||||||
|
}
|
||||||
|
|
||||||
// register the key by creating an attribute that stores a guid and its encrypted value
|
// register the key by creating an attribute that stores a guid and its encrypted value
|
||||||
String guid = GUID.generate();
|
String guid = GUID.generate();
|
||||||
|
|
||||||
|
KeyMap keys = new KeyMap();
|
||||||
|
keys.setKey(keyAlias, key);
|
||||||
|
Encryptor encryptor = getEncryptor(keys);
|
||||||
Serializable encrypted = encryptor.sealObject(keyAlias, null, guid);
|
Serializable encrypted = encryptor.sealObject(keyAlias, null, guid);
|
||||||
KeyCheck keyCheck = new KeyCheck(guid, encrypted);
|
KeyCheck keyCheck = new KeyCheck(guid, encrypted);
|
||||||
attributeService.createAttribute(keyCheck, TOP_LEVEL_KEY, keyAlias);
|
attributeService.createAttribute(keyCheck, TOP_LEVEL_KEY, keyAlias);
|
||||||
logger.info("Registered key " + keyAlias);
|
logger.info("Registered key " + keyAlias);
|
||||||
}
|
}
|
||||||
|
|
||||||
public KeysReport getKeysReport()
|
public void unregisterKey(String keyAlias)
|
||||||
|
{
|
||||||
|
attributeService.removeAttribute(TOP_LEVEL_KEY, keyAlias);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isKeyRegistered(String keyAlias)
|
||||||
|
{
|
||||||
|
return (attributeService.getAttribute(TOP_LEVEL_KEY, keyAlias) != null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getRegisteredKeys(final Set<String> keyStoreKeys)
|
||||||
{
|
{
|
||||||
final List<String> registeredKeys = new ArrayList<String>();
|
final List<String> registeredKeys = new ArrayList<String>();
|
||||||
|
|
||||||
if(attributeService.exists(TOP_LEVEL_KEY))
|
|
||||||
{
|
|
||||||
attributeService.getAttributes(new AttributeQueryCallback()
|
attributeService.getAttributes(new AttributeQueryCallback()
|
||||||
{
|
{
|
||||||
public boolean handleAttribute(Long id, Serializable value,
|
public boolean handleAttribute(Long id, Serializable value,
|
||||||
Serializable[] keys)
|
Serializable[] keys)
|
||||||
{
|
{
|
||||||
registeredKeys.add((String)value);
|
if(value instanceof KeyCheck)
|
||||||
|
{
|
||||||
|
// Add as a registered key if the keystore contains the key
|
||||||
|
String keyAlias = (String)keys[1];
|
||||||
|
if(keyStoreKeys.contains(keyAlias))
|
||||||
|
{
|
||||||
|
registeredKeys.add(keyAlias);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
logger.warn("Unexpected value class in keys registry: " + value.getClass());
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
TOP_LEVEL_KEY);
|
TOP_LEVEL_KEY);
|
||||||
|
|
||||||
|
return registeredKeys;
|
||||||
}
|
}
|
||||||
|
|
||||||
List<String> keyAliasesChanged = new ArrayList<String>();
|
public KEY_STATUS checkKey(String keyAlias, Key key)
|
||||||
List<String> keyAliasesUnchanged = new ArrayList<String>();
|
|
||||||
|
|
||||||
for(String keyAlias : registeredKeys)
|
|
||||||
{
|
|
||||||
KEY_STATUS keyStatus = checkKey(keyAlias);
|
|
||||||
if(keyStatus == KEY_STATUS.CHANGED)
|
|
||||||
{
|
|
||||||
keyAliasesChanged.add(keyAlias);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
keyAliasesUnchanged.add(keyAlias);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return new KeysReport(keyAliasesChanged, keyAliasesUnchanged);
|
|
||||||
}
|
|
||||||
|
|
||||||
public KEY_STATUS checkKey(String keyAlias)
|
|
||||||
{
|
{
|
||||||
if(attributeService.exists(TOP_LEVEL_KEY, keyAlias))
|
if(attributeService.exists(TOP_LEVEL_KEY, keyAlias))
|
||||||
{
|
{
|
||||||
@@ -120,6 +162,10 @@ public class EncryptionKeysRegistryImpl implements EncryptionKeysRegistry
|
|||||||
// check that the key has not changed by decrypting the encrypted guid attribute
|
// check that the key has not changed by decrypting the encrypted guid attribute
|
||||||
// comparing against the guid
|
// comparing against the guid
|
||||||
KeyCheck keyCheck = (KeyCheck)attributeService.getAttribute(TOP_LEVEL_KEY, keyAlias);
|
KeyCheck keyCheck = (KeyCheck)attributeService.getAttribute(TOP_LEVEL_KEY, keyAlias);
|
||||||
|
|
||||||
|
KeyMap keys = new KeyMap();
|
||||||
|
keys.setKey(keyAlias, key);
|
||||||
|
Encryptor encryptor = getEncryptor(keys);
|
||||||
Serializable storedGUID = encryptor.unsealObject(keyAlias, keyCheck.getEncrypted());
|
Serializable storedGUID = encryptor.unsealObject(keyAlias, keyCheck.getEncrypted());
|
||||||
return EqualsHelper.nullSafeEquals(storedGUID, keyCheck.getGuid()) ? KEY_STATUS.OK : KEY_STATUS.CHANGED;
|
return EqualsHelper.nullSafeEquals(storedGUID, keyCheck.getGuid()) ? KEY_STATUS.OK : KEY_STATUS.CHANGED;
|
||||||
}
|
}
|
||||||
@@ -137,14 +183,14 @@ public class EncryptionKeysRegistryImpl implements EncryptionKeysRegistry
|
|||||||
}
|
}
|
||||||
|
|
||||||
// note that this removes _all_ keys in the keystore. Use with care.
|
// note that this removes _all_ keys in the keystore. Use with care.
|
||||||
public void removeRegisteredKeys(final AlfrescoKeyStore keyStore)
|
public void removeRegisteredKeys(final Set<String> keys)
|
||||||
{
|
{
|
||||||
RetryingTransactionHelper retryingTransactionHelper = transactionService.getRetryingTransactionHelper();
|
RetryingTransactionHelper retryingTransactionHelper = transactionService.getRetryingTransactionHelper();
|
||||||
final RetryingTransactionCallback<Void> removeKeysCallback = new RetryingTransactionCallback<Void>()
|
final RetryingTransactionCallback<Void> removeKeysCallback = new RetryingTransactionCallback<Void>()
|
||||||
{
|
{
|
||||||
public Void execute() throws Throwable
|
public Void execute() throws Throwable
|
||||||
{
|
{
|
||||||
for(String keyAlias : keyStore.getKeyAliases())
|
for(String keyAlias : keys)
|
||||||
{
|
{
|
||||||
attributeService.removeAttribute(TOP_LEVEL_KEY, keyAlias);
|
attributeService.removeAttribute(TOP_LEVEL_KEY, keyAlias);
|
||||||
}
|
}
|
||||||
@@ -161,7 +207,7 @@ public class EncryptionKeysRegistryImpl implements EncryptionKeysRegistry
|
|||||||
* @since 4.0
|
* @since 4.0
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
private static class KeyCheck implements Serializable
|
public static class KeyCheck implements Serializable
|
||||||
{
|
{
|
||||||
private static final long serialVersionUID = 4514315444977162903L;
|
private static final long serialVersionUID = 4514315444977162903L;
|
||||||
|
|
||||||
@@ -201,4 +247,5 @@ public class EncryptionKeysRegistryImpl implements EncryptionKeysRegistry
|
|||||||
EqualsHelper.nullSafeEquals(keyCheck.getEncrypted(), getEncrypted());
|
EqualsHelper.nullSafeEquals(keyCheck.getEncrypted(), getEncrypted());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -18,8 +18,8 @@
|
|||||||
*/
|
*/
|
||||||
package org.alfresco.encryption;
|
package org.alfresco.encryption;
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
import java.security.AlgorithmParameters;
|
||||||
import java.security.InvalidKeyException;
|
import java.security.InvalidKeyException;
|
||||||
import java.security.Key;
|
import java.security.Key;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
@@ -51,6 +51,7 @@ import org.alfresco.service.cmr.repository.StoreRef;
|
|||||||
import org.alfresco.service.namespace.QName;
|
import org.alfresco.service.namespace.QName;
|
||||||
import org.alfresco.service.transaction.TransactionService;
|
import org.alfresco.service.transaction.TransactionService;
|
||||||
import org.alfresco.util.ApplicationContextHelper;
|
import org.alfresco.util.ApplicationContextHelper;
|
||||||
|
import org.alfresco.util.Pair;
|
||||||
import org.springframework.context.ApplicationContext;
|
import org.springframework.context.ApplicationContext;
|
||||||
|
|
||||||
public class EncryptionTests extends TestCase
|
public class EncryptionTests extends TestCase
|
||||||
@@ -72,7 +73,8 @@ public class EncryptionTests extends TestCase
|
|||||||
private ReEncryptor reEncryptor;
|
private ReEncryptor reEncryptor;
|
||||||
private String cipherAlgorithm = "DESede/CBC/PKCS5Padding";
|
private String cipherAlgorithm = "DESede/CBC/PKCS5Padding";
|
||||||
private KeyStoreParameters backupKeyStoreParameters;
|
private KeyStoreParameters backupKeyStoreParameters;
|
||||||
private AlfrescoKeyStoreImpl backupKeyStore;
|
private AlfrescoKeyStoreImpl mainKeyStore;
|
||||||
|
//private AlfrescoKeyStoreImpl backupKeyStore;
|
||||||
private KeyResourceLoader keyResourceLoader;
|
private KeyResourceLoader keyResourceLoader;
|
||||||
private EncryptionKeysRegistryImpl encryptionKeysRegistry;
|
private EncryptionKeysRegistryImpl encryptionKeysRegistry;
|
||||||
private KeyStoreChecker keyStoreChecker;
|
private KeyStoreChecker keyStoreChecker;
|
||||||
@@ -84,7 +86,7 @@ public class EncryptionTests extends TestCase
|
|||||||
private TenantService tenantService;
|
private TenantService tenantService;
|
||||||
|
|
||||||
private String keyAlgorithm;
|
private String keyAlgorithm;
|
||||||
private Map<String, Key> newKeys = new HashMap<String, Key>();
|
private KeyMap newKeys = new KeyMap();
|
||||||
private List<NodeRef> before = new ArrayList<NodeRef>();
|
private List<NodeRef> before = new ArrayList<NodeRef>();
|
||||||
private List<NodeRef> after = new ArrayList<NodeRef>();
|
private List<NodeRef> after = new ArrayList<NodeRef>();
|
||||||
|
|
||||||
@@ -104,8 +106,9 @@ public class EncryptionTests extends TestCase
|
|||||||
backupKeyStoreParameters = (KeyStoreParameters)ctx.getBean("backupKeyStoreParameters");
|
backupKeyStoreParameters = (KeyStoreParameters)ctx.getBean("backupKeyStoreParameters");
|
||||||
keyStoreChecker = (KeyStoreChecker)ctx.getBean("keyStoreChecker");
|
keyStoreChecker = (KeyStoreChecker)ctx.getBean("keyStoreChecker");
|
||||||
encryptionKeysRegistry = (EncryptionKeysRegistryImpl)ctx.getBean("encryptionKeysRegistry");
|
encryptionKeysRegistry = (EncryptionKeysRegistryImpl)ctx.getBean("encryptionKeysRegistry");
|
||||||
backupKeyStore = (AlfrescoKeyStoreImpl)ctx.getBean("backupKeyStore");
|
//backupKeyStore = (AlfrescoKeyStoreImpl)ctx.getBean("backupKeyStore");
|
||||||
mainEncryptor = (DefaultEncryptor)ctx.getBean("encryptor");
|
mainKeyStore = (AlfrescoKeyStoreImpl)ctx.getBean("keyStore");
|
||||||
|
mainEncryptor = (DefaultEncryptor)ctx.getBean("mainEncryptor");
|
||||||
backupEncryptor = (DefaultEncryptor)ctx.getBean("backupEncryptor");
|
backupEncryptor = (DefaultEncryptor)ctx.getBean("backupEncryptor");
|
||||||
|
|
||||||
// reencrypt in one txn (since we don't commit the model, the qnames won't be available across transactions)
|
// reencrypt in one txn (since we don't commit the model, the qnames won't be available across transactions)
|
||||||
@@ -122,7 +125,7 @@ public class EncryptionTests extends TestCase
|
|||||||
rootNodeRef = nodeService.getRootNode(storeRef);
|
rootNodeRef = nodeService.getRootNode(storeRef);
|
||||||
|
|
||||||
keyAlgorithm = "DESede";
|
keyAlgorithm = "DESede";
|
||||||
newKeys.put(KeyProvider.ALIAS_METADATA, generateSecretKey(keyAlgorithm));
|
newKeys.setKey(KeyProvider.ALIAS_METADATA, generateSecretKey(keyAlgorithm));
|
||||||
|
|
||||||
// Load models
|
// Load models
|
||||||
DictionaryBootstrap bootstrap = new DictionaryBootstrap();
|
DictionaryBootstrap bootstrap = new DictionaryBootstrap();
|
||||||
@@ -156,46 +159,19 @@ public class EncryptionTests extends TestCase
|
|||||||
super.tearDown();
|
super.tearDown();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected KeyProvider getKeyProvider(final Map<String, Key> keys)
|
protected KeyProvider getKeyProvider(final KeyMap keys)
|
||||||
{
|
{
|
||||||
KeyProvider keyProvider = new KeyProvider()
|
KeyProvider keyProvider = new KeyProvider()
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
public Key getKey(String keyAlias)
|
public Key getKey(String keyAlias)
|
||||||
{
|
{
|
||||||
return keys.get(keyAlias);
|
return keys.getCachedKey(keyAlias).getKey();
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void refresh()
|
|
||||||
{
|
|
||||||
// nothing to do
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
return keyProvider;
|
return keyProvider;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Encryptor getFallbackEncryptor(KeyProvider keyProvider)
|
|
||||||
{
|
|
||||||
DefaultEncryptor encryptor = new DefaultEncryptor();
|
|
||||||
encryptor.setCipherAlgorithm(cipherAlgorithm);
|
|
||||||
encryptor.setCipherProvider(null);
|
|
||||||
encryptor.setKeyProvider(keyProvider);
|
|
||||||
|
|
||||||
DefaultFallbackEncryptor fallbackEncryptor = new DefaultFallbackEncryptor(encryptor, mainEncryptor);
|
|
||||||
|
|
||||||
return fallbackEncryptor;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected MetadataEncryptor getMetadataEncryptor(Encryptor encryptor)
|
|
||||||
{
|
|
||||||
MetadataEncryptor metadataEncryptor = new MetadataEncryptor();
|
|
||||||
metadataEncryptor.setDictionaryService(dictionaryService);
|
|
||||||
metadataEncryptor.setEncryptor(encryptor);
|
|
||||||
|
|
||||||
return metadataEncryptor;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void createEncryptedProperties(List<NodeRef> nodes)
|
protected void createEncryptedProperties(List<NodeRef> nodes)
|
||||||
{
|
{
|
||||||
for(int i = 0; i < NUM_PROPERTIES; i++)
|
for(int i = 0; i < NUM_PROPERTIES; i++)
|
||||||
@@ -269,7 +245,7 @@ public class EncryptionTests extends TestCase
|
|||||||
assertEquals("", nodeRef.toString(), props.get(PROP));
|
assertEquals("", nodeRef.toString(), props.get(PROP));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch(MissingKeyStoreException e)
|
catch(MissingKeyException e)
|
||||||
{
|
{
|
||||||
fail(e.getMessage());
|
fail(e.getMessage());
|
||||||
}
|
}
|
||||||
@@ -294,36 +270,142 @@ public class EncryptionTests extends TestCase
|
|||||||
{
|
{
|
||||||
// ensure that the backup key store is not available
|
// ensure that the backup key store is not available
|
||||||
backupKeyStoreParameters.setLocation("");
|
backupKeyStoreParameters.setLocation("");
|
||||||
backupKeyStore.reload();
|
//backupKeyStore.reload();
|
||||||
|
mainKeyStore.reload();
|
||||||
|
|
||||||
reEncryptor.bootstrapReEncrypt();
|
reEncryptor.bootstrapReEncrypt();
|
||||||
fail("Should have caught missing backup key store");
|
fail("Should have caught missing backup key store");
|
||||||
}
|
}
|
||||||
catch(MissingKeyStoreException e)
|
catch(MissingKeyException e)
|
||||||
{
|
{
|
||||||
System.out.println("Successfully caught missing key store exception");
|
System.out.println("Successfully caught missing key exception");
|
||||||
}
|
}
|
||||||
}
|
catch(InvalidKeystoreException e)
|
||||||
|
|
||||||
public void testKeyStoreCreation()
|
|
||||||
{
|
{
|
||||||
String keyStoreLocation = System.getProperty("user.dir") + File.separator + "encryption-tests.keystore";
|
fail("Unexpected exception: " + e.getMessage());
|
||||||
File keyStoreFile = new File(keyStoreLocation);
|
}
|
||||||
if(keyStoreFile.exists())
|
}
|
||||||
|
|
||||||
|
protected void testChangeKeysImpl(boolean cacheCiphers) throws Throwable
|
||||||
{
|
{
|
||||||
assertTrue("", keyStoreFile.delete());
|
// on a single thread
|
||||||
|
// create an encryptor, encrypt a string, change encryptor keys, decrypt -> should result in Invalid Key
|
||||||
|
|
||||||
|
Pair<byte[], AlgorithmParameters> pair = null;
|
||||||
|
DefaultEncryptor encryptor = null;
|
||||||
|
Key secretKey1 = null;
|
||||||
|
Key secretKey2 = null;
|
||||||
|
String test = "hello world";
|
||||||
|
final KeyMap keys = new KeyMap();
|
||||||
|
byte[] decrypted = null;
|
||||||
|
String test1 = null;
|
||||||
|
|
||||||
|
secretKey1 = generateSecretKey("DESede");
|
||||||
|
keys.setKey("test", secretKey1);
|
||||||
|
KeyProvider keyProvider = new KeyProvider()
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public Key getKey(String keyAlias)
|
||||||
|
{
|
||||||
|
return keys.getCachedKey(keyAlias).getKey();
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
KeyStoreParameters keyStoreParameters = new KeyStoreParameters();
|
encryptor = new DefaultEncryptor();
|
||||||
keyStoreParameters.setLocation(keyStoreLocation);
|
encryptor.setCipherAlgorithm("DESede/CBC/PKCS5Padding");
|
||||||
keyStoreParameters.setKeyMetaDataFileLocation("classpath:org/alfresco/encryption/keystore-parameters.properties");
|
encryptor.setCipherProvider(null);
|
||||||
keyStoreParameters.setType("JCEKS");
|
encryptor.setKeyProvider(keyProvider);
|
||||||
AlfrescoKeyStore keyStore = new AlfrescoKeyStoreImpl(keyStoreParameters, keyResourceLoader);
|
encryptor.setCacheCiphers(cacheCiphers);
|
||||||
|
pair = encryptor.encrypt("test", null, test.getBytes("UTF-8"));
|
||||||
|
|
||||||
encryptionKeysRegistry.removeRegisteredKeys(keyStore);
|
decrypted = encryptor.decrypt("test", pair.getSecond(), pair.getFirst());
|
||||||
|
test1 = new String(decrypted, "UTF-8");
|
||||||
|
|
||||||
keyStoreChecker.checkKeyStore(keyStore);
|
assertEquals("Expected encrypt,decrypt to end up with the original value", test, test1);
|
||||||
|
System.out.println("1:" + new String(decrypted, "UTF-8"));
|
||||||
|
|
||||||
assertNotNull("", keyStore.getKey("test"));
|
secretKey2 = generateSecretKey("DESede");
|
||||||
|
keys.setKey("test", secretKey2);
|
||||||
|
|
||||||
|
assertNotNull(encryptor);
|
||||||
|
assertNotNull(pair);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
decrypted = encryptor.decrypt("test", pair.getSecond(), pair.getFirst());
|
||||||
|
test1 = new String(decrypted, "UTF-8");
|
||||||
|
}
|
||||||
|
catch(AlfrescoRuntimeException e)
|
||||||
|
{
|
||||||
|
// ok - decryption failed expected.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testChangeKeys() throws Throwable
|
||||||
|
{
|
||||||
|
testChangeKeysImpl(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testChangeKeysCachedCiphers() throws Throwable
|
||||||
|
{
|
||||||
|
testChangeKeysImpl(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testFailedEncryptionWithCachedCiphers() throws Throwable
|
||||||
|
{
|
||||||
|
Pair<byte[], AlgorithmParameters> pair = null;
|
||||||
|
DefaultEncryptor encryptor = null;
|
||||||
|
Key secretKey1 = null;
|
||||||
|
Key secretKey2 = null;
|
||||||
|
String test = "hello world";
|
||||||
|
final KeyMap keys = new KeyMap();
|
||||||
|
byte[] decrypted = null;
|
||||||
|
String test1 = null;
|
||||||
|
|
||||||
|
secretKey1 = generateSecretKey("DESede");
|
||||||
|
keys.setKey("test", secretKey1);
|
||||||
|
KeyProvider keyProvider = new KeyProvider()
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public Key getKey(String keyAlias)
|
||||||
|
{
|
||||||
|
return keys.getCachedKey(keyAlias).getKey();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
encryptor = new DefaultEncryptor();
|
||||||
|
encryptor.setCipherAlgorithm("DESede/CBC/PKCS5Padding");
|
||||||
|
encryptor.setCipherProvider(null);
|
||||||
|
encryptor.setKeyProvider(keyProvider);
|
||||||
|
encryptor.setCacheCiphers(true);
|
||||||
|
pair = encryptor.encrypt("test", null, test.getBytes("UTF-8"));
|
||||||
|
|
||||||
|
secretKey2 = generateSecretKey("DESede");
|
||||||
|
keys.setKey("test", secretKey2);
|
||||||
|
|
||||||
|
assertNotNull(encryptor);
|
||||||
|
assertNotNull(pair);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
decrypted = encryptor.decrypt("test", pair.getSecond(), pair.getFirst());
|
||||||
|
test1 = new String(decrypted, "UTF-8");
|
||||||
|
fail("Decryption should have failed");
|
||||||
|
}
|
||||||
|
catch(AlfrescoRuntimeException e)
|
||||||
|
{
|
||||||
|
// ok - decryption failed expected - changed key
|
||||||
|
}
|
||||||
|
|
||||||
|
keys.setKey("test", secretKey1);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
decrypted = encryptor.decrypt("test", pair.getSecond(), pair.getFirst());
|
||||||
|
test1 = new String(decrypted, "UTF-8");
|
||||||
|
}
|
||||||
|
catch(AlfrescoRuntimeException e)
|
||||||
|
{
|
||||||
|
fail("Expected decryption to work ok");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -85,15 +85,14 @@ public class EncryptorTest extends TestCase
|
|||||||
Serializable testObject = " This is a string, but will be serialized ";
|
Serializable testObject = " This is a string, but will be serialized ";
|
||||||
|
|
||||||
Serializable sealedObject = encryptor.sealObject("mykey2", null, testObject);
|
Serializable sealedObject = encryptor.sealObject("mykey2", null, testObject);
|
||||||
// try
|
try
|
||||||
// {
|
{
|
||||||
Object output = encryptor.unsealObject("mykey2", sealedObject);
|
Object output = encryptor.unsealObject("mykey2", sealedObject);
|
||||||
assertEquals("Encryption round trip failed. ", testObject, output);
|
assertEquals("Encryption round trip failed. ", testObject, output);
|
||||||
// }
|
}
|
||||||
// catch(KeyException e)
|
catch(KeyException e)
|
||||||
// {
|
{
|
||||||
// throw new AlfrescoRuntimeException("", e)
|
throw new AlfrescoRuntimeException("", e);
|
||||||
//; }
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -18,18 +18,11 @@
|
|||||||
*/
|
*/
|
||||||
package org.alfresco.encryption;
|
package org.alfresco.encryption;
|
||||||
|
|
||||||
import org.alfresco.error.AlfrescoRuntimeException;
|
|
||||||
import org.alfresco.repo.transaction.RetryingTransactionHelper;
|
|
||||||
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
|
|
||||||
import org.alfresco.service.transaction.TransactionService;
|
|
||||||
import org.apache.commons.logging.Log;
|
import org.apache.commons.logging.Log;
|
||||||
import org.apache.commons.logging.LogFactory;
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validates an Alfresco keystore by ensuring that it's registered keys have not changed.
|
* Checks the repository key stores.
|
||||||
*
|
|
||||||
* The keys are registered using the AttributeService and are stored in one level under TOP_LEVEL_KEY
|
|
||||||
* (so key aliases must be unique across however many keystores are being used).
|
|
||||||
*
|
*
|
||||||
* @since 4.0
|
* @since 4.0
|
||||||
*
|
*
|
||||||
@@ -38,79 +31,23 @@ public class KeyStoreChecker
|
|||||||
{
|
{
|
||||||
private static final Log logger = LogFactory.getLog(KeyStoreChecker.class);
|
private static final Log logger = LogFactory.getLog(KeyStoreChecker.class);
|
||||||
|
|
||||||
private TransactionService transactionService;
|
private AlfrescoKeyStore mainKeyStore;
|
||||||
private EncryptionKeysRegistryImpl encryptionKeysRegistry;
|
|
||||||
|
|
||||||
public KeyStoreChecker()
|
public KeyStoreChecker()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setEncryptionKeysRegistry(EncryptionKeysRegistryImpl encryptionKeysRegistry)
|
public void setMainKeyStore(AlfrescoKeyStore mainKeyStore)
|
||||||
{
|
{
|
||||||
this.encryptionKeysRegistry = encryptionKeysRegistry;
|
this.mainKeyStore = mainKeyStore;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setTransactionService(TransactionService transactionService)
|
public void validateKeyStores() throws InvalidKeystoreException, MissingKeyException
|
||||||
{
|
{
|
||||||
this.transactionService = transactionService;
|
mainKeyStore.validateKeys();
|
||||||
}
|
if(!mainKeyStore.exists())
|
||||||
|
|
||||||
public KeyStoreChecker(EncryptionKeysRegistryImpl encryptionKeysRegistry)
|
|
||||||
{
|
{
|
||||||
this.encryptionKeysRegistry = encryptionKeysRegistry;
|
mainKeyStore.create();
|
||||||
}
|
|
||||||
|
|
||||||
protected void createKeyStore(AlfrescoKeyStore keyStore)
|
|
||||||
{
|
|
||||||
keyStore.create();
|
|
||||||
|
|
||||||
// Register the key store keys
|
|
||||||
for(String keyAlias : keyStore.getKeyAliases())
|
|
||||||
{
|
|
||||||
encryptionKeysRegistry.registerKey(keyAlias);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void checkKeyStore(final AlfrescoKeyStore keyStore)
|
|
||||||
{
|
|
||||||
// TODO check that, if keystore exists, keys are registered
|
|
||||||
RetryingTransactionHelper retryingTransactionHelper = transactionService.getRetryingTransactionHelper();
|
|
||||||
final RetryingTransactionCallback<Void> checkKeysCallback = new RetryingTransactionCallback<Void>()
|
|
||||||
{
|
|
||||||
public Void execute() throws Throwable
|
|
||||||
{
|
|
||||||
KeysReport keysReport = encryptionKeysRegistry.getKeysReport();
|
|
||||||
|
|
||||||
// Check for the existence of a key store first
|
|
||||||
if(keyStore.exists())
|
|
||||||
{
|
|
||||||
// The keystore exists - check whether any keys have been changed
|
|
||||||
// find out which registered keys have changed
|
|
||||||
if(keysReport.getKeysChanged().size() > 0)
|
|
||||||
{
|
|
||||||
// Note: this will halt the application bootstrap.
|
|
||||||
throw new AlfrescoRuntimeException("The keys with aliases " + keysReport.getKeysChanged() + " have been changed, re-instate the previous keystore");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// keystore not found, check whether any keys have been registered
|
|
||||||
if(keysReport.getKeysChanged().size() + keysReport.getKeysUnchanged().size() > 0)
|
|
||||||
{
|
|
||||||
// Note: this will halt the application bootstrap.
|
|
||||||
throw new AlfrescoRuntimeException("Keys have already been registered, re-instate the previous keystore");
|
|
||||||
}
|
|
||||||
|
|
||||||
if(logger.isDebugEnabled())
|
|
||||||
{
|
|
||||||
logger.debug("Keystore not found, creating...");
|
|
||||||
}
|
|
||||||
createKeyStore(keyStore);
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
retryingTransactionHelper.doInTransaction(checkKeysCallback, false);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
419
source/java/org/alfresco/encryption/KeyStoreTests.java
Normal file
419
source/java/org/alfresco/encryption/KeyStoreTests.java
Normal file
@@ -0,0 +1,419 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2005-2011 Alfresco Software Limited.
|
||||||
|
*
|
||||||
|
* This file is part of Alfresco
|
||||||
|
*
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
package org.alfresco.encryption;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertNotNull;
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -1,41 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2005-2011 Alfresco Software Limited.
|
|
||||||
*
|
|
||||||
* This file is part of Alfresco
|
|
||||||
*
|
|
||||||
* 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/>.
|
|
||||||
*/
|
|
||||||
package org.alfresco.encryption;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @since 4.0
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public class MissingKeyException extends Exception
|
|
||||||
{
|
|
||||||
private static final long serialVersionUID = -7843412242954504581L;
|
|
||||||
|
|
||||||
private String keyAlias;
|
|
||||||
|
|
||||||
public MissingKeyException(String keyAlias)
|
|
||||||
{
|
|
||||||
this.keyAlias = keyAlias;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getKeyAlias()
|
|
||||||
{
|
|
||||||
return keyAlias;
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,35 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2005-2011 Alfresco Software Limited.
|
|
||||||
*
|
|
||||||
* This file is part of Alfresco
|
|
||||||
*
|
|
||||||
* 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/>.
|
|
||||||
*/
|
|
||||||
package org.alfresco.encryption;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @since 4.0
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
public class MissingKeyStoreException extends Exception
|
|
||||||
{
|
|
||||||
private static final long serialVersionUID = -6484290539637378020L;
|
|
||||||
|
|
||||||
public MissingKeyStoreException(String message)
|
|
||||||
{
|
|
||||||
super(message);
|
|
||||||
}
|
|
||||||
}
|
|
@@ -76,13 +76,12 @@ public class ReEncryptor implements ApplicationContextAware
|
|||||||
private QNameDAO qnameDAO;
|
private QNameDAO qnameDAO;
|
||||||
|
|
||||||
private MetadataEncryptor metadataEncryptor;
|
private MetadataEncryptor metadataEncryptor;
|
||||||
private KeyProvider backupKeyProvider;
|
|
||||||
private KeyProvider keyProvider;
|
|
||||||
|
|
||||||
private ApplicationContext applicationContext;
|
private ApplicationContext applicationContext;
|
||||||
private TransactionService transactionService;
|
private TransactionService transactionService;
|
||||||
private RetryingTransactionHelper transactionHelper;
|
private RetryingTransactionHelper transactionHelper;
|
||||||
|
|
||||||
|
private int numThreads;
|
||||||
private int chunkSize;
|
private int chunkSize;
|
||||||
private boolean splitTxns = true;
|
private boolean splitTxns = true;
|
||||||
|
|
||||||
@@ -114,6 +113,11 @@ public class ReEncryptor implements ApplicationContextAware
|
|||||||
this.jobLockService = jobLockService;
|
this.jobLockService = jobLockService;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setNumThreads(int numThreads)
|
||||||
|
{
|
||||||
|
this.numThreads = numThreads;
|
||||||
|
}
|
||||||
|
|
||||||
public void setChunkSize(int chunkSize)
|
public void setChunkSize(int chunkSize)
|
||||||
{
|
{
|
||||||
this.chunkSize = chunkSize;
|
this.chunkSize = chunkSize;
|
||||||
@@ -139,16 +143,6 @@ public class ReEncryptor implements ApplicationContextAware
|
|||||||
this.qnameDAO = qnameDAO;
|
this.qnameDAO = qnameDAO;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setBackupKeyProvider(KeyProvider backupKeyProvider)
|
|
||||||
{
|
|
||||||
this.backupKeyProvider = backupKeyProvider;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setKeyProvider(KeyProvider keyProvider)
|
|
||||||
{
|
|
||||||
this.keyProvider = keyProvider;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Attempts to get the lock. If the lock couldn't be taken, then <tt>null</tt> is returned.
|
* Attempts to get the lock. If the lock couldn't be taken, then <tt>null</tt> is returned.
|
||||||
*
|
*
|
||||||
@@ -266,7 +260,7 @@ public class ReEncryptor implements ApplicationContextAware
|
|||||||
I18NUtil.getMessage("reencryptor.batchprocessor.name"), // TODO i18n name
|
I18NUtil.getMessage("reencryptor.batchprocessor.name"), // TODO i18n name
|
||||||
transactionHelper,
|
transactionHelper,
|
||||||
provider,
|
provider,
|
||||||
2, 100,
|
numThreads, chunkSize,
|
||||||
applicationContext,
|
applicationContext,
|
||||||
logger, 100).process(worker, splitTxns);
|
logger, 100).process(worker, splitTxns);
|
||||||
}
|
}
|
||||||
@@ -274,11 +268,11 @@ public class ReEncryptor implements ApplicationContextAware
|
|||||||
/**
|
/**
|
||||||
* Re-encrypt using the configured backup keystore to decrypt and the main keystore to encrypt
|
* Re-encrypt using the configured backup keystore to decrypt and the main keystore to encrypt
|
||||||
*/
|
*/
|
||||||
public int bootstrapReEncrypt() throws MissingKeyStoreException
|
public int bootstrapReEncrypt() throws MissingKeyException
|
||||||
{
|
{
|
||||||
if(backupKeyProvider.getKey(KeyProvider.ALIAS_METADATA) == null)
|
if(!metadataEncryptor.backupKeyAvailable(KeyProvider.ALIAS_METADATA))
|
||||||
{
|
{
|
||||||
throw new MissingKeyStoreException("Backup key store is either not present or does not contain a metadata encryption key");
|
throw new MissingKeyException("Backup key store is either not present or does not contain a metadata encryption key");
|
||||||
}
|
}
|
||||||
return reEncrypt();
|
return reEncrypt();
|
||||||
}
|
}
|
||||||
@@ -287,23 +281,21 @@ public class ReEncryptor implements ApplicationContextAware
|
|||||||
* Re-encrypt by decrypting using the configured keystore and encrypting using a keystore configured using the provided new key store parameters.
|
* Re-encrypt by decrypting using the configured keystore and encrypting using a keystore configured using the provided new key store parameters.
|
||||||
* Called from e.g. JMX.
|
* Called from e.g. JMX.
|
||||||
*
|
*
|
||||||
|
* Assumes that the main key store has been already been reloaded.
|
||||||
|
*
|
||||||
* Note: it is the responsibility of the end user to ensure that the underlying keystores have been set up appropriately
|
* Note: it is the responsibility of the end user to ensure that the underlying keystores have been set up appropriately
|
||||||
* i.e. the old key store is backed up to the location defined by the property '${dir.keystore}/backup-keystore' and the new
|
* i.e. the old key store is backed up to the location defined by the property '${dir.keystore}/backup-keystore' and the new
|
||||||
* key store replaces it. This can be done while the repository is running.
|
* key store replaces it. This can be done while the repository is running.
|
||||||
*/
|
*/
|
||||||
public int reEncrypt() throws MissingKeyStoreException
|
public int reEncrypt() throws MissingKeyException
|
||||||
{
|
{
|
||||||
// refresh the key providers to pick up changes made
|
if(!metadataEncryptor.keyAvailable(KeyProvider.ALIAS_METADATA))
|
||||||
backupKeyProvider.refresh();
|
|
||||||
keyProvider.refresh();
|
|
||||||
|
|
||||||
if(keyProvider.getKey(KeyProvider.ALIAS_METADATA) == null)
|
|
||||||
{
|
{
|
||||||
throw new MissingKeyStoreException("Main key store is either not present or does not contain a metadata encryption key");
|
throw new MissingKeyException("Main key store is either not present or does not contain a metadata encryption key");
|
||||||
}
|
}
|
||||||
if(backupKeyProvider.getKey(KeyProvider.ALIAS_METADATA) == null)
|
if(!metadataEncryptor.backupKeyAvailable(KeyProvider.ALIAS_METADATA))
|
||||||
{
|
{
|
||||||
throw new MissingKeyStoreException("Backup key store is either not present or does not contain a metadata encryption key");
|
throw new MissingKeyException("Backup key store is either not present or does not contain a metadata encryption key");
|
||||||
}
|
}
|
||||||
|
|
||||||
int numProps = reEncryptImpl();
|
int numProps = reEncryptImpl();
|
||||||
@@ -312,6 +304,7 @@ public class ReEncryptor implements ApplicationContextAware
|
|||||||
|
|
||||||
protected int reEncryptImpl()
|
protected int reEncryptImpl()
|
||||||
{
|
{
|
||||||
|
// Take out a re-encryptor lock
|
||||||
RetryingTransactionCallback<String> txnWork = new RetryingTransactionCallback<String>()
|
RetryingTransactionCallback<String> txnWork = new RetryingTransactionCallback<String>()
|
||||||
{
|
{
|
||||||
public String execute() throws Exception
|
public String execute() throws Exception
|
||||||
|
@@ -1532,7 +1532,6 @@ public class NodeDAOImpl extends AbstractNodeDAOImpl
|
|||||||
qnames.add(propDef.getName());
|
qnames.add(propDef.getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO use callback approach
|
|
||||||
// qnames of properties that are encrypted
|
// qnames of properties that are encrypted
|
||||||
Set<Long> qnameIds = qnameDAO.convertQNamesToIds(qnames, false);
|
Set<Long> qnameIds = qnameDAO.convertQNamesToIds(qnames, false);
|
||||||
if(qnameIds.size() > 0)
|
if(qnameIds.size() > 0)
|
||||||
|
201
source/java/org/alfresco/repo/node/MetadataEncryptorTests.java
Normal file
201
source/java/org/alfresco/repo/node/MetadataEncryptorTests.java
Normal file
@@ -0,0 +1,201 @@
|
|||||||
|
package org.alfresco.repo.node;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import junit.framework.TestCase;
|
||||||
|
|
||||||
|
import org.alfresco.model.ContentModel;
|
||||||
|
import org.alfresco.repo.dictionary.DictionaryBootstrap;
|
||||||
|
import org.alfresco.repo.dictionary.DictionaryDAO;
|
||||||
|
import org.alfresco.repo.node.encryption.MetadataEncryptor;
|
||||||
|
import org.alfresco.repo.node.integrity.IntegrityException;
|
||||||
|
import org.alfresco.repo.security.authentication.AuthenticationUtil;
|
||||||
|
import org.alfresco.repo.tenant.TenantService;
|
||||||
|
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
|
||||||
|
import org.alfresco.service.ServiceRegistry;
|
||||||
|
import org.alfresco.service.cmr.repository.NodeRef;
|
||||||
|
import org.alfresco.service.cmr.repository.NodeService;
|
||||||
|
import org.alfresco.service.cmr.repository.StoreRef;
|
||||||
|
import org.alfresco.service.namespace.NamespaceService;
|
||||||
|
import org.alfresco.service.namespace.QName;
|
||||||
|
import org.alfresco.service.transaction.TransactionService;
|
||||||
|
import org.alfresco.util.ApplicationContextHelper;
|
||||||
|
import org.alfresco.util.PropertyMap;
|
||||||
|
import org.apache.commons.logging.Log;
|
||||||
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
import org.springframework.context.ConfigurableApplicationContext;
|
||||||
|
import org.springframework.extensions.surf.util.I18NUtil;
|
||||||
|
|
||||||
|
public class MetadataEncryptorTests extends TestCase
|
||||||
|
{
|
||||||
|
private static final String TEST_MODEL = "org/alfresco/repo/node/encrypted_prop_model.xml";
|
||||||
|
private static QName ENCRYPTED_TYPE_QNAME = QName.createQName("http://www.alfresco.org/test/encryptedPropModel/1.0", "encrypted");
|
||||||
|
private static QName ENCRYPTED_PROP_QNAME = QName.createQName("http://www.alfresco.org/test/encryptedPropModel/1.0", "prop1");
|
||||||
|
|
||||||
|
private static final Log logger = LogFactory.getLog(MetadataEncryptorTests.class);
|
||||||
|
|
||||||
|
private ConfigurableApplicationContext ctx = (ConfigurableApplicationContext) ApplicationContextHelper.getApplicationContext();
|
||||||
|
|
||||||
|
private TransactionService transactionService;
|
||||||
|
private NodeService nodeService;
|
||||||
|
private TenantService tenantService;
|
||||||
|
private DictionaryDAO dictionaryDAO;
|
||||||
|
private MetadataEncryptor metadataEncryptor;
|
||||||
|
|
||||||
|
private StoreRef storeRef;
|
||||||
|
private NodeRef rootNodeRef;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setUp() throws Exception
|
||||||
|
{
|
||||||
|
ServiceRegistry serviceRegistry = (ServiceRegistry) ctx.getBean(ServiceRegistry.SERVICE_REGISTRY);
|
||||||
|
transactionService = serviceRegistry.getTransactionService();
|
||||||
|
// txnHelper = transactionService.getRetryingTransactionHelper();
|
||||||
|
metadataEncryptor = (MetadataEncryptor)ctx.getBean("metadataEncryptor");
|
||||||
|
nodeService = serviceRegistry.getNodeService();
|
||||||
|
tenantService = (TenantService)ctx.getBean("tenantService");
|
||||||
|
dictionaryDAO = (DictionaryDAO)ctx.getBean("dictionaryDAO");
|
||||||
|
|
||||||
|
AuthenticationUtil.setRunAsUserSystem();
|
||||||
|
|
||||||
|
DictionaryBootstrap bootstrap = new DictionaryBootstrap();
|
||||||
|
List<String> bootstrapModels = new ArrayList<String>();
|
||||||
|
bootstrapModels.add("alfresco/model/dictionaryModel.xml");
|
||||||
|
bootstrapModels.add(TEST_MODEL);
|
||||||
|
// List<String> labels = new ArrayList<String>();
|
||||||
|
// labels.add(TEST_BUNDLE);
|
||||||
|
bootstrap.setModels(bootstrapModels);
|
||||||
|
// bootstrap.setLabels(labels);
|
||||||
|
bootstrap.setDictionaryDAO(dictionaryDAO);
|
||||||
|
bootstrap.setTenantService(tenantService);
|
||||||
|
bootstrap.bootstrap();
|
||||||
|
|
||||||
|
// create a first store directly
|
||||||
|
RetryingTransactionCallback<NodeRef> createStoreWork = new RetryingTransactionCallback<NodeRef>()
|
||||||
|
{
|
||||||
|
public NodeRef execute()
|
||||||
|
{
|
||||||
|
StoreRef storeRef = nodeService.createStore(
|
||||||
|
StoreRef.PROTOCOL_WORKSPACE,
|
||||||
|
"Test_" + System.nanoTime());
|
||||||
|
return nodeService.getRootNode(storeRef);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
rootNodeRef = transactionService.getRetryingTransactionHelper().doInTransaction(createStoreWork);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads the test model required for building the node graphs
|
||||||
|
*/
|
||||||
|
// public static DictionaryService loadModel(ApplicationContext applicationContext)
|
||||||
|
// {
|
||||||
|
// DictionaryDAO dictionaryDao = (DictionaryDAO) applicationContext.getBean("dictionaryDAO");
|
||||||
|
// // load the system model
|
||||||
|
// ClassLoader cl = BaseNodeServiceTest.class.getClassLoader();
|
||||||
|
// InputStream modelStream = cl.getResourceAsStream("alfresco/model/contentModel.xml");
|
||||||
|
// assertNotNull(modelStream);
|
||||||
|
// M2Model model = M2Model.createModel(modelStream);
|
||||||
|
// dictionaryDao.putModel(model);
|
||||||
|
// // load the test model
|
||||||
|
// modelStream = cl.getResourceAsStream("org/alfresco/repo/node/BaseNodeServiceTest_model.xml");
|
||||||
|
// assertNotNull(modelStream);
|
||||||
|
// model = M2Model.createModel(modelStream);
|
||||||
|
// dictionaryDao.putModel(model);
|
||||||
|
//
|
||||||
|
// DictionaryComponent dictionary = new DictionaryComponent();
|
||||||
|
// dictionary.setDictionaryDAO(dictionaryDao);
|
||||||
|
// // done
|
||||||
|
// return dictionary;
|
||||||
|
// }
|
||||||
|
|
||||||
|
public void testWithoutEncryption()
|
||||||
|
{
|
||||||
|
RetryingTransactionCallback<Void> encryptionWork = new RetryingTransactionCallback<Void>()
|
||||||
|
{
|
||||||
|
public Void execute()
|
||||||
|
{
|
||||||
|
NodeRef nodeRef1 = nodeService.createNode(
|
||||||
|
rootNodeRef,
|
||||||
|
ContentModel.ASSOC_CHILDREN,
|
||||||
|
QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, getName()),
|
||||||
|
ContentModel.TYPE_FOLDER, null).getChildRef();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Create a node using the thread's locale
|
||||||
|
NodeRef nodeRef2 = nodeService.createNode(
|
||||||
|
nodeRef1,
|
||||||
|
ContentModel.ASSOC_CONTAINS,
|
||||||
|
QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, getName()),
|
||||||
|
ENCRYPTED_TYPE_QNAME,
|
||||||
|
Collections.singletonMap(ENCRYPTED_PROP_QNAME, (Serializable)"hello world")).getChildRef();
|
||||||
|
fail("Should have generated an IllegalArgumentException");
|
||||||
|
}
|
||||||
|
//catch(IntegrityException e)
|
||||||
|
catch(IllegalArgumentException e)
|
||||||
|
{
|
||||||
|
// expected
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
transactionService.getRetryingTransactionHelper().doInTransaction(encryptionWork);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testWithEncryption()
|
||||||
|
{
|
||||||
|
RetryingTransactionCallback<Void> encryptionWork = new RetryingTransactionCallback<Void>()
|
||||||
|
{
|
||||||
|
public Void execute()
|
||||||
|
{
|
||||||
|
NodeRef nodeRef1 = nodeService.createNode(
|
||||||
|
rootNodeRef,
|
||||||
|
ContentModel.ASSOC_CHILDREN,
|
||||||
|
QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, getName()),
|
||||||
|
ContentModel.TYPE_FOLDER, null).getChildRef();
|
||||||
|
|
||||||
|
Map<QName, Serializable> allProperties = new PropertyMap();
|
||||||
|
allProperties.put(ENCRYPTED_PROP_QNAME, "ABC");
|
||||||
|
allProperties = metadataEncryptor.encrypt(allProperties);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
NodeRef nodeRef2 = nodeService.createNode(
|
||||||
|
nodeRef1,
|
||||||
|
ContentModel.ASSOC_CONTAINS,
|
||||||
|
QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, getName()),
|
||||||
|
ENCRYPTED_TYPE_QNAME, allProperties).getChildRef();
|
||||||
|
assertNotNull(nodeRef2);
|
||||||
|
|
||||||
|
Serializable encryptedPropertyValue = nodeService.getProperty(nodeRef2, ENCRYPTED_PROP_QNAME);
|
||||||
|
Serializable decryptedPropertyValue = metadataEncryptor.decrypt(ENCRYPTED_PROP_QNAME, encryptedPropertyValue);
|
||||||
|
assertEquals("ABC", decryptedPropertyValue);
|
||||||
|
}
|
||||||
|
//catch(IntegrityException e)
|
||||||
|
catch(Throwable e)
|
||||||
|
{
|
||||||
|
fail();
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
transactionService.getRetryingTransactionHelper().doInTransaction(encryptionWork);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clean up the test thread
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected void tearDown()
|
||||||
|
{
|
||||||
|
AuthenticationUtil.clearCurrentSecurityContext();
|
||||||
|
I18NUtil.setLocale(null);
|
||||||
|
}
|
||||||
|
}
|
47
source/java/org/alfresco/repo/node/encrypted_prop_model.xml
Normal file
47
source/java/org/alfresco/repo/node/encrypted_prop_model.xml
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
<model name="test:encryptedPropModel" xmlns="http://www.alfresco.org/model/dictionary/1.0">
|
||||||
|
|
||||||
|
<description>Alfresco Content Model</description>
|
||||||
|
<author>Alfresco</author>
|
||||||
|
<published>2005-05-30</published>
|
||||||
|
<version>1.0</version>
|
||||||
|
|
||||||
|
<imports>
|
||||||
|
<import uri="http://www.alfresco.org/model/dictionary/1.0" prefix="d"/>
|
||||||
|
<import uri="http://www.alfresco.org/model/content/1.0" prefix="cm"/>
|
||||||
|
</imports>
|
||||||
|
|
||||||
|
<namespaces>
|
||||||
|
<namespace uri="http://www.alfresco.org/test/encryptedPropModel/1.0" prefix="test"/>
|
||||||
|
</namespaces>
|
||||||
|
|
||||||
|
<constraints>
|
||||||
|
|
||||||
|
</constraints>
|
||||||
|
|
||||||
|
<types>
|
||||||
|
|
||||||
|
<type name="test:encrypted">
|
||||||
|
<title>Encrypted</title>
|
||||||
|
<description>The Base Type</description>
|
||||||
|
<parent>cm:content</parent>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<property name="test:prop1">
|
||||||
|
<type>d:encrypted</type>
|
||||||
|
<protected>true</protected>
|
||||||
|
<default></default>
|
||||||
|
<constraints>
|
||||||
|
</constraints>
|
||||||
|
</property>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<associations>
|
||||||
|
|
||||||
|
</associations>
|
||||||
|
|
||||||
|
<mandatory-aspects>
|
||||||
|
</mandatory-aspects>
|
||||||
|
</type>
|
||||||
|
|
||||||
|
</types>
|
||||||
|
</model>
|
@@ -9,7 +9,7 @@ import java.util.Set;
|
|||||||
|
|
||||||
import javax.crypto.SealedObject;
|
import javax.crypto.SealedObject;
|
||||||
|
|
||||||
import org.alfresco.encryption.Encryptor;
|
import org.alfresco.encryption.FallbackEncryptor;
|
||||||
import org.alfresco.encryption.KeyProvider;
|
import org.alfresco.encryption.KeyProvider;
|
||||||
import org.alfresco.error.AlfrescoRuntimeException;
|
import org.alfresco.error.AlfrescoRuntimeException;
|
||||||
import org.alfresco.repo.security.authentication.AuthenticationException;
|
import org.alfresco.repo.security.authentication.AuthenticationException;
|
||||||
@@ -35,7 +35,7 @@ import org.alfresco.service.namespace.QName;
|
|||||||
public class MetadataEncryptor
|
public class MetadataEncryptor
|
||||||
{
|
{
|
||||||
private DictionaryService dictionaryService;
|
private DictionaryService dictionaryService;
|
||||||
private Encryptor encryptor;
|
private FallbackEncryptor encryptor;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param dictionaryService service to check if properties need encrypting
|
* @param dictionaryService service to check if properties need encrypting
|
||||||
@@ -48,7 +48,7 @@ public class MetadataEncryptor
|
|||||||
/**
|
/**
|
||||||
* @param encryptor the class that does the encryption/decryption
|
* @param encryptor the class that does the encryption/decryption
|
||||||
*/
|
*/
|
||||||
public void setEncryptor(Encryptor encryptor)
|
public void setEncryptor(FallbackEncryptor encryptor)
|
||||||
{
|
{
|
||||||
this.encryptor = encryptor;
|
this.encryptor = encryptor;
|
||||||
}
|
}
|
||||||
@@ -221,4 +221,14 @@ public class MetadataEncryptor
|
|||||||
// Done
|
// Done
|
||||||
return outbound;
|
return outbound;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean keyAvailable(String keyAlias)
|
||||||
|
{
|
||||||
|
return encryptor.keyAvailable(keyAlias);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean backupKeyAvailable(String keyAlias)
|
||||||
|
{
|
||||||
|
return encryptor.backupKeyAvailable(keyAlias);
|
||||||
|
}
|
||||||
}
|
}
|
Reference in New Issue
Block a user