From 1c2b677a47c059d01465510ce7287252d11b1a2c Mon Sep 17 00:00:00 2001 From: Derek Hulley Date: Thu, 16 Jun 2011 15:42:56 +0000 Subject: [PATCH] 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 --- config/alfresco/.keystore | Bin 0 -> 643 bytes config/alfresco/application-context-core.xml | 1 + .../authentication-services-context.xml | 2 +- config/alfresco/core-services-context.xml | 38 +- config/alfresco/dao/dao-context.xml | 2 +- config/alfresco/encryption-context.xml | 31 ++ .../model-specific-services-context.xml | 2 +- config/alfresco/repository.properties | 17 +- .../repo/domain/node/AbstractNodeDAOImpl.java | 18 +- .../repo/domain/node/NodePropertyHelper.java | 89 +---- .../domain/node/NodePropertyHelperTest.java | 6 +- .../repo/domain/node/NodePropertyValue.java | 94 ++--- .../GetChildrenCannedQueryFactory.java | 12 +- .../BasicPasswordGenerator.java | 8 + .../encryption/AESEncryptionEngine.java | 109 ------ .../encryption/AbstractEncryptor.java | 189 ++++++++++ .../encryption/AbstractKeyProvider.java | 104 +----- .../encryption/DefaultEncryptionEngine.java | 129 ------- .../DefaultEncryptionEngineTest.java | 32 -- .../security/encryption/DefaultEncryptor.java | 75 ++++ .../security/encryption/EncryptionEngine.java | 12 - .../repo/security/encryption/Encryptor.java | 86 +++++ .../security/encryption/EncryptorTest.java | 61 ++++ .../repo/security/encryption/KeyProvider.java | 34 +- .../encryption/KeyStoreKeyProviderTest.java | 161 +++++++++ .../encryption/KeystoreKeyProvider.java | 337 +++++++++++------- .../security/encryption/TestKeyProvider.java | 26 -- .../encryption-test-context.xml | 15 + .../alfresco/keystore-tests/ks-test-1.jks | Bin 0 -> 643 bytes .../alfresco/keystore-tests/ks-test-2.jks | Bin 0 -> 1254 bytes .../alfresco/keystore-tests/ks-test-3.jks | Bin 0 -> 1254 bytes .../alfresco/keystore-tests/ks-test-4.jks | Bin 0 -> 643 bytes .../alfresco/keystore-tests/passwords.txt | 37 ++ 33 files changed, 1022 insertions(+), 705 deletions(-) create mode 100644 config/alfresco/.keystore create mode 100644 config/alfresco/encryption-context.xml delete mode 100644 source/java/org/alfresco/repo/security/encryption/AESEncryptionEngine.java create mode 100644 source/java/org/alfresco/repo/security/encryption/AbstractEncryptor.java delete mode 100644 source/java/org/alfresco/repo/security/encryption/DefaultEncryptionEngine.java delete mode 100644 source/java/org/alfresco/repo/security/encryption/DefaultEncryptionEngineTest.java create mode 100644 source/java/org/alfresco/repo/security/encryption/DefaultEncryptor.java delete mode 100644 source/java/org/alfresco/repo/security/encryption/EncryptionEngine.java create mode 100644 source/java/org/alfresco/repo/security/encryption/Encryptor.java create mode 100644 source/java/org/alfresco/repo/security/encryption/EncryptorTest.java create mode 100644 source/java/org/alfresco/repo/security/encryption/KeyStoreKeyProviderTest.java delete mode 100644 source/java/org/alfresco/repo/security/encryption/TestKeyProvider.java create mode 100644 source/test-resources/alfresco/keystore-tests/encryption-test-context.xml create mode 100644 source/test-resources/alfresco/keystore-tests/ks-test-1.jks create mode 100644 source/test-resources/alfresco/keystore-tests/ks-test-2.jks create mode 100644 source/test-resources/alfresco/keystore-tests/ks-test-3.jks create mode 100644 source/test-resources/alfresco/keystore-tests/ks-test-4.jks create mode 100644 source/test-resources/alfresco/keystore-tests/passwords.txt diff --git a/config/alfresco/.keystore b/config/alfresco/.keystore new file mode 100644 index 0000000000000000000000000000000000000000..01bd383454a4286748fc53e92235e76972d3c396 GIT binary patch literal 643 zcmX?i?%X*B1_mY|W&~np2DaSF?9@s_AlG2(W0Tl5Zy8vNix`ZP^K@CmS zv7NExlFv)8KO5RsS!5+IxO}#I(S<*4wJ+~1{+>49H|MPPrT?$j_gkDlV}ANWd6N75 z{Y$E&8COmFvfb-Ym~7~g_p1e$SwCFeSSd@aoV z@v5WuPpf}cYb}}3yo6^DSrWV4HW3{jJU4 zhs^xaXDm!}T~c7cUH&#w(+1+IQjuf8v$!tBgfDG736xaByN`*wi2IL7uuf{&c3?zF1+DJn1b$FG{gx-na6PYHuqfRk%@W=V#xi>YH?N=Q*= hK~AcRYcNiUA6;U+mJPQ$t7lJ`tF6`dQCD#OB>*9&1j7IT literal 0 HcmV?d00001 diff --git a/config/alfresco/application-context-core.xml b/config/alfresco/application-context-core.xml index ec66e257a1..624aeaa6a2 100644 --- a/config/alfresco/application-context-core.xml +++ b/config/alfresco/application-context-core.xml @@ -8,6 +8,7 @@ + diff --git a/config/alfresco/authentication-services-context.xml b/config/alfresco/authentication-services-context.xml index 5a52a2f575..d479ed536d 100644 --- a/config/alfresco/authentication-services-context.xml +++ b/config/alfresco/authentication-services-context.xml @@ -375,7 +375,7 @@ - + diff --git a/config/alfresco/core-services-context.xml b/config/alfresco/core-services-context.xml index 5d0a7f4aa1..5d516945fa 100644 --- a/config/alfresco/core-services-context.xml +++ b/config/alfresco/core-services-context.xml @@ -1549,35 +1549,19 @@ - - - - - - - - org.alfresco.repo.admin.SysAdminParams - - - + + + + + + + + org.alfresco.repo.admin.SysAdminParams + + + - - - - - - - - - - - - - - diff --git a/config/alfresco/dao/dao-context.xml b/config/alfresco/dao/dao-context.xml index f323277c29..b5f10c3fe0 100644 --- a/config/alfresco/dao/dao-context.xml +++ b/config/alfresco/dao/dao-context.xml @@ -90,6 +90,7 @@ + @@ -103,7 +104,6 @@ - diff --git a/config/alfresco/encryption-context.xml b/config/alfresco/encryption-context.xml new file mode 100644 index 0000000000..1aeecdd71d --- /dev/null +++ b/config/alfresco/encryption-context.xml @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/config/alfresco/model-specific-services-context.xml b/config/alfresco/model-specific-services-context.xml index 74865d6fa6..fa96d86121 100644 --- a/config/alfresco/model-specific-services-context.xml +++ b/config/alfresco/model-specific-services-context.xml @@ -148,7 +148,7 @@ - + diff --git a/config/alfresco/repository.properties b/config/alfresco/repository.properties index 934888da73..44b0bac3b6 100644 --- a/config/alfresco/repository.properties +++ b/config/alfresco/repository.properties @@ -632,7 +632,16 @@ deployment.filesystem.default.rootdir=./www deployment.filesystem.default.name=filesystem deployment.filesystem.default.metadatadir=${deployment.filesystem.metadatadir}/default -# Encryption engine properties. These passwords protect the key store in which the secret -# key used for encryption and decryption is stored. -encryption.keystore.keystorePassword=keystorepassword -encryption.keystore.secretKeyPassword=secretkeypassword \ No newline at end of file +# +# Encryption properties +# +encryption.encryption.cipherAlgorithm=DESede/CBC/PKCS5Padding +encryption.keystore.location=classpath:alfresco/.keystore +encryption.keystore.provider= +encryption.keystore.type=JCEKS +# The password protecting the keystore entries +encryption.keystore.password=ksPwd1 +# The password protecting the alias: METADATA +encryption.keystore.password.METADATA=${encryption.keystore.password} +# The password protecting the alias: SOLR +encryption.keystore.password.SOLR=${encryption.keystore.password} \ No newline at end of file diff --git a/source/java/org/alfresco/repo/domain/node/AbstractNodeDAOImpl.java b/source/java/org/alfresco/repo/domain/node/AbstractNodeDAOImpl.java index 93d7079a27..9f1a7d013e 100644 --- a/source/java/org/alfresco/repo/domain/node/AbstractNodeDAOImpl.java +++ b/source/java/org/alfresco/repo/domain/node/AbstractNodeDAOImpl.java @@ -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:
@@ -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); } /* diff --git a/source/java/org/alfresco/repo/domain/node/NodePropertyHelper.java b/source/java/org/alfresco/repo/domain/node/NodePropertyHelper.java index 00249e7730..9fa4a7adac 100644 --- a/source/java/org/alfresco/repo/domain/node/NodePropertyHelper.java +++ b/source/java/org/alfresco/repo/domain/node/NodePropertyHelper.java @@ -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 convertToPersistentProperties(Map 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 Serializable value into a full, persistable {@link NodePropertyValue}. *

@@ -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; } diff --git a/source/java/org/alfresco/repo/domain/node/NodePropertyHelperTest.java b/source/java/org/alfresco/repo/domain/node/NodePropertyHelperTest.java index cf5c5a41c6..6f58c9eb9d 100644 --- a/source/java/org/alfresco/repo/domain/node/NodePropertyHelperTest.java +++ b/source/java/org/alfresco/repo/domain/node/NodePropertyHelperTest.java @@ -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); diff --git a/source/java/org/alfresco/repo/domain/node/NodePropertyValue.java b/source/java/org/alfresco/repo/domain/node/NodePropertyValue.java index 3d8341ebc9..5d8fbd50ae 100644 --- a/source/java/org/alfresco/repo/domain/node/NodePropertyValue.java +++ b/source/java/org/alfresco/repo/domain/node/NodePropertyValue.java @@ -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 { diff --git a/source/java/org/alfresco/repo/node/getchildren/GetChildrenCannedQueryFactory.java b/source/java/org/alfresco/repo/node/getchildren/GetChildrenCannedQueryFactory.java index e677bcdd55..c4cb434574 100644 --- a/source/java/org/alfresco/repo/node/getchildren/GetChildrenCannedQueryFactory.java +++ b/source/java/org/alfresco/repo/node/getchildren/GetChildrenCannedQueryFactory.java @@ -29,8 +29,8 @@ import org.alfresco.query.CannedQuery; import org.alfresco.query.CannedQueryPageDetails; import org.alfresco.query.CannedQueryParameters; import org.alfresco.query.CannedQuerySortDetails; -import org.alfresco.query.PagingRequest; import org.alfresco.query.CannedQuerySortDetails.SortOrder; +import org.alfresco.query.PagingRequest; import org.alfresco.repo.domain.contentdata.ContentDataDAO; import org.alfresco.repo.domain.locale.LocaleDAO; import org.alfresco.repo.domain.node.NodeDAO; @@ -38,7 +38,7 @@ import org.alfresco.repo.domain.node.NodePropertyHelper; import org.alfresco.repo.domain.qname.QNameDAO; import org.alfresco.repo.domain.query.CannedQueryDAO; import org.alfresco.repo.security.authentication.AuthenticationUtil; -import org.alfresco.repo.security.encryption.EncryptionEngine; +import org.alfresco.repo.security.encryption.Encryptor; import org.alfresco.repo.security.permissions.impl.acegi.MethodSecurityInterceptor; import org.alfresco.repo.tenant.TenantService; import org.alfresco.service.cmr.dictionary.DictionaryService; @@ -64,7 +64,7 @@ public class GetChildrenCannedQueryFactory extends AbstractCannedQueryFactory getCannedQuery(CannedQueryParameters parameters) { - NodePropertyHelper nodePropertyHelper = new NodePropertyHelper(dictionaryService, qnameDAO, localeDAO, contentDataDAO, encryptionEngine); + NodePropertyHelper nodePropertyHelper = new NodePropertyHelper(dictionaryService, qnameDAO, localeDAO, contentDataDAO, encryptor); Method method = null; for (Method m : methodService.getClass().getMethods()) diff --git a/source/java/org/alfresco/repo/security/authentication/BasicPasswordGenerator.java b/source/java/org/alfresco/repo/security/authentication/BasicPasswordGenerator.java index 44a147e7bc..fe406350f3 100644 --- a/source/java/org/alfresco/repo/security/authentication/BasicPasswordGenerator.java +++ b/source/java/org/alfresco/repo/security/authentication/BasicPasswordGenerator.java @@ -49,4 +49,12 @@ public class BasicPasswordGenerator implements PasswordGenerator { return RandomStringUtils.randomAlphanumeric(passwordLength); } + + public static void main(String ... args) + { + BasicPasswordGenerator pwdGen = new BasicPasswordGenerator(); + pwdGen.setPasswordLength(10); + + System.out.println("A password: " + pwdGen.generatePassword()); + } } diff --git a/source/java/org/alfresco/repo/security/encryption/AESEncryptionEngine.java b/source/java/org/alfresco/repo/security/encryption/AESEncryptionEngine.java deleted file mode 100644 index 2b6640af04..0000000000 --- a/source/java/org/alfresco/repo/security/encryption/AESEncryptionEngine.java +++ /dev/null @@ -1,109 +0,0 @@ -package org.alfresco.repo.security.encryption; - -import java.io.UnsupportedEncodingException; -import java.security.Key; -import java.security.Security; - -import javax.crypto.Cipher; - -import org.alfresco.error.AlfrescoRuntimeException; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.bouncycastle.jce.provider.BouncyCastleProvider; - -public class AESEncryptionEngine implements EncryptionEngine -{ - private static String ALGORITHM = "AES/ECB/PKCS5Padding"; - private static final Log logger = LogFactory.getLog(AESEncryptionEngine.class); - - private KeyProvider keyProvider; - //private Key key; - - private ThreadLocal cipher; - - public AESEncryptionEngine() - { - } - - public void setKeyProvider(KeyProvider keyProvider) - { - this.keyProvider = keyProvider; - } - - public void init() - { - this.cipher = new ThreadLocal(); -// key = keyProvider.getKey(); -// if(key == null) -// { -// throw new AlfrescoRuntimeException("Secret key is null."); -// } - } - - protected byte[] process(int cipherMode, byte[] input) - { - Cipher cipher = this.cipher.get(); - - if(cipher == null) - { - try - { - cipher = Cipher.getInstance(ALGORITHM); - } - catch(Exception e) - { - Security.addProvider(new BouncyCastleProvider()); - try - { - cipher = Cipher.getInstance(ALGORITHM); - } - catch(Exception e1) - { - throw new AlfrescoRuntimeException("Unable to initialise encryption engine", e1); - } - } - - if(cipher == null) - { - throw new AlfrescoRuntimeException("Unable to initialise encryption engine"); - } - - this.cipher.set(cipher); - - logger.debug("Initialised thread local cipher"); - } - - try - { - cipher.init(cipherMode, keyProvider.getKey()); - - // do the encryption/decryption in one go - return cipher.doFinal(input); - } - catch(Exception e) - { - throw new AlfrescoRuntimeException("Unexpected exception during encryption/decryption", e); - } - } - - public byte[] encrypt(byte[] input) - { - return process(Cipher.ENCRYPT_MODE, input); - } - - public byte[] decrypt(byte[] input) - { - return process(Cipher.DECRYPT_MODE, input); - } - - public byte[] encryptString(String input) throws UnsupportedEncodingException - { - byte[] in = input.getBytes("UTF-8"); - return encrypt(in); - } - - public String decryptAsString(byte[] input) throws UnsupportedEncodingException - { - return new String(decrypt(input), "UTF-8").trim(); - } -} diff --git a/source/java/org/alfresco/repo/security/encryption/AbstractEncryptor.java b/source/java/org/alfresco/repo/security/encryption/AbstractEncryptor.java new file mode 100644 index 0000000000..172bf9d56c --- /dev/null +++ b/source/java/org/alfresco/repo/security/encryption/AbstractEncryptor.java @@ -0,0 +1,189 @@ +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 and initialize + * 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 encrypt(String keyAlias, AlgorithmParameters params, byte[] input) + { + Cipher cipher = getCipher(keyAlias, params, Cipher.ENCRYPT_MODE); + if (cipher == null) + { + return new Pair(input, null); + } + try + { + byte[] output = cipher.doFinal(input); + params = cipher.getParameters(); + return new Pair(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} + *

+ * Serializes and {@link #encrypt(byte[]) encrypts} the input data. + */ + @Override + public Pair 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} + *

