mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-08-07 17:49:17 +00:00
Initial checkin for ALF-8702: "RSOLR 026: Alfresco to SOLR communication and configuration"
Initial checkin for ALF-8791: "RSOLR 036: Update IndexChecker to support SOLR" git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@29179 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Binary file not shown.
@@ -9,9 +9,13 @@
|
|||||||
http://code.google.com/p/spring-crypto-utils/schema/crypt
|
http://code.google.com/p/spring-crypto-utils/schema/crypt
|
||||||
http://code.google.com/p/spring-crypto-utils/schema/crypt.xsd">
|
http://code.google.com/p/spring-crypto-utils/schema/crypt.xsd">
|
||||||
|
|
||||||
|
<bean id="springKeyStoreLoader" class="org.alfresco.encryption.SpringKeyStoreLoader">
|
||||||
|
</bean>
|
||||||
|
|
||||||
<!-- Beans to initilize encryption -->
|
<!-- Beans to initilize encryption -->
|
||||||
<bean id="keyProvider" class="org.alfresco.repo.security.encryption.KeystoreKeyProvider" init-method="init">
|
<bean id="keyProvider" class="org.alfresco.encryption.KeystoreKeyProvider" init-method="init">
|
||||||
<property name="location" value="${encryption.keystore.location}"/>
|
<property name="location" value="${encryption.keystore.location}"/>
|
||||||
|
<property name="keyStoreLoader" ref="springKeyStoreLoader"/>
|
||||||
<property name="provider" value="${encryption.keystore.provider}"/>
|
<property name="provider" value="${encryption.keystore.provider}"/>
|
||||||
<property name="type" value="${encryption.keystore.type}"/>
|
<property name="type" value="${encryption.keystore.type}"/>
|
||||||
<property name="passwords">
|
<property name="passwords">
|
||||||
@@ -23,9 +27,21 @@
|
|||||||
</property>
|
</property>
|
||||||
</bean>
|
</bean>
|
||||||
|
|
||||||
<bean id="encryptor" class="org.alfresco.repo.security.encryption.DefaultEncryptor" init-method="init">
|
<bean id="encryptor" class="org.alfresco.encryption.DefaultEncryptor" init-method="init">
|
||||||
<property name="keyProvider" ref="keyProvider"/>
|
<property name="keyProvider" ref="keyProvider"/>
|
||||||
<property name="cipherAlgorithm" value="${encryption.encryption.cipherAlgorithm}" />
|
<property name="cipherAlgorithm" value="${encryption.encryption.cipherAlgorithm}" />
|
||||||
</bean>
|
</bean>
|
||||||
|
|
||||||
|
<bean id="macUtils" class="org.alfresco.encryption.MACUtils">
|
||||||
|
<property name="keyProvider" ref="keyProvider"/>
|
||||||
|
<property name="macAlgorithm" value="${encryption.macAlgorithm}"/>
|
||||||
|
</bean>
|
||||||
|
|
||||||
|
<bean id="encryptionUtils" class="org.alfresco.encryption.DefaultEncryptionUtils">
|
||||||
|
<property name="encryptor" ref="encryptor"/>
|
||||||
|
<property name="macUtils" ref="macUtils"/>
|
||||||
|
<property name="messageTimeout" value="${encryption.messageTimeout}"/>
|
||||||
|
<property name="remoteIP" value="${solr.solrHost}" />
|
||||||
|
</bean>
|
||||||
|
|
||||||
</beans>
|
</beans>
|
||||||
|
@@ -658,6 +658,7 @@ deployment.filesystem.default.metadatadir=${deployment.filesystem.metadatadir}/d
|
|||||||
#
|
#
|
||||||
encryption.encryption.cipherAlgorithm=DESede/CBC/PKCS5Padding
|
encryption.encryption.cipherAlgorithm=DESede/CBC/PKCS5Padding
|
||||||
encryption.keystore.location=classpath:alfresco/.keystore
|
encryption.keystore.location=classpath:alfresco/.keystore
|
||||||
|
encryption.keystore.passwordsFile.location=
|
||||||
encryption.keystore.provider=
|
encryption.keystore.provider=
|
||||||
encryption.keystore.type=JCEKS
|
encryption.keystore.type=JCEKS
|
||||||
# The password protecting the keystore entries
|
# The password protecting the keystore entries
|
||||||
@@ -666,6 +667,15 @@ encryption.keystore.password=mp6yc0UD9e
|
|||||||
encryption.keystore.password.metadata=oKIWzVdEdA
|
encryption.keystore.password.metadata=oKIWzVdEdA
|
||||||
# The password protecting the alias: solr
|
# The password protecting the alias: solr
|
||||||
encryption.keystore.password.solr=TxHTtOnrwQ
|
encryption.keystore.password.solr=TxHTtOnrwQ
|
||||||
|
encryption.messageTimeout=30000
|
||||||
|
encryption.macAlgorithm=HmacSHA1
|
||||||
|
|
||||||
|
# SOLR connection details
|
||||||
|
solr.solrHost=localhost
|
||||||
|
solr.solrUrl=http://${solr.solrHost}:8080/solr
|
||||||
|
solr.solrUser=solr
|
||||||
|
solr.solrPassword=solr
|
||||||
|
solr.secureComms.enabled=true
|
||||||
|
|
||||||
#
|
#
|
||||||
# Web Publishing Properties
|
# Web Publishing Properties
|
||||||
|
@@ -8,10 +8,10 @@ import java.util.Set;
|
|||||||
|
|
||||||
import javax.crypto.SealedObject;
|
import javax.crypto.SealedObject;
|
||||||
|
|
||||||
|
import org.alfresco.encryption.Encryptor;
|
||||||
|
import org.alfresco.encryption.KeyProvider;
|
||||||
import org.alfresco.repo.security.authentication.AuthenticationException;
|
import org.alfresco.repo.security.authentication.AuthenticationException;
|
||||||
import org.alfresco.repo.security.authentication.AuthenticationUtil;
|
import org.alfresco.repo.security.authentication.AuthenticationUtil;
|
||||||
import org.alfresco.repo.security.encryption.Encryptor;
|
|
||||||
import org.alfresco.repo.security.encryption.KeyProvider;
|
|
||||||
import org.alfresco.service.cmr.dictionary.DataTypeDefinition;
|
import org.alfresco.service.cmr.dictionary.DataTypeDefinition;
|
||||||
import org.alfresco.service.cmr.dictionary.DictionaryService;
|
import org.alfresco.service.cmr.dictionary.DictionaryService;
|
||||||
import org.alfresco.service.cmr.dictionary.PropertyDefinition;
|
import org.alfresco.service.cmr.dictionary.PropertyDefinition;
|
||||||
|
@@ -1,232 +0,0 @@
|
|||||||
package org.alfresco.repo.security.encryption;
|
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
|
||||||
import java.io.ByteArrayOutputStream;
|
|
||||||
import java.io.ObjectInputStream;
|
|
||||||
import java.io.ObjectOutputStream;
|
|
||||||
import java.io.Serializable;
|
|
||||||
import java.security.AlgorithmParameters;
|
|
||||||
import java.security.Key;
|
|
||||||
|
|
||||||
import javax.crypto.Cipher;
|
|
||||||
import javax.crypto.SealedObject;
|
|
||||||
|
|
||||||
import org.alfresco.error.AlfrescoRuntimeException;
|
|
||||||
import org.alfresco.util.Pair;
|
|
||||||
import org.alfresco.util.PropertyCheck;
|
|
||||||
import org.apache.commons.logging.Log;
|
|
||||||
import org.apache.commons.logging.LogFactory;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Basic support for encryption engines.
|
|
||||||
*
|
|
||||||
* @since 4.0
|
|
||||||
*/
|
|
||||||
public abstract class AbstractEncryptor implements Encryptor
|
|
||||||
{
|
|
||||||
private static final Log logger = LogFactory.getLog(AbstractEncryptor.class);
|
|
||||||
|
|
||||||
private KeyProvider keyProvider;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructs with defaults
|
|
||||||
*/
|
|
||||||
protected AbstractEncryptor()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param keyProvider provides encryption keys based on aliases
|
|
||||||
*/
|
|
||||||
public void setKeyProvider(KeyProvider keyProvider)
|
|
||||||
{
|
|
||||||
this.keyProvider = keyProvider;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void init()
|
|
||||||
{
|
|
||||||
PropertyCheck.mandatory(this, "keyProvider", keyProvider);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Cipher getCipher(String keyAlias, AlgorithmParameters params, int mode)
|
|
||||||
{
|
|
||||||
// Get the encryption key
|
|
||||||
Key key = keyProvider.getKey(keyAlias);
|
|
||||||
if (key == null)
|
|
||||||
{
|
|
||||||
// No encryption possible
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Cipher cipher = getCipher(key, params, mode);
|
|
||||||
// Done
|
|
||||||
if (logger.isDebugEnabled())
|
|
||||||
{
|
|
||||||
logger.debug("Cipher constructed: alias=" + keyAlias + "; mode=" + mode + ": " + cipher);
|
|
||||||
}
|
|
||||||
return cipher;
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
throw new AlfrescoRuntimeException(
|
|
||||||
"Failed to construct cipher: alias=" + keyAlias + "; mode=" + mode,
|
|
||||||
e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Factory method to be written by implementations to construct <b>and initialize</b>
|
|
||||||
* physical ciphering objects.
|
|
||||||
*
|
|
||||||
* @param keyAlias the key alias
|
|
||||||
* @param params algorithm-specific parameters
|
|
||||||
* @param mode the cipher mode
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
protected abstract Cipher getCipher(Key key, AlgorithmParameters params, int mode) throws Exception;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public Pair<byte[], AlgorithmParameters> encrypt(String keyAlias, AlgorithmParameters params, byte[] input)
|
|
||||||
{
|
|
||||||
Cipher cipher = getCipher(keyAlias, params, Cipher.ENCRYPT_MODE);
|
|
||||||
if (cipher == null)
|
|
||||||
{
|
|
||||||
return new Pair<byte[], AlgorithmParameters>(input, null);
|
|
||||||
}
|
|
||||||
try
|
|
||||||
{
|
|
||||||
byte[] output = cipher.doFinal(input);
|
|
||||||
params = cipher.getParameters();
|
|
||||||
return new Pair<byte[], AlgorithmParameters>(output, params);
|
|
||||||
}
|
|
||||||
catch (Throwable e)
|
|
||||||
{
|
|
||||||
throw new AlfrescoRuntimeException("Decryption failed for key alias: " + keyAlias, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public byte[] decrypt(String keyAlias, AlgorithmParameters params, byte[] input)
|
|
||||||
{
|
|
||||||
Cipher cipher = getCipher(keyAlias, params, Cipher.DECRYPT_MODE);
|
|
||||||
if (cipher == null)
|
|
||||||
{
|
|
||||||
return input;
|
|
||||||
}
|
|
||||||
try
|
|
||||||
{
|
|
||||||
return cipher.doFinal(input);
|
|
||||||
}
|
|
||||||
catch (Throwable e)
|
|
||||||
{
|
|
||||||
throw new AlfrescoRuntimeException("Decryption failed for key alias: " + keyAlias, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
* <p/>
|
|
||||||
* Serializes and {@link #encrypt(byte[]) encrypts} the input data.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public Pair<byte[], AlgorithmParameters> encryptObject(String keyAlias, AlgorithmParameters params, Object input)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
ByteArrayOutputStream bos = new ByteArrayOutputStream(1024);
|
|
||||||
ObjectOutputStream oos = new ObjectOutputStream(bos);
|
|
||||||
oos.writeObject(input);
|
|
||||||
byte[] unencrypted = bos.toByteArray();
|
|
||||||
return encrypt(keyAlias, params, unencrypted);
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
throw new AlfrescoRuntimeException("Failed to serialize or encrypt object", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
* <p/>
|
|
||||||
* {@link #decrypt(byte[]) Decrypts} and deserializes the input data
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public Object decryptObject(String keyAlias, AlgorithmParameters params, byte[] input)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
byte[] unencrypted = decrypt(keyAlias, params, input);
|
|
||||||
ByteArrayInputStream bis = new ByteArrayInputStream(unencrypted);
|
|
||||||
ObjectInputStream ois = new ObjectInputStream(bis);
|
|
||||||
Object obj = ois.readObject();
|
|
||||||
return obj;
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
throw new AlfrescoRuntimeException("Failed to deserialize or decrypt object", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Serializable sealObject(String keyAlias, AlgorithmParameters params, Serializable input)
|
|
||||||
{
|
|
||||||
if (input == null)
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
Cipher cipher = getCipher(keyAlias, params, Cipher.ENCRYPT_MODE);
|
|
||||||
if (cipher == null)
|
|
||||||
{
|
|
||||||
return input;
|
|
||||||
}
|
|
||||||
try
|
|
||||||
{
|
|
||||||
return new SealedObject(input, cipher);
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
throw new AlfrescoRuntimeException("Failed to seal object", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Serializable unsealObject(String keyAlias, Serializable input)
|
|
||||||
{
|
|
||||||
if (input == null)
|
|
||||||
{
|
|
||||||
return input;
|
|
||||||
}
|
|
||||||
// Don't unseal it if it is not sealed
|
|
||||||
if (!(input instanceof SealedObject))
|
|
||||||
{
|
|
||||||
return input;
|
|
||||||
}
|
|
||||||
// Get the Key, rather than a Cipher
|
|
||||||
Key key = keyProvider.getKey(keyAlias);
|
|
||||||
if (key == null)
|
|
||||||
{
|
|
||||||
// The client will be expecting to unseal the object
|
|
||||||
throw new IllegalStateException("No key matching " + keyAlias + ". Cannot unseal object.");
|
|
||||||
}
|
|
||||||
// Unseal it using the key
|
|
||||||
SealedObject sealedInput = (SealedObject) input;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Serializable output = (Serializable) sealedInput.getObject(key);
|
|
||||||
// Done
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
throw new AlfrescoRuntimeException("Failed to unseal object", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,17 +0,0 @@
|
|||||||
package org.alfresco.repo.security.encryption;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Basic support for key providers
|
|
||||||
* <p/>
|
|
||||||
* TODO: This class will provide the alias name mapping so that use-cases can be mapped
|
|
||||||
* to different alias names in the keystore.
|
|
||||||
*
|
|
||||||
* @author Derek Hulley
|
|
||||||
* @since 4.0
|
|
||||||
*/
|
|
||||||
public abstract class AbstractKeyProvider implements KeyProvider
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* Not a useless class.
|
|
||||||
*/
|
|
||||||
}
|
|
@@ -1,75 +0,0 @@
|
|||||||
package org.alfresco.repo.security.encryption;
|
|
||||||
|
|
||||||
import java.security.AlgorithmParameters;
|
|
||||||
import java.security.Key;
|
|
||||||
|
|
||||||
import javax.crypto.Cipher;
|
|
||||||
|
|
||||||
import org.alfresco.util.PropertyCheck;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Derek Hulley
|
|
||||||
* @since 4.0
|
|
||||||
*/
|
|
||||||
public class DefaultEncryptor extends AbstractEncryptor
|
|
||||||
{
|
|
||||||
private String cipherAlgorithm;
|
|
||||||
private String cipherProvider;
|
|
||||||
|
|
||||||
private final ThreadLocal<Cipher> threadCipher;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Default constructor for IOC
|
|
||||||
*/
|
|
||||||
public DefaultEncryptor()
|
|
||||||
{
|
|
||||||
threadCipher = new ThreadLocal<Cipher>();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Convenience constructor for tests
|
|
||||||
*/
|
|
||||||
/* package */ DefaultEncryptor(KeyProvider keyProvider, String cipherAlgorithm, String cipherProvider)
|
|
||||||
{
|
|
||||||
this();
|
|
||||||
setKeyProvider(keyProvider);
|
|
||||||
setCipherAlgorithm(cipherAlgorithm);
|
|
||||||
setCipherProvider(cipherProvider);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setCipherAlgorithm(String cipherAlgorithm)
|
|
||||||
{
|
|
||||||
this.cipherAlgorithm = cipherAlgorithm;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setCipherProvider(String cipherProvider)
|
|
||||||
{
|
|
||||||
this.cipherProvider = cipherProvider;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void init()
|
|
||||||
{
|
|
||||||
super.init();
|
|
||||||
PropertyCheck.mandatory(this, "cipherAlgorithm", cipherAlgorithm);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Cipher getCipher(Key key, AlgorithmParameters params, int mode) throws Exception
|
|
||||||
{
|
|
||||||
Cipher cipher = threadCipher.get();
|
|
||||||
if (cipher == null)
|
|
||||||
{
|
|
||||||
if (cipherProvider == null)
|
|
||||||
{
|
|
||||||
cipher = Cipher.getInstance(cipherAlgorithm);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
cipher = Cipher.getInstance(cipherAlgorithm, cipherProvider);
|
|
||||||
}
|
|
||||||
threadCipher.set(cipher);
|
|
||||||
}
|
|
||||||
cipher.init(mode, key, params);
|
|
||||||
return cipher;
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,93 +0,0 @@
|
|||||||
package org.alfresco.repo.security.encryption;
|
|
||||||
|
|
||||||
import java.io.Serializable;
|
|
||||||
import java.security.AlgorithmParameters;
|
|
||||||
|
|
||||||
import javax.crypto.Cipher;
|
|
||||||
|
|
||||||
import org.alfresco.util.Pair;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Interface providing methods to encrypt and decrypt data.
|
|
||||||
*
|
|
||||||
* @since 4.0
|
|
||||||
*/
|
|
||||||
public interface Encryptor
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Get the basic cipher that must be used for the given use-case
|
|
||||||
*
|
|
||||||
* @param keyAlias the encryption key alias
|
|
||||||
* @param params the parameters for the encryption or decryption
|
|
||||||
* @param mode the encryption mode
|
|
||||||
* @return the cipher to use or <tt>null</tt> if there is no
|
|
||||||
* key associated with the key alias
|
|
||||||
*/
|
|
||||||
Cipher getCipher(String keyAlias, AlgorithmParameters params, int mode);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Encrypt some bytes
|
|
||||||
*
|
|
||||||
* @param keyAlias the encryption key alias
|
|
||||||
* @param input the data to encrypt
|
|
||||||
* @return the encrypted data and parameters used
|
|
||||||
*/
|
|
||||||
Pair<byte[], AlgorithmParameters> encrypt(String keyAlias, AlgorithmParameters params, byte[] input);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Decrypt some bytes
|
|
||||||
*
|
|
||||||
* @param keyAlias the encryption key alias
|
|
||||||
* @param input the data to decrypt
|
|
||||||
* @return the unencrypted data
|
|
||||||
*/
|
|
||||||
byte[] decrypt(String keyAlias, AlgorithmParameters params, byte[] input);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Encrypt an object
|
|
||||||
*
|
|
||||||
* @param keyAlias the encryption key alias
|
|
||||||
* @param input the object to write to bytes
|
|
||||||
* @return the encrypted data and parameters used
|
|
||||||
*/
|
|
||||||
Pair<byte[], AlgorithmParameters> encryptObject(String keyAlias, AlgorithmParameters params, Object input);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Decrypt data as an object
|
|
||||||
*
|
|
||||||
* @param keyAlias the encryption key alias
|
|
||||||
* @param input the data to decrypt
|
|
||||||
* @return the unencrypted data deserialized
|
|
||||||
*/
|
|
||||||
Object decryptObject(String keyAlias, AlgorithmParameters params, byte[] input);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Convenience method to seal on object up cryptographically.
|
|
||||||
* <p/>
|
|
||||||
* Note that the original object may be returned directly if there is no key associated with
|
|
||||||
* the alias.
|
|
||||||
*
|
|
||||||
* @param keyAlias the encryption key alias
|
|
||||||
* @param input the object to encrypt and seal
|
|
||||||
* @return the sealed object that can be decrypted with the original key
|
|
||||||
*/
|
|
||||||
Serializable sealObject(String keyAlias, AlgorithmParameters params, Serializable input);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Convenience method to unseal on object sealed up cryptographically.
|
|
||||||
* <p/>
|
|
||||||
* Note that the algorithm parameters not provided on the assumption that a symmetric key
|
|
||||||
* algorithm is in use - only the key is required for unsealing.
|
|
||||||
* <p/>
|
|
||||||
* Note that the original object may be returned directly if there is no key associated with
|
|
||||||
* the alias or if the input object is not a <code>SealedObject</code>.
|
|
||||||
*
|
|
||||||
* @param keyAlias the encryption key alias
|
|
||||||
* @param input the object to decrypt and unseal
|
|
||||||
* @return the original unsealed object that was encrypted with the original key
|
|
||||||
* @throws IllegalStateException if the key alias is not valid <b>and</b> the input is a
|
|
||||||
* <tt>SealedObject</tt>
|
|
||||||
*/
|
|
||||||
Serializable unsealObject(String keyAlias, Serializable input);
|
|
||||||
}
|
|
@@ -1,71 +0,0 @@
|
|||||||
package org.alfresco.repo.security.encryption;
|
|
||||||
|
|
||||||
import java.io.Serializable;
|
|
||||||
import java.security.AlgorithmParameters;
|
|
||||||
|
|
||||||
import junit.framework.TestCase;
|
|
||||||
|
|
||||||
import org.alfresco.util.Pair;
|
|
||||||
import org.bouncycastle.util.Arrays;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @since 4.0
|
|
||||||
*/
|
|
||||||
public class EncryptorTest extends TestCase
|
|
||||||
{
|
|
||||||
private DefaultEncryptor encryptor;
|
|
||||||
|
|
||||||
public void setUp() throws Exception
|
|
||||||
{
|
|
||||||
encryptor = new DefaultEncryptor(
|
|
||||||
KeyStoreKeyProviderTest.getTestKeyStoreProvider(),
|
|
||||||
"DESede/CBC/PKCS5Padding",
|
|
||||||
null);
|
|
||||||
encryptor.init(); // Not currently necessary
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testBasicBytes_NoKey()
|
|
||||||
{
|
|
||||||
byte[] bytes = new byte[] {11, 12, 13};
|
|
||||||
|
|
||||||
Pair<byte[], AlgorithmParameters> encryptedPair = encryptor.encrypt("fluff", null, bytes);
|
|
||||||
byte[] decrypted = encryptor.decrypt(
|
|
||||||
"fluff",
|
|
||||||
encryptedPair.getSecond(),
|
|
||||||
encryptedPair.getFirst());
|
|
||||||
assertTrue("Encryption round trip failed. ", Arrays.areEqual(bytes, decrypted));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testBasicBytes_WithKey()
|
|
||||||
{
|
|
||||||
byte[] bytes = new byte[] {11, 12, 13};
|
|
||||||
|
|
||||||
Pair<byte[], AlgorithmParameters> encryptedPair = encryptor.encrypt("mykey1", null, bytes);
|
|
||||||
byte[] decrypted = encryptor.decrypt(
|
|
||||||
"mykey1",
|
|
||||||
encryptedPair.getSecond(),
|
|
||||||
encryptedPair.getFirst());
|
|
||||||
assertTrue("Encryption round trip failed. ", Arrays.areEqual(bytes, decrypted));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testBasicObject()
|
|
||||||
{
|
|
||||||
Object testObject = " This is a string, but will be serialized ";
|
|
||||||
|
|
||||||
Pair<byte[], AlgorithmParameters> encryptedPair = encryptor.encryptObject("mykey2", null, testObject);
|
|
||||||
Object output = encryptor.decryptObject(
|
|
||||||
"mykey2",
|
|
||||||
encryptedPair.getSecond(),
|
|
||||||
encryptedPair.getFirst());
|
|
||||||
assertEquals("Encryption round trip failed. ", testObject, output);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testSealedObject()
|
|
||||||
{
|
|
||||||
Serializable testObject = " This is a string, but will be serialized ";
|
|
||||||
|
|
||||||
Serializable sealedObject = encryptor.sealObject("mykey2", null, testObject);
|
|
||||||
Object output = encryptor.unsealObject("mykey2", sealedObject);
|
|
||||||
assertEquals("Encryption round trip failed. ", testObject, output);
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,29 +0,0 @@
|
|||||||
package org.alfresco.repo.security.encryption;
|
|
||||||
|
|
||||||
import java.security.Key;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A key provider returns the secret keys for different use cases.
|
|
||||||
*
|
|
||||||
* @since 4.0
|
|
||||||
*/
|
|
||||||
public interface KeyProvider
|
|
||||||
{
|
|
||||||
// TODO: Allow the aliases to be configured i.e. include an alias mapper
|
|
||||||
/**
|
|
||||||
* Constant representing the keystore alias for keys to encrypt/decrype node metadata
|
|
||||||
*/
|
|
||||||
public static final String ALIAS_METADATA = "metadata";
|
|
||||||
/**
|
|
||||||
* Constant representing the keystore alias for keys to encrypt/decrype SOLR transfer data
|
|
||||||
*/
|
|
||||||
public static final String ALIAS_SOLR = "solr";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get an encryption key if available.
|
|
||||||
*
|
|
||||||
* @param keyAlias the key alias
|
|
||||||
* @return the encryption key or <tt>null</tt> if there is no associated key
|
|
||||||
*/
|
|
||||||
public Key getKey(String keyAlias);
|
|
||||||
}
|
|
@@ -1,164 +0,0 @@
|
|||||||
package org.alfresco.repo.security.encryption;
|
|
||||||
|
|
||||||
import java.security.Key;
|
|
||||||
import java.security.KeyStore;
|
|
||||||
import java.security.UnrecoverableKeyException;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import org.alfresco.error.AlfrescoRuntimeException;
|
|
||||||
import org.alfresco.util.ApplicationContextHelper;
|
|
||||||
import org.springframework.context.ApplicationContext;
|
|
||||||
|
|
||||||
import junit.framework.TestCase;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tests {@link KeystoreKeyProvider}
|
|
||||||
*
|
|
||||||
* @author Derek Hulley
|
|
||||||
* @since 4.0
|
|
||||||
*/
|
|
||||||
public class KeyStoreKeyProviderTest extends TestCase
|
|
||||||
{
|
|
||||||
private static final String FILE_ONE = "classpath:alfresco/keystore-tests/ks-test-1.jks";
|
|
||||||
private static final String FILE_TWO = "classpath:alfresco/keystore-tests/ks-test-2.jks";
|
|
||||||
private static final String FILE_THREE = "classpath:alfresco/keystore-tests/ks-test-3.jks";
|
|
||||||
private static final String ALIAS_ONE = "mykey1";
|
|
||||||
private static final String ALIAS_TWO = "mykey2";
|
|
||||||
private static final String ALIAS_THREE = "mykey3";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Helper utility to create a two-alias keystore.
|
|
||||||
* <p/>
|
|
||||||
* TODO: Allow the required aliases and key types to be specified and generate
|
|
||||||
* a keystore on the fly
|
|
||||||
*/
|
|
||||||
/* package */ static KeystoreKeyProvider getTestKeyStoreProvider()
|
|
||||||
{
|
|
||||||
Map<String, String> passwords = new HashMap<String, String>(5);
|
|
||||||
passwords.put(KeystoreKeyProvider.KEY_KEYSTORE_PASSWORD, "ksPwd2");
|
|
||||||
passwords.put(ALIAS_ONE, "aliasPwd1");
|
|
||||||
passwords.put(ALIAS_TWO, "aliasPwd2");
|
|
||||||
KeystoreKeyProvider ks = new KeystoreKeyProvider(
|
|
||||||
FILE_TWO,
|
|
||||||
"SunJCE",
|
|
||||||
"JCEKS",
|
|
||||||
passwords);
|
|
||||||
return ks;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setUp() throws Exception
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testNoKeyStorePasswords() throws Exception
|
|
||||||
{
|
|
||||||
KeystoreKeyProvider keyProvider = new KeystoreKeyProvider(
|
|
||||||
FILE_ONE,
|
|
||||||
"SunJCE",
|
|
||||||
"JCEKS",
|
|
||||||
Collections.<String,String>emptyMap());
|
|
||||||
// This has succeeded because we have not attempted to access it
|
|
||||||
assertNull("Should be no keys available", keyProvider.getKey(ALIAS_ONE));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testKeyStoreWithOnlyAliasPasswords() throws Exception
|
|
||||||
{
|
|
||||||
KeystoreKeyProvider keyProvider = new KeystoreKeyProvider(
|
|
||||||
FILE_TWO,
|
|
||||||
"SunJCE",
|
|
||||||
"JCEKS",
|
|
||||||
Collections.singletonMap(ALIAS_ONE, "aliasPwd1"));
|
|
||||||
// This has succeeded because we have not attempted to access it
|
|
||||||
assertNotNull("Should be able to key alias with same password", keyProvider.getKey(ALIAS_ONE));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testAliasWithIncorrectPassword_One() throws Exception
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
new KeystoreKeyProvider(
|
|
||||||
FILE_ONE,
|
|
||||||
"SunJCE",
|
|
||||||
"JCEKS",
|
|
||||||
Collections.singletonMap(ALIAS_ONE, "password_fail"));
|
|
||||||
fail("Expect to fail because password is incorrect");
|
|
||||||
}
|
|
||||||
catch (AlfrescoRuntimeException e)
|
|
||||||
{
|
|
||||||
// Expected
|
|
||||||
assertTrue(e.getCause() instanceof UnrecoverableKeyException);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testAliasWithIncorrectPassword_Two() throws Exception
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
new KeystoreKeyProvider(
|
|
||||||
FILE_TWO,
|
|
||||||
"SunJCE",
|
|
||||||
"JCEKS",
|
|
||||||
Collections.singletonMap(ALIAS_TWO, "password_fail"));
|
|
||||||
fail("Expect to fail because password is incorrect");
|
|
||||||
}
|
|
||||||
catch (AlfrescoRuntimeException e)
|
|
||||||
{
|
|
||||||
// Expected
|
|
||||||
assertTrue(e.getCause() instanceof UnrecoverableKeyException);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testAliasWithCorrectPassword_One() throws Exception
|
|
||||||
{
|
|
||||||
KeystoreKeyProvider ks = new KeystoreKeyProvider(
|
|
||||||
FILE_ONE,
|
|
||||||
"SunJCE",
|
|
||||||
"JCEKS",
|
|
||||||
Collections.singletonMap(ALIAS_ONE, "aliasPwd1"));
|
|
||||||
Key keyOne = ks.getKey(ALIAS_ONE);
|
|
||||||
assertNotNull(keyOne);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testAliasWithCorrectPassword_Two() throws Exception
|
|
||||||
{
|
|
||||||
Map<String, String> passwords = new HashMap<String, String>(5);
|
|
||||||
passwords.put(ALIAS_ONE, "aliasPwd1");
|
|
||||||
passwords.put(ALIAS_TWO, "aliasPwd2");
|
|
||||||
KeystoreKeyProvider ks = new KeystoreKeyProvider(
|
|
||||||
FILE_TWO,
|
|
||||||
"SunJCE",
|
|
||||||
"JCEKS",
|
|
||||||
passwords);
|
|
||||||
assertNotNull(ks.getKey(ALIAS_ONE));
|
|
||||||
assertNotNull(ks.getKey(ALIAS_TWO));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testAliasWithCorrectPassword_Three() throws Exception
|
|
||||||
{
|
|
||||||
Map<String, String> passwords = new HashMap<String, String>(5);
|
|
||||||
passwords.put(ALIAS_ONE, "aliasPwd1");
|
|
||||||
passwords.put(ALIAS_TWO, "aliasPwd2");
|
|
||||||
passwords.put(ALIAS_THREE, "aliasPwd3");
|
|
||||||
KeystoreKeyProvider ks = new KeystoreKeyProvider(
|
|
||||||
FILE_THREE,
|
|
||||||
"SunJCE",
|
|
||||||
"JCEKS",
|
|
||||||
passwords);
|
|
||||||
assertNotNull(ks.getKey(ALIAS_ONE));
|
|
||||||
assertNotNull(ks.getKey(ALIAS_TWO));
|
|
||||||
assertNull(ks.getKey(ALIAS_THREE));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* TODO: Do we need spring-crypto when it is V1.0?
|
|
||||||
*/
|
|
||||||
public void DISABLED_testSpringCrypto() throws Throwable
|
|
||||||
{
|
|
||||||
ApplicationContext ctx = ApplicationContextHelper.getApplicationContext(
|
|
||||||
new String[] {"alfresco/keystore-tests/encryption-test-context.xml"});
|
|
||||||
@SuppressWarnings("unused")
|
|
||||||
KeyStore ks1 = (KeyStore) ctx.getBean("ks-test-1");
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,246 +0,0 @@
|
|||||||
package org.alfresco.repo.security.encryption;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileInputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.security.Key;
|
|
||||||
import java.security.KeyStore;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
|
||||||
import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
|
|
||||||
import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;
|
|
||||||
|
|
||||||
import org.alfresco.error.AlfrescoRuntimeException;
|
|
||||||
import org.alfresco.util.PropertyCheck;
|
|
||||||
import org.apache.commons.logging.Log;
|
|
||||||
import org.apache.commons.logging.LogFactory;
|
|
||||||
import org.springframework.util.ResourceUtils;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* Provides the system-wide secret key for symmetric database encryption from a key store
|
|
||||||
* in the filesystem.
|
|
||||||
*
|
|
||||||
* @author Derek Hulley
|
|
||||||
* @since 4.0
|
|
||||||
*/
|
|
||||||
public class KeystoreKeyProvider extends AbstractKeyProvider
|
|
||||||
{
|
|
||||||
public static final String KEY_KEYSTORE_PASSWORD = "keystore";
|
|
||||||
|
|
||||||
private static final Log logger = LogFactory.getLog(KeyProvider.class);
|
|
||||||
|
|
||||||
// Will be cleared after initialization
|
|
||||||
private Map<String, String> passwords;
|
|
||||||
private String location;
|
|
||||||
private String provider;
|
|
||||||
private String type;
|
|
||||||
private Map<String, Key> keys;
|
|
||||||
|
|
||||||
private final ReadLock readLock;
|
|
||||||
private final WriteLock writeLock;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructs the provider with required defaults
|
|
||||||
*/
|
|
||||||
public KeystoreKeyProvider()
|
|
||||||
{
|
|
||||||
ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
|
|
||||||
readLock = lock.readLock();
|
|
||||||
writeLock = lock.writeLock();
|
|
||||||
keys = new HashMap<String, Key>(7);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Convenience constructor for tests. Note that {@link #init()} is also called.
|
|
||||||
*/
|
|
||||||
/* package */ KeystoreKeyProvider(String location, String provider, String type, Map<String, String> passwords)
|
|
||||||
{
|
|
||||||
this();
|
|
||||||
setLocation(location);
|
|
||||||
setProvider(provider);
|
|
||||||
setType(type);
|
|
||||||
setPasswords(passwords);
|
|
||||||
init();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setLocation(String location)
|
|
||||||
{
|
|
||||||
this.location = location;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setProvider(String provider)
|
|
||||||
{
|
|
||||||
this.provider = provider;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setType(String type)
|
|
||||||
{
|
|
||||||
this.type = type;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the map of passwords to access the keystore.
|
|
||||||
* <p/>
|
|
||||||
* Where required, <tt>null</tt> values must be inserted into the map to indicate the presence
|
|
||||||
* of a key that is not protected by a password. They entry for {@link #KEY_KEYSTORE_PASSWORD}
|
|
||||||
* is required if the keystore is password protected.
|
|
||||||
*
|
|
||||||
* @param passwords a map of passwords including <tt>null</tt> values
|
|
||||||
*/
|
|
||||||
public void setPasswords(Map<String, String> passwords)
|
|
||||||
{
|
|
||||||
this.passwords = new HashMap<String, String>(passwords);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void init()
|
|
||||||
{
|
|
||||||
writeLock.lock();
|
|
||||||
try
|
|
||||||
{
|
|
||||||
safeInit();
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
writeLock.unlock();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initializes class; must be done in a write lock.
|
|
||||||
*/
|
|
||||||
private void safeInit()
|
|
||||||
{
|
|
||||||
if (!PropertyCheck.isValidPropertyString(location))
|
|
||||||
{
|
|
||||||
location = null;
|
|
||||||
}
|
|
||||||
if (!PropertyCheck.isValidPropertyString(provider))
|
|
||||||
{
|
|
||||||
provider = null;
|
|
||||||
}
|
|
||||||
if (!PropertyCheck.isValidPropertyString(type))
|
|
||||||
{
|
|
||||||
type = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
PropertyCheck.mandatory(this, "location", location);
|
|
||||||
// Extract the keystore password
|
|
||||||
String pwdKeyStore = passwords.get(KEY_KEYSTORE_PASSWORD);
|
|
||||||
|
|
||||||
// Make sure we choose the default type, if required
|
|
||||||
if (type == null)
|
|
||||||
{
|
|
||||||
type = KeyStore.getDefaultType();
|
|
||||||
}
|
|
||||||
|
|
||||||
KeyStore ks = null;
|
|
||||||
InputStream is = null;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (provider == null)
|
|
||||||
{
|
|
||||||
ks = KeyStore.getInstance(type);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ks = KeyStore.getInstance(type, provider);
|
|
||||||
}
|
|
||||||
// Load it up
|
|
||||||
File ksFile = ResourceUtils.getFile(location);
|
|
||||||
if (!ksFile.exists())
|
|
||||||
{
|
|
||||||
throw new IOException("Unable to find keystore file: " + ksFile);
|
|
||||||
}
|
|
||||||
is = new FileInputStream(ksFile);
|
|
||||||
ks.load(is, pwdKeyStore == null ? null : pwdKeyStore.toCharArray());
|
|
||||||
// Loaded
|
|
||||||
if (logger.isDebugEnabled())
|
|
||||||
{
|
|
||||||
logger.debug(
|
|
||||||
"Initialize keystore provider: \n" +
|
|
||||||
" Location: " + location + "\n" +
|
|
||||||
" Provider: " + provider + "\n" +
|
|
||||||
" Type: " + type);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Throwable e)
|
|
||||||
{
|
|
||||||
throw new AlfrescoRuntimeException(
|
|
||||||
"Failed to initialize keystore provider: \n" +
|
|
||||||
" Location: " + location + "\n" +
|
|
||||||
" Provider: " + provider + "\n" +
|
|
||||||
" Type: " + type,
|
|
||||||
e);
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
pwdKeyStore = null;
|
|
||||||
passwords.remove(KEY_KEYSTORE_PASSWORD);
|
|
||||||
if (is != null)
|
|
||||||
{
|
|
||||||
try { is.close(); } catch (Throwable e) {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now get the other keys
|
|
||||||
for (Map.Entry<String, String> element : passwords.entrySet())
|
|
||||||
{
|
|
||||||
String keyAlias = element.getKey();
|
|
||||||
String passwordStr = element.getValue();
|
|
||||||
if (!PropertyCheck.isValidPropertyString(passwordStr))
|
|
||||||
{
|
|
||||||
// Force a failure because the property was not properly initialized
|
|
||||||
PropertyCheck.mandatory(this, "passwords." + keyAlias, null);
|
|
||||||
}
|
|
||||||
// Null is an acceptable value (means no key)
|
|
||||||
Key key = null;
|
|
||||||
// Attempt to key the key
|
|
||||||
try
|
|
||||||
{
|
|
||||||
key = ks.getKey(keyAlias, passwordStr == null ? null : passwordStr.toCharArray());
|
|
||||||
keys.put(keyAlias, key);
|
|
||||||
// Key loaded
|
|
||||||
if (logger.isDebugEnabled())
|
|
||||||
{
|
|
||||||
logger.debug(
|
|
||||||
"Retrieved key from keystore: \n" +
|
|
||||||
" Location: " + location + "\n" +
|
|
||||||
" Provider: " + provider + "\n" +
|
|
||||||
" Type: " + type + "\n" +
|
|
||||||
" Alias: " + keyAlias + "\n" +
|
|
||||||
" Password?: " + (passwordStr != null));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Throwable e)
|
|
||||||
{
|
|
||||||
throw new AlfrescoRuntimeException(
|
|
||||||
"Failed to retrieve key from keystore: \n" +
|
|
||||||
" Location: " + location + "\n" +
|
|
||||||
" Provider: " + provider + "\n" +
|
|
||||||
" Type: " + type + "\n" +
|
|
||||||
" Alias: " + keyAlias + "\n" +
|
|
||||||
" Password?: " + (passwordStr != null),
|
|
||||||
e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Clear passwords
|
|
||||||
passwords.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Key getKey(String keyAlias)
|
|
||||||
{
|
|
||||||
readLock.lock();
|
|
||||||
try
|
|
||||||
{
|
|
||||||
return keys.get(keyAlias);
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
readLock.unlock();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
Reference in New Issue
Block a user