mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-08-07 17:49:17 +00:00
Added Encryptor interface for symmetric encryption esp. targeting SealedObject
- This will allow a keystore to be checked in (.keystore) and specified by installer - Algorithm parameters embedded in SealedObject but also supported by other Cipher methods ALF-8646: RINF 38: Text data encryption ALF-8956: RINF 38: Encryption key password specified by installer ALF-9055: RINF 38: Support encryption against existing data git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@28438 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
@@ -19,7 +19,6 @@
|
||||
package org.alfresco.repo.domain.node;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.sql.Savepoint;
|
||||
@@ -53,12 +52,12 @@ import org.alfresco.repo.domain.permissions.AclDAO;
|
||||
import org.alfresco.repo.domain.qname.QNameDAO;
|
||||
import org.alfresco.repo.domain.usage.UsageDAO;
|
||||
import org.alfresco.repo.policy.BehaviourFilter;
|
||||
import org.alfresco.repo.security.encryption.EncryptionEngine;
|
||||
import org.alfresco.repo.security.encryption.Encryptor;
|
||||
import org.alfresco.repo.security.permissions.AccessControlListProperties;
|
||||
import org.alfresco.repo.transaction.AlfrescoTransactionSupport;
|
||||
import org.alfresco.repo.transaction.AlfrescoTransactionSupport.TxnReadState;
|
||||
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
|
||||
import org.alfresco.repo.transaction.RetryingTransactionHelper;
|
||||
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
|
||||
import org.alfresco.repo.transaction.TransactionAwareSingleton;
|
||||
import org.alfresco.repo.transaction.TransactionListenerAdapter;
|
||||
import org.alfresco.repo.transaction.TransactionalResourceHelper;
|
||||
@@ -136,7 +135,7 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO
|
||||
private ContentDataDAO contentDataDAO;
|
||||
private LocaleDAO localeDAO;
|
||||
private UsageDAO usageDAO;
|
||||
private EncryptionEngine encryptionEngine;
|
||||
private Encryptor encryptor;
|
||||
|
||||
/**
|
||||
* Cache for the Store root nodes by StoreRef:<br/>
|
||||
@@ -218,9 +217,12 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO
|
||||
this.dictionaryService = dictionaryService;
|
||||
}
|
||||
|
||||
public void setEncryptionEngine(EncryptionEngine encryptionEngine)
|
||||
/**
|
||||
* @param encryptor helper to do symmetric property encryption
|
||||
*/
|
||||
public void setEncryptor(Encryptor encryptor)
|
||||
{
|
||||
this.encryptionEngine = encryptionEngine;
|
||||
this.encryptor = encryptor;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -368,9 +370,9 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO
|
||||
PropertyCheck.mandatory(this, "contentDataDAO", contentDataDAO);
|
||||
PropertyCheck.mandatory(this, "localeDAO", localeDAO);
|
||||
PropertyCheck.mandatory(this, "usageDAO", usageDAO);
|
||||
// PropertyCheck.mandatory(this, "encryptionEngine", encryptionEngine);
|
||||
PropertyCheck.mandatory(this, "encryptor", encryptor);
|
||||
|
||||
this.nodePropertyHelper = new NodePropertyHelper(dictionaryService, qnameDAO, localeDAO, contentDataDAO, encryptionEngine);
|
||||
this.nodePropertyHelper = new NodePropertyHelper(dictionaryService, qnameDAO, localeDAO, contentDataDAO, encryptor);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@@ -19,7 +19,6 @@
|
||||
package org.alfresco.repo.domain.node;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
@@ -33,7 +32,7 @@ import org.alfresco.error.AlfrescoRuntimeException;
|
||||
import org.alfresco.repo.domain.contentdata.ContentDataDAO;
|
||||
import org.alfresco.repo.domain.locale.LocaleDAO;
|
||||
import org.alfresco.repo.domain.qname.QNameDAO;
|
||||
import org.alfresco.repo.security.encryption.EncryptionEngine;
|
||||
import org.alfresco.repo.security.encryption.Encryptor;
|
||||
import org.alfresco.service.cmr.dictionary.DataTypeDefinition;
|
||||
import org.alfresco.service.cmr.dictionary.DictionaryException;
|
||||
import org.alfresco.service.cmr.dictionary.DictionaryService;
|
||||
@@ -60,7 +59,7 @@ public class NodePropertyHelper
|
||||
private static final Log logger = LogFactory.getLog(NodePropertyHelper.class);
|
||||
|
||||
private final DictionaryService dictionaryService;
|
||||
private final EncryptionEngine encryptionEngine;
|
||||
private final Encryptor encryptor;
|
||||
private final QNameDAO qnameDAO;
|
||||
private final LocaleDAO localeDAO;
|
||||
private final ContentDataDAO contentDataDAO;
|
||||
@@ -73,13 +72,13 @@ public class NodePropertyHelper
|
||||
QNameDAO qnameDAO,
|
||||
LocaleDAO localeDAO,
|
||||
ContentDataDAO contentDataDAO,
|
||||
EncryptionEngine encryptionEngine)
|
||||
Encryptor encryptor)
|
||||
{
|
||||
this.dictionaryService = dictionaryService;
|
||||
this.qnameDAO = qnameDAO;
|
||||
this.localeDAO = localeDAO;
|
||||
this.contentDataDAO = contentDataDAO;
|
||||
this.encryptionEngine = encryptionEngine;
|
||||
this.encryptor = encryptor;
|
||||
}
|
||||
|
||||
public Map<NodePropertyKey, NodePropertyValue> convertToPersistentProperties(Map<QName, Serializable> in)
|
||||
@@ -148,14 +147,17 @@ public class NodePropertyHelper
|
||||
|
||||
// Get or spoof the property datatype
|
||||
QName propertyTypeQName;
|
||||
boolean isEncrypted;
|
||||
if (propertyDef == null) // property not recognised
|
||||
{
|
||||
// allow it for now - persisting excess properties can be useful sometimes
|
||||
propertyTypeQName = DataTypeDefinition.ANY;
|
||||
isEncrypted = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
propertyTypeQName = propertyDef.getDataType().getName();
|
||||
isEncrypted = propertyDef.isEncrypted();
|
||||
}
|
||||
|
||||
// A property may appear to be multi-valued if the model definition is loose and
|
||||
@@ -266,19 +268,8 @@ public class NodePropertyHelper
|
||||
// Get the Locale ID for the text
|
||||
Long mlTextLocaleId = localeDAO.getOrCreateLocalePair(mlTextLocale).getFirst();
|
||||
// This is persisted against the current locale, but as a d:text instance
|
||||
Serializable v = null;
|
||||
try
|
||||
{
|
||||
v = propertyDef.isEncrypted() ? encrypt(mlTextStr) : mlTextStr;
|
||||
}
|
||||
catch (UnsupportedEncodingException e)
|
||||
{
|
||||
// TODO check that throwing the exception preserves the original logic
|
||||
throw new TypeConversionException(
|
||||
"The property value could not be decoded as a UTF-8 string " + value.getClass(),
|
||||
e);
|
||||
}
|
||||
NodePropertyValue npValue = new NodePropertyValue(DataTypeDefinition.TEXT, v, propertyDef.isEncrypted());
|
||||
// This is persisted against the current locale, but as a d:text instance
|
||||
NodePropertyValue npValue = new NodePropertyValue(DataTypeDefinition.TEXT, mlTextStr);
|
||||
NodePropertyKey npKey = new NodePropertyKey();
|
||||
npKey.setListIndex(collectionIndex);
|
||||
npKey.setQnameId(propertyQNameId);
|
||||
@@ -289,29 +280,6 @@ public class NodePropertyHelper
|
||||
}
|
||||
else
|
||||
{
|
||||
if(!propertyTypeQName.equals(DataTypeDefinition.ANY) && propertyDef.isEncrypted())
|
||||
{
|
||||
if(propertyTypeQName.equals(DataTypeDefinition.TEXT))
|
||||
{
|
||||
try
|
||||
{
|
||||
// TODO check type of value
|
||||
value = propertyDef.isEncrypted() ? encrypt((String)value) : value;
|
||||
}
|
||||
catch (UnsupportedEncodingException e)
|
||||
{
|
||||
// TODO check that throwing the exception preserves the original logic
|
||||
throw new TypeConversionException(
|
||||
"The property value could not be decoded as a UTF-8 string " + value.getClass(),
|
||||
e);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.warn("Encryption is not supported for type " + propertyTypeQName + ", encryption will not be performed");
|
||||
}
|
||||
}
|
||||
|
||||
NodePropertyValue npValue = makeNodePropertyValue(propertyDef, value);
|
||||
NodePropertyKey npKey = new NodePropertyKey();
|
||||
npKey.setListIndex(collectionIndex);
|
||||
@@ -323,18 +291,6 @@ public class NodePropertyHelper
|
||||
}
|
||||
}
|
||||
|
||||
protected byte[] encrypt(String input) throws UnsupportedEncodingException
|
||||
{
|
||||
byte[] bytes = encryptionEngine.encryptString(input);
|
||||
return bytes;
|
||||
}
|
||||
|
||||
protected String decrypt(byte[] input) throws UnsupportedEncodingException
|
||||
{
|
||||
String s = encryptionEngine.decryptAsString(input);
|
||||
return s;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to convert the <code>Serializable</code> value into a full, persistable {@link NodePropertyValue}.
|
||||
* <p>
|
||||
@@ -364,8 +320,7 @@ public class NodePropertyHelper
|
||||
try
|
||||
{
|
||||
NodePropertyValue propertyValue = null;
|
||||
boolean isEncrypted = propertyDef==null ? false : propertyDef.isEncrypted();
|
||||
propertyValue = new NodePropertyValue(propertyTypeQName, value, isEncrypted);
|
||||
propertyValue = new NodePropertyValue(propertyTypeQName, value);
|
||||
|
||||
// done
|
||||
return propertyValue;
|
||||
@@ -697,17 +652,17 @@ public class NodePropertyHelper
|
||||
}
|
||||
// get property attributes
|
||||
final QName propertyTypeQName;
|
||||
boolean encrypted;
|
||||
boolean isEncrypted;
|
||||
if (propertyDef == null)
|
||||
{
|
||||
// allow this for now
|
||||
propertyTypeQName = DataTypeDefinition.ANY;
|
||||
encrypted = false;
|
||||
isEncrypted = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
propertyTypeQName = propertyDef.getDataType().getName();
|
||||
encrypted = propertyDef.isEncrypted();
|
||||
isEncrypted = propertyDef.isEncrypted();
|
||||
}
|
||||
try
|
||||
{
|
||||
@@ -727,24 +682,6 @@ public class NodePropertyHelper
|
||||
ContentData contentData = contentDataDAO.getContentData(contentDataId).getSecond();
|
||||
value = new ContentDataWithId(contentData, contentDataId);
|
||||
}
|
||||
else if (encrypted)
|
||||
{
|
||||
if (propertyTypeQName.equals(DataTypeDefinition.TEXT) || propertyTypeQName.equals(DataTypeDefinition.MLTEXT))
|
||||
{
|
||||
try
|
||||
{
|
||||
value = decrypt((byte[])value);
|
||||
}
|
||||
catch (UnsupportedEncodingException e)
|
||||
{
|
||||
throw new AlfrescoRuntimeException("Unexpected exception during decryption", e);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new AlfrescoRuntimeException("Encryption is not supported for " + propertyDef.getDataType().getName() + " types");
|
||||
}
|
||||
}
|
||||
// done
|
||||
return value;
|
||||
}
|
||||
|
@@ -32,7 +32,7 @@ import org.alfresco.model.ContentModel;
|
||||
import org.alfresco.repo.domain.contentdata.ContentDataDAO;
|
||||
import org.alfresco.repo.domain.locale.LocaleDAO;
|
||||
import org.alfresco.repo.domain.qname.QNameDAO;
|
||||
import org.alfresco.repo.security.encryption.EncryptionEngine;
|
||||
import org.alfresco.repo.security.encryption.Encryptor;
|
||||
import org.alfresco.repo.transaction.RetryingTransactionHelper;
|
||||
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
|
||||
import org.alfresco.repo.version.VersionModel;
|
||||
@@ -87,9 +87,9 @@ public class NodePropertyHelperTest extends TestCase
|
||||
QNameDAO qnameDAO = (QNameDAO) ctx.getBean("qnameDAO");
|
||||
LocaleDAO localeDAO = (LocaleDAO) ctx.getBean("localeDAO");
|
||||
ContentDataDAO contentDataDAO = (ContentDataDAO) ctx.getBean("contentDataDAO");
|
||||
EncryptionEngine encryptionEngine = (EncryptionEngine) ctx.getBean("encryptionEngine");
|
||||
Encryptor encryptor = (Encryptor) ctx.getBean("encryptor");
|
||||
|
||||
helper = new NodePropertyHelper(dictionaryService, qnameDAO, localeDAO, contentDataDAO, encryptionEngine);
|
||||
helper = new NodePropertyHelper(dictionaryService, qnameDAO, localeDAO, contentDataDAO, encryptor);
|
||||
transactionService = serviceRegistry.getTransactionService();
|
||||
txnHelper = transactionService.getRetryingTransactionHelper();
|
||||
txnHelper.setMinRetryWaitMs(10);
|
||||
|
@@ -23,7 +23,6 @@ import java.io.ByteArrayOutputStream;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.ObjectOutputStream;
|
||||
import java.io.Serializable;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
@@ -31,11 +30,11 @@ import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.crypto.SealedObject;
|
||||
|
||||
import org.alfresco.error.AlfrescoRuntimeException;
|
||||
import org.alfresco.repo.domain.schema.SchemaBootstrap;
|
||||
import org.alfresco.repo.security.encryption.EncryptionEngine;
|
||||
import org.alfresco.service.cmr.dictionary.DataTypeDefinition;
|
||||
import org.alfresco.service.cmr.dictionary.PropertyDefinition;
|
||||
import org.alfresco.service.cmr.repository.AssociationRef;
|
||||
import org.alfresco.service.cmr.repository.ChildAssociationRef;
|
||||
import org.alfresco.service.cmr.repository.ContentData;
|
||||
@@ -527,6 +526,37 @@ public class NodePropertyValue implements Cloneable, Serializable
|
||||
}
|
||||
}
|
||||
},
|
||||
SEALED_OBJECT
|
||||
{
|
||||
@Override
|
||||
public Integer getOrdinalNumber()
|
||||
{
|
||||
return Integer.valueOf(22);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ValueType getPersistedType(Serializable value)
|
||||
{
|
||||
return ValueType.SERIALIZABLE;
|
||||
}
|
||||
|
||||
@Override
|
||||
Serializable convert(Serializable value)
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
else if (value instanceof SealedObject)
|
||||
{
|
||||
return value;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new IllegalArgumentException("SealedObject value not supported: " + value);
|
||||
}
|
||||
}
|
||||
},
|
||||
;
|
||||
|
||||
/**
|
||||
@@ -637,6 +667,10 @@ public class NodePropertyValue implements Cloneable, Serializable
|
||||
{
|
||||
return ValueType.CONTENT;
|
||||
}
|
||||
else if (value instanceof SealedObject)
|
||||
{
|
||||
return ValueType.SEALED_OBJECT;
|
||||
}
|
||||
else
|
||||
{
|
||||
// type is not recognised as belonging to any particular slot
|
||||
@@ -732,8 +766,6 @@ public class NodePropertyValue implements Cloneable, Serializable
|
||||
private Double doubleValue;
|
||||
private String stringValue;
|
||||
private Serializable serializableValue;
|
||||
|
||||
private boolean encrypted;
|
||||
|
||||
/**
|
||||
* default constructor
|
||||
@@ -748,16 +780,13 @@ public class NodePropertyValue implements Cloneable, Serializable
|
||||
* @param typeQName the dictionary-defined property type to store the property as
|
||||
* @param value the value to store. This will be converted into a format compatible
|
||||
* with the type given
|
||||
* @param isEncrypted true if value should be encrypted when persisted
|
||||
*
|
||||
* @throws java.lang.UnsupportedOperationException if the value cannot be converted to the type given
|
||||
*/
|
||||
public NodePropertyValue(QName typeQName, Serializable value, boolean encrypted)
|
||||
public NodePropertyValue(QName typeQName, Serializable value)
|
||||
{
|
||||
ParameterCheck.mandatory("typeQName", typeQName);
|
||||
|
||||
this.encrypted = encrypted;
|
||||
|
||||
if (value == null)
|
||||
{
|
||||
this.actualType = NodePropertyValue.getActualType(value);
|
||||
@@ -765,38 +794,17 @@ public class NodePropertyValue implements Cloneable, Serializable
|
||||
}
|
||||
else
|
||||
{
|
||||
ValueType persistedValueType = null;
|
||||
|
||||
if(encrypted)
|
||||
{
|
||||
// this constructor doesn't appear to get called for type DataTypeDefinition.MLTEXT because MLTEXT is
|
||||
// split out into strings in NodePropertyHelper
|
||||
if(typeQName.equals(DataTypeDefinition.TEXT))
|
||||
{
|
||||
this.actualType = ValueType.STRING;
|
||||
persistedValueType = ValueType.SERIALIZABLE;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new AlfrescoRuntimeException("Can encrypt only TEXT and MLTEXT types, this type is " + typeQName);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Convert the value to the type required. This ensures that any type conversion issues
|
||||
// are caught early and prevent the scenario where the data in the DB cannot be given
|
||||
// back out because it is unconvertable.
|
||||
ValueType valueType = makeValueType(typeQName);
|
||||
value = valueType.convert(value);
|
||||
|
||||
this.actualType = NodePropertyValue.getActualType(value);
|
||||
|
||||
// get the persisted type
|
||||
persistedValueType = this.actualType.getPersistedType(value);
|
||||
// convert to the persistent type
|
||||
value = persistedValueType.convert(value);
|
||||
}
|
||||
|
||||
// Convert the value to the type required. This ensures that any type conversion issues
|
||||
// are caught early and prevent the scenario where the data in the DB cannot be given
|
||||
// back out because it is unconvertable.
|
||||
ValueType valueType = makeValueType(typeQName);
|
||||
value = valueType.convert(value);
|
||||
|
||||
this.actualType = NodePropertyValue.getActualType(value);
|
||||
// get the persisted type
|
||||
ValueType persistedValueType = this.actualType.getPersistedType(value);
|
||||
// convert to the persistent type
|
||||
value = persistedValueType.convert(value);
|
||||
setPersistedValue(persistedValueType, value);
|
||||
}
|
||||
}
|
||||
@@ -1070,9 +1078,9 @@ public class NodePropertyValue implements Cloneable, Serializable
|
||||
// have been converted on the way in.
|
||||
ret = (Serializable) persistedValue;
|
||||
}
|
||||
else if(encrypted)
|
||||
else if(persistedValue instanceof SealedObject)
|
||||
{
|
||||
ret = persistedValue;
|
||||
ret = (Serializable) persistedValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
Reference in New Issue
Block a user