+ * {@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 SealedObject sealObject(String keyAlias, AlgorithmParameters params, Serializable input) + { + throw new UnsupportedOperationException(); + } + + @Override + public Serializable unsealObject(String keyAlias, SealedObject input) + { + throw new UnsupportedOperationException(); + } +} diff --git a/source/java/org/alfresco/repo/security/encryption/AbstractKeyProvider.java b/source/java/org/alfresco/repo/security/encryption/AbstractKeyProvider.java index 28a48a5840..4ab5f7e640 100644 --- a/source/java/org/alfresco/repo/security/encryption/AbstractKeyProvider.java +++ b/source/java/org/alfresco/repo/security/encryption/AbstractKeyProvider.java @@ -1,101 +1,21 @@ package org.alfresco.repo.security.encryption; -import java.security.InvalidParameterException; import java.security.Key; -import java.security.NoSuchAlgorithmException; -import java.security.Security; -import javax.crypto.KeyGenerator; +import org.alfresco.util.ParameterCheck; -import org.alfresco.error.AlfrescoRuntimeException; -import org.alfresco.repo.security.authentication.PasswordGenerator; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.bouncycastle.jce.provider.BouncyCastleProvider; - -public abstract class AbstractKeyProvider /*extends AbstractLifecycleBean*/ implements KeyProvider +/** + * Basic support for key providers + * + * @author Derek Hulley + * @since 4.0 + */ +public abstract class AbstractKeyProvider implements KeyProvider { - private static final Log logger = LogFactory.getLog(KeyProvider.class); - - private static int KEY_SIZE = 256; // this requires unlimited strength policy files - private static int DEFAULT_KEY_SIZE = 128; // default key size should work if KEY_SIZE doesn't - private static String KEY_ALGORITHM = "AES"; - - protected PasswordGenerator passwordGenerator; - - private Key key; - - public void setKey(Key key) + @Override + public Key getKey(AlfrescoKeyAlias keyAlias) { - this.key = key; - } - - - public PasswordGenerator getPasswordGenerator() - { - return passwordGenerator; - } - - public void setPasswordGenerator(PasswordGenerator passwordGenerator) - { - this.passwordGenerator = passwordGenerator; - } - - public Key getKey() - { - return key; - } - - protected KeyGenerator getKeyGenerator() - { - KeyGenerator keyGenerator = null; - - try - { - keyGenerator = KeyGenerator.getInstance(KEY_ALGORITHM); - } - catch(NoSuchAlgorithmException e) - { - Security.addProvider(new BouncyCastleProvider()); - try - { - keyGenerator = KeyGenerator.getInstance(KEY_ALGORITHM); - } - catch(NoSuchAlgorithmException e1) - { - throw new AlfrescoRuntimeException("Unable to initialise encryption engine, no key generator is available", e1); - } - } - - if(keyGenerator == null) - { - throw new AlfrescoRuntimeException("Unable to initialise encryption engine, no key generator is available"); - } - - try - { - keyGenerator.init(KEY_SIZE); - } - catch(InvalidParameterException e) - { - logger.warn(KEY_SIZE + " bits key size is not supported, trying " + DEFAULT_KEY_SIZE + " bits"); - try - { - // try a smaller key size - keyGenerator.init(DEFAULT_KEY_SIZE); - } - catch(InvalidParameterException e1) - { - throw new AlfrescoRuntimeException("Unable to initialise encryption engine, no key generator is available", e1); - } - } - - return keyGenerator; - } - - protected Key generateSecretKey() - { - KeyGenerator keyGenerator = getKeyGenerator(); - return keyGenerator.generateKey(); + ParameterCheck.mandatory("keyAlias", keyAlias); + return getKey(keyAlias.name()); } } diff --git a/source/java/org/alfresco/repo/security/encryption/DefaultEncryptionEngine.java b/source/java/org/alfresco/repo/security/encryption/DefaultEncryptionEngine.java deleted file mode 100644 index b725e90aae..0000000000 --- a/source/java/org/alfresco/repo/security/encryption/DefaultEncryptionEngine.java +++ /dev/null @@ -1,129 +0,0 @@ -package org.alfresco.repo.security.encryption; - -import java.io.UnsupportedEncodingException; -import java.security.Security; - -import javax.crypto.Cipher; - -import org.alfresco.error.AlfrescoRuntimeException; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.bouncycastle.crypto.BufferedBlockCipher; -import org.bouncycastle.crypto.CipherParameters; -import org.bouncycastle.crypto.InvalidCipherTextException; -import org.bouncycastle.crypto.engines.AESEngine; -import org.bouncycastle.crypto.paddings.BlockCipherPadding; -import org.bouncycastle.crypto.paddings.PaddedBufferedBlockCipher; -import org.bouncycastle.crypto.paddings.ZeroBytePadding; -import org.bouncycastle.crypto.params.KeyParameter; - -public class DefaultEncryptionEngine implements EncryptionEngine -{ - private static final Log logger = LogFactory.getLog(EncryptionEngine.class); - - //private String encryptionProvider; - private KeyProvider keyProvider; - - private BufferedBlockCipher cipher; - private AESEngine engine; - - //private Cipher cipher; - //private byte[] key; - - public DefaultEncryptionEngine(/*byte[] key*/) - { - // TODO check that this hasn't already been done - Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider()); -// -// this.key = key; - } - - public void setKeyProvider(KeyProvider keyProvider) - { - this.keyProvider = keyProvider; - } - -// public void setEncryptionProvider(String encryptionProvider) -// { -// this.encryptionProvider = encryptionProvider; -// } - - public void init() - { - //cipher = Cipher.getInstance("AES"); - this.engine = new AESEngine(); - - /* - * Paddings available (http://www.bouncycastle.org/docs/docs1.6/org/bouncycastle/crypto/paddings/BlockCipherPadding.html): - * - ISO10126d2Padding - * - ISO7816d4Padding - * - PKCS7Padding - * - TBCPadding - * - X923Padding - * - ZeroBytePadding - */ - BlockCipherPadding blockCipherPadding = new ZeroBytePadding(); - this.cipher = new PaddedBufferedBlockCipher(engine, blockCipherPadding); - -// logger.debug("Encryption cipher: " + cipher.getProvider().getInfo()); - } - - protected byte[] process(boolean toEncrypt, byte[] input) - { - try - { - //CipherParameters param = new KeyParameter(keyProvider.getKey()); - //cipher.init(toEncrypt, param); - - int inputLength = input.length; - int maximumOutputLength = cipher.getOutputSize(inputLength); - byte[] output = new byte[maximumOutputLength]; - - int outputOffset = 0; - int outputLength = 0; - int bytesProcessed = cipher.processBytes(input, 0, input.length, output, 0); - outputOffset += bytesProcessed; - outputLength += bytesProcessed; - bytesProcessed = cipher.doFinal(output, outputOffset); - outputOffset += bytesProcessed; - outputLength += bytesProcessed; - - if(outputLength == output.length) - { - return output; - } - else - { - byte[] truncatedOutput = new byte[outputLength]; - System.arraycopy(output, 0, truncatedOutput, 0, outputLength); - return truncatedOutput; - } - } - catch(InvalidCipherTextException ex) - { - throw new AlfrescoRuntimeException("Unexpected encryption error", ex); - } - } - - public byte[] encrypt(byte[] input) - { - return process(true, input); - } - - public byte[] decrypt(byte[] input) - { - return process(false, input); - } - - public byte[] encryptString(String input) throws UnsupportedEncodingException - { - byte[] in = input.getBytes("UTF-8"); - return encrypt(in); - } - - public String decryptAsString(byte[] input) throws UnsupportedEncodingException - { - return new String(decrypt(input), "UTF-8").trim(); - } - -} diff --git a/source/java/org/alfresco/repo/security/encryption/DefaultEncryptionEngineTest.java b/source/java/org/alfresco/repo/security/encryption/DefaultEncryptionEngineTest.java deleted file mode 100644 index a8f97bcae0..0000000000 --- a/source/java/org/alfresco/repo/security/encryption/DefaultEncryptionEngineTest.java +++ /dev/null @@ -1,32 +0,0 @@ -package org.alfresco.repo.security.encryption; - -import java.io.UnsupportedEncodingException; - -import junit.framework.TestCase; - -public class DefaultEncryptionEngineTest extends TestCase -{ - private DefaultEncryptionEngine encryptionEngine; - - public void setUp() throws Exception - { - encryptionEngine = new DefaultEncryptionEngine(); - encryptionEngine.setKeyProvider(new TestKeyProvider()); - } - - public void testBasic() - { - try - { - String testString = "Hello World"; - - byte[] bytes = encryptionEngine.encryptString(testString); - String output = encryptionEngine.decryptAsString(bytes); - assertEquals("", testString, output); - } - catch(UnsupportedEncodingException ex) - { - fail("Unexpected exception: " + ex); - } - } -} diff --git a/source/java/org/alfresco/repo/security/encryption/DefaultEncryptor.java b/source/java/org/alfresco/repo/security/encryption/DefaultEncryptor.java new file mode 100644 index 0000000000..a59414844a --- /dev/null +++ b/source/java/org/alfresco/repo/security/encryption/DefaultEncryptor.java @@ -0,0 +1,75 @@ +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 threadCipher; + + /** + * Default constructor for IOC + */ + public DefaultEncryptor() + { + threadCipher = new ThreadLocal(); + } + + /** + * 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; + } +} diff --git a/source/java/org/alfresco/repo/security/encryption/EncryptionEngine.java b/source/java/org/alfresco/repo/security/encryption/EncryptionEngine.java deleted file mode 100644 index 46a45353ab..0000000000 --- a/source/java/org/alfresco/repo/security/encryption/EncryptionEngine.java +++ /dev/null @@ -1,12 +0,0 @@ -package org.alfresco.repo.security.encryption; - -import java.io.UnsupportedEncodingException; - - -public interface EncryptionEngine -{ - public byte[] encrypt(byte[] input); - public byte[] decrypt(byte[] input); - public byte[] encryptString(String input) throws UnsupportedEncodingException; - public String decryptAsString(byte[] input) throws UnsupportedEncodingException; -} diff --git a/source/java/org/alfresco/repo/security/encryption/Encryptor.java b/source/java/org/alfresco/repo/security/encryption/Encryptor.java new file mode 100644 index 0000000000..ff5d075692 --- /dev/null +++ b/source/java/org/alfresco/repo/security/encryption/Encryptor.java @@ -0,0 +1,86 @@ +package org.alfresco.repo.security.encryption; + +import java.io.Serializable; +import java.security.AlgorithmParameters; + +import javax.crypto.Cipher; +import javax.crypto.SealedObject; + +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 null 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 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 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 + * + * @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 + */ + SealedObject sealObject(String keyAlias, AlgorithmParameters params, Serializable input); + + /** + * Convenience method to unseal on object up cryptographically. + *

+ * Note that the algorithm parameters are stored in the sealed object and are + * not therefore required for decryption. + * + * @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 + */ + Serializable unsealObject(String keyAlias, SealedObject input); +} diff --git a/source/java/org/alfresco/repo/security/encryption/EncryptorTest.java b/source/java/org/alfresco/repo/security/encryption/EncryptorTest.java new file mode 100644 index 0000000000..e60a9f1f4d --- /dev/null +++ b/source/java/org/alfresco/repo/security/encryption/EncryptorTest.java @@ -0,0 +1,61 @@ +package org.alfresco.repo.security.encryption; + +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 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 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 encryptedPair = encryptor.encryptObject("mykey2", null, testObject); + Object output = encryptor.decryptObject( + "mykey2", + encryptedPair.getSecond(), + encryptedPair.getFirst()); + assertEquals("Encryption round trip failed. ", testObject, output); + } +} diff --git a/source/java/org/alfresco/repo/security/encryption/KeyProvider.java b/source/java/org/alfresco/repo/security/encryption/KeyProvider.java index 86405777d1..044d65a9f0 100644 --- a/source/java/org/alfresco/repo/security/encryption/KeyProvider.java +++ b/source/java/org/alfresco/repo/security/encryption/KeyProvider.java @@ -3,11 +3,37 @@ package org.alfresco.repo.security.encryption; import java.security.Key; /** - * A key provider returns the secret key used to encrypt text and mltext properties in the - * database. - * + * A key provider returns the secret keys for different use cases. + * + * @since 4.0 */ public interface KeyProvider { - public Key getKey(); + /** + * Enumeration of key aliases supported internally by Alfresco + * + * @author derekh + * @since 4.0 + */ + public static enum AlfrescoKeyAlias + { + METADATA, + SOLR + } + + /** + * Get an encryption key if available. + * + * @param keyAlias the key alias + * @return the encryption key or null if there is no associated key + */ + public Key getKey(String keyAlias); + + /** + * Get an encryption key if available, using a convenience constant. + * + * @param keyAlias the key alias + * @return the encryption key or null if there is no associated key + */ + public Key getKey(AlfrescoKeyAlias keyAlias); } diff --git a/source/java/org/alfresco/repo/security/encryption/KeyStoreKeyProviderTest.java b/source/java/org/alfresco/repo/security/encryption/KeyStoreKeyProviderTest.java new file mode 100644 index 0000000000..da03c05c0c --- /dev/null +++ b/source/java/org/alfresco/repo/security/encryption/KeyStoreKeyProviderTest.java @@ -0,0 +1,161 @@ +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"; + + public void setUp() throws Exception + { + } + + /** + * Helper utility to create a two-alias keystore. + */ + /* package */ static KeystoreKeyProvider getTestKeyStoreProvider() + { + Map passwords = new HashMap(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 testNoKeyStorePasswords() throws Exception + { + KeystoreKeyProvider keyProvider = new KeystoreKeyProvider( + FILE_ONE, + "SunJCE", + "JCEKS", + Collections.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 passwords = new HashMap(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 passwords = new HashMap(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"); + } +} diff --git a/source/java/org/alfresco/repo/security/encryption/KeystoreKeyProvider.java b/source/java/org/alfresco/repo/security/encryption/KeystoreKeyProvider.java index c8ff17131b..00848060f8 100644 --- a/source/java/org/alfresco/repo/security/encryption/KeystoreKeyProvider.java +++ b/source/java/org/alfresco/repo/security/encryption/KeystoreKeyProvider.java @@ -1,171 +1,246 @@ package org.alfresco.repo.security.encryption; -import java.io.BufferedInputStream; import java.io.File; import java.io.FileInputStream; -import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.security.Key; import java.security.KeyStore; -import java.security.KeyStoreException; -import java.security.NoSuchAlgorithmException; -import java.security.cert.CertificateException; - -import javax.crypto.SecretKey; +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.ParameterCheck; +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); - private static String KEY_STORE_TYPE = "JCEKS"; - private static String SECRET_KEY_ALIAS = "secret"; - - // key store holding the secret key for encrypting and decrypting repository properties - private String keyStoreFile; - - // key store passwords - private char[] keyStorePassword; - private char[] secretKeyPassword; - - public void setKeyStoreFile(String keyStoreFile) - { - this.keyStoreFile = keyStoreFile; - } - - public void setKeyStorePassword(String keyStorePassword) - { - this.keyStorePassword = keyStorePassword.toCharArray(); - } + // Will be cleared after initialization + private Map passwords; + private String location; + private String provider; + private String type; + private Map keys; - public void setSecretKeyPassword(String secretKeyPassword) - { - this.secretKeyPassword = secretKeyPassword.toCharArray(); - } - - public Key getKey() - { - return super.getKey(); - } + private final ReadLock readLock; + private final WriteLock writeLock; - protected void saveKeyStore(KeyStore ks) throws IOException, CertificateException, NoSuchAlgorithmException, KeyStoreException - { - FileOutputStream fos = null; - - try - { - fos = new FileOutputStream(keyStoreFile); - ks.store(fos, keyStorePassword); - } - finally - { - if(fos != null) - { - fos.close(); - } - } - } - - /* - * Create a new secret key and store it in the keystore ks + /** + * Constructs the provider with required defaults */ - protected void createSecretKey(KeyStore ks) throws Exception + public KeystoreKeyProvider() { - Key key = generateSecretKey(); - if(key == null) - { - throw new AlfrescoRuntimeException("Unable to generate secret key"); - } - - byte[] encoded = key.getEncoded(); - - logger.debug("secret key size = " + (encoded.length * 8) + " bits"); - - KeyStore.SecretKeyEntry skEntry = new KeyStore.SecretKeyEntry((SecretKey)key); - - ks.setEntry(SECRET_KEY_ALIAS, skEntry, new KeyStore.PasswordProtection(secretKeyPassword)); - - saveKeyStore(ks); + ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); + readLock = lock.readLock(); + writeLock = lock.writeLock(); + keys = new HashMap(7); } - protected void loadKeyStore() + /** + * Convenience constructor for tests. Note that {@link #init()} is also called. + */ + /* package */ KeystoreKeyProvider(String location, String provider, String type, Map passwords) { - InputStream is = null; - KeyStore ks = null; - - try - { - ks = KeyStore.getInstance(KEY_STORE_TYPE); - - File f = new File(keyStoreFile); - if(!f.exists()) - { - // no keystore, create one and save it - ks.load(null, keyStorePassword); - - // generate a secret key - createSecretKey(ks); - } - else - { - is = new BufferedInputStream(new FileInputStream(keyStoreFile)); - ks.load(is, keyStorePassword); - } - } - catch(Throwable e) - { - throw new AlfrescoRuntimeException( - "Unable to load keystore from " + keyStoreFile, e); - } - finally - { - if (is != null) - - { - try - { - is.close(); - } - catch(IOException e) - { - } - } - } + this(); + setLocation(location); + setProvider(provider); + setType(type); + setPasswords(passwords); + init(); + } - try - { - Key key = ks.getKey(SECRET_KEY_ALIAS, secretKeyPassword); - if(key == null) - { - createSecretKey(ks); - } + public void setLocation(String location) + { + this.location = location; + } - setKey(key); - } - catch(Throwable e) - { - throw new AlfrescoRuntimeException( - "Unable to get secret key from " + keyStoreFile, e); - } + 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. + *

+ * Where required, null 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 null values + */ + public void setPasswords(Map passwords) + { + this.passwords = new HashMap(passwords); } public void init() { - ParameterCheck.mandatory("keyStoreFile", keyStoreFile); - ParameterCheck.mandatory("passwordGenerator", passwordGenerator); + 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); - loadKeyStore(); + // 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 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(); + } + } } diff --git a/source/java/org/alfresco/repo/security/encryption/TestKeyProvider.java b/source/java/org/alfresco/repo/security/encryption/TestKeyProvider.java deleted file mode 100644 index 3bcb6a8fb9..0000000000 --- a/source/java/org/alfresco/repo/security/encryption/TestKeyProvider.java +++ /dev/null @@ -1,26 +0,0 @@ -package org.alfresco.repo.security.encryption; - -import java.security.Key; - -import javax.crypto.KeyGenerator; -import javax.crypto.SecretKey; - -import org.alfresco.error.AlfrescoRuntimeException; - -public class TestKeyProvider implements KeyProvider -{ - public Key getKey() - { - try - { - KeyGenerator keyGenerator = KeyGenerator.getInstance("AES"); - SecretKey key = keyGenerator.generateKey(); - return key; - // return Hex.decode("80000000000000000000000000000000"); - } - catch(Exception e) - { - throw new AlfrescoRuntimeException("Unexpected exception generating secret key", e); - } - } -} diff --git a/source/test-resources/alfresco/keystore-tests/encryption-test-context.xml b/source/test-resources/alfresco/keystore-tests/encryption-test-context.xml new file mode 100644 index 0000000000..a28492b7e3 --- /dev/null +++ b/source/test-resources/alfresco/keystore-tests/encryption-test-context.xml @@ -0,0 +1,15 @@ + + + + + + + + diff --git a/source/test-resources/alfresco/keystore-tests/ks-test-1.jks b/source/test-resources/alfresco/keystore-tests/ks-test-1.jks new file mode 100644 index 0000000000000000000000000000000000000000..01bd383454a4286748fc53e92235e76972d3c396 GIT binary patch literal 643 zcmX?i?%X*B1_mY|W&~np2DaSF?9@s_AlG2(W0Tl5Zy8vNix`ZP^K@CmS zv7NExlFv)8KO5RsS!5+IxO}#I(S<*4wJ+~1{+>49H|MPPrT?$j_gkDlV}ANWd6N75 z{Y$E&8COmFvfb-Ym~7~g_p1e$SwCFeSSd@aoV z@v5WuPpf}cYb}}3yo6^DSrWV4HW3{jJU4 zhs^xaXDm!}T~c7cUH&#w(+1+IQjuf8v$!tBgfDG736xaByN`*wi2IL7uuf{&c3?zF1+DJn1b$FG{gx-na6PYHuqfRk%@W=V#xi>YH?N=Q*= hK~AcRYcNiUA6;U+mJPQ$t7lJ`tF6`dQCD#OB>*9&1j7IT literal 0 HcmV?d00001 diff --git a/source/test-resources/alfresco/keystore-tests/ks-test-2.jks b/source/test-resources/alfresco/keystore-tests/ks-test-2.jks new file mode 100644 index 0000000000000000000000000000000000000000..37232225004cd4771d4732f946dd4136e7c79fb0 GIT binary patch literal 1254 zcmX?i?%X*B1_mZ5W@cc^t;|lXG-6<2G?@BCKz7Yr2G-&t2IJ)XT)pDbJiX+i%7T)7 zy@I0rvdom!BE8_$#GKR=|D>$c$18-_xa(+r`N15>n9G=l(85M*y^igSKm zNorn6AwwMllMe%D0a%q|PI?K0kPpZL{hY+Sbp7CxqRhN>Yaa&oVxaSY(jfKB6$Pb5 zAoJFI7XQJ<5%K`!t^%OH`3-nkIL`RqFxWQXz8w>zNGV7)D=+{Q(zoq?wcwV*8^gVw z++tnJmhOD*%6v*?YPw>EoV1c_|HP{u(QLvhjLGtrSv^Tjdzt-S{a(RqIiG5lZQS$mwA=m7qN-k+SR!pwZ5+lv!zrjOOm_9|S(h}GGo@y=-k-LgJ-^mxyM={Cdv>n(M+FZ32^yM- zHaC{)KR&RK-FTher>xrS$!Wca$6I_9N>6lE6V zq`J5USFfIPjoxv zev5nB;eJuU_Jp$4Gox%z?wf8r_p)1(7Hj0f|NE4q4lm@b+rUwB?vT3S=jb~}bom** zTi>^+^Mt3!9Q{8zqOoF9mh8suReWI!*Zj54@z=O1e`djjb?&W4-`^1}o<^zkQgq$f`xrS@-+--C=i@Ti=d6vmkiB!nNtf`=d*h!Y+MKX?ddc<5u+7 zSKrTHwD$N>-_+pA<-b`n@6Dr^=X4j#=kd)iSo>Le!>1$t7<2Mc%2E0Y&*GKu&(q+3-`5&kc451I?}|Vvx*A zEK96_=||RPXJ)(X@b-`bCI*&h2Hw=Xlz_yd#N6T%2Bv7IXa)hGAjsa-6zBZB zlGMDCLWVj9CLadQ0(h`ZH5JXMVWc&);VyTp1PJzanDzuMh3JGl9sx7`zAR_4AaUU4oj zVrRA1`~SIW6V+~n?0h!mO-8%z?!;y2zD zrE=Uaq{~?hUKnJWb6AM%nE9%qI8>zT zdGEE3D-Ih<6rTN>z~Yy1uKs53>aLloPmkp4?W-$uTl+A4-Rr{VPFJs{u|8XEs$W?3 zgfrDdjVWPO{@-K;ttji+c8C5~-ka;d$NzV^%KDZ&?stwo3BM&Lw4~#X@GOZ50k_ZW zS#iSS;=IduZ`t;=X9&9s>swYB?>hObPO`tkD0|g~j<~Bu`BPrDFMSm>i@C32)~`yx zMVVeNb0&wpElugkn_pAI&3J#Ct(;at`{8vQx1;`_*NorZ>9|t1W13m7L`izIfV;?{ zDGBTJdZw?bli2T|ayD$!`qho?uFq_<6FmNf9+pT+x60XFyYc1)AD{i|p&P$Q>aLKw zQn=uK?#Id_OD@f1I`qZm>zM;5>-%t~B%BhF=kBmbTK9CwsLz=hzIWOLcA*c`0KnA# AVgLXD literal 0 HcmV?d00001 diff --git a/source/test-resources/alfresco/keystore-tests/ks-test-4.jks b/source/test-resources/alfresco/keystore-tests/ks-test-4.jks new file mode 100644 index 0000000000000000000000000000000000000000..0e5c097f707aafb2653518e2d32eb05a502d29ad GIT binary patch literal 643 zcmX?i?%X*B1_mY|W&~np2DaSF?9@s_AlG2(UAYHq-ZHQj7cm$o=jZAbm*(ju7gZLN zXRatH zEdrUh=Ck+@Hja=7Aa@l2{mgH`%fbz=RzABS*}*yDb>5eK+nkO+VAPJ# zm$Xa0JI_d}Uj26Ybnd|AsVBC)`0c#p1*p> z8CDlsw+o$L*)3YT|I~T?#z{MZWm?Y){L$_+n0};cYs?336OAi}1@b1`mQs5A` zYk+L6<;NRuH9jumQd@LnmQvVJ`CsnOYxa~Y3j2N+aGJW^q~^<~C9kz!$4vZpn903h zwb#=nX=`8H+4#15NB@f0_Zr#@OfILHiUptGdSA3Z#$|2^gIIu*Yj|c!hOdjMV_r%~ jQD#9-s*7tdPKj9>ds>%Q760if75FURtM$wO;iM=4_3sTg literal 0 HcmV?d00001 diff --git a/source/test-resources/alfresco/keystore-tests/passwords.txt b/source/test-resources/alfresco/keystore-tests/passwords.txt new file mode 100644 index 0000000000..9a3aba0085 --- /dev/null +++ b/source/test-resources/alfresco/keystore-tests/passwords.txt @@ -0,0 +1,37 @@ +The keystores contained in this folder are used by tests. +There are their details: + +================== +== ks-test-1.ks == +================== + +Keystore password: ksPwd1 +Keystore type: JCEKS +Keystore provider: SunJCE + +Your keystore contains 1 entry +mykey1: aliasPwd1 + +================== +== ks-test-2.ks == +================== + +Keystore password: ksPwd2 +Keystore type: JCEKS +Keystore provider: SunJCE + +Your keystore contains 2 entries +mykey1: aliasPwd1 +mykey2: aliasPwd2 + +================== +== ks-test-3.ks == +================== + +Keystore password: ksPwd3 +Keystore type: JCEKS +Keystore provider: SunJCE + +Your keystore contains 2 entries +mykey1: aliasPwd1 +mykey2: aliasPwd2 \ No newline at end of file