diff --git a/config/alfresco/bootstrap-context.xml b/config/alfresco/bootstrap-context.xml
index 748086dea8..40c97a0249 100644
--- a/config/alfresco/bootstrap-context.xml
+++ b/config/alfresco/bootstrap-context.xml
@@ -173,6 +173,18 @@
+
+
+
+
+
+
+ metadata
+ solr
+
+
+
+
diff --git a/config/alfresco/encryption-context.xml b/config/alfresco/encryption-context.xml
index 1821136c03..bbe5f12799 100644
--- a/config/alfresco/encryption-context.xml
+++ b/config/alfresco/encryption-context.xml
@@ -9,22 +9,16 @@
http://code.google.com/p/spring-crypto-utils/schema/crypt
http://code.google.com/p/spring-crypto-utils/schema/crypt.xsd">
-
+
-
+
-
-
-
+
diff --git a/config/alfresco/keystore-passwords.properties b/config/alfresco/keystore-passwords.properties
new file mode 100644
index 0000000000..251d17c0d7
--- /dev/null
+++ b/config/alfresco/keystore-passwords.properties
@@ -0,0 +1,6 @@
+# The password protecting the keystore entries
+keystore=mp6yc0UD9e
+# The password protecting the alias: metadata
+metadata=oKIWzVdEdA
+# The password protecting the alias: solr
+solr=TxHTtOnrwQ
\ No newline at end of file
diff --git a/config/alfresco/repository.properties b/config/alfresco/repository.properties
index b63aca8789..6083e73247 100644
--- a/config/alfresco/repository.properties
+++ b/config/alfresco/repository.properties
@@ -673,15 +673,9 @@ deployment.filesystem.default.metadatadir=${deployment.filesystem.metadatadir}/d
#
encryption.encryption.cipherAlgorithm=DESede/CBC/PKCS5Padding
encryption.keystore.location=classpath:alfresco/.keystore
-encryption.keystore.passwordsFile.location=
+encryption.keystore.passwordsFile.location=classpath:alfresco/keystore-passwords.properties
encryption.keystore.provider=
encryption.keystore.type=JCEKS
-# The password protecting the keystore entries
-encryption.keystore.password=mp6yc0UD9e
-# The password protecting the alias: metadata
-encryption.keystore.password.metadata=oKIWzVdEdA
-# The password protecting the alias: solr
-encryption.keystore.password.solr=TxHTtOnrwQ
encryption.messageTimeout=30000
encryption.macAlgorithm=HmacSHA1
diff --git a/source/java/org/alfresco/encryption/EncryptionChecker.java b/source/java/org/alfresco/encryption/EncryptionChecker.java
new file mode 100644
index 0000000000..0ea16d8025
--- /dev/null
+++ b/source/java/org/alfresco/encryption/EncryptionChecker.java
@@ -0,0 +1,213 @@
+/*
+ * Copyright (C) 2005-2011 Alfresco Software Limited.
+ *
+ * This file is part of Alfresco
+ *
+ * Alfresco is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Alfresco is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Alfresco. If not, see .
+ */
+package org.alfresco.encryption;
+
+import java.io.Serializable;
+import java.security.InvalidKeyException;
+import java.util.List;
+
+import org.alfresco.error.AlfrescoRuntimeException;
+import org.alfresco.repo.transaction.RetryingTransactionHelper;
+import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
+import org.alfresco.service.cmr.attributes.AttributeService;
+import org.alfresco.service.transaction.TransactionService;
+import org.alfresco.util.EqualsHelper;
+import org.alfresco.util.GUID;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.springframework.context.ApplicationEvent;
+import org.springframework.extensions.surf.util.AbstractLifecycleBean;
+
+/**
+ * The EncryptionChecker checks the state of the repository's encryption system.
+ * In particular it checks:
+ *
+ * - that the encryption keys have not been changed. If so, the bootstrap will be halted.
+ *
+ *
+ * @since 4.0
+ *
+ */
+public class EncryptionChecker extends AbstractLifecycleBean
+{
+ private static Log logger = LogFactory. getLog(EncryptionChecker.class);
+ public static String TOP_LEVEL_KEY = "keyCheck";
+ private static enum KEY_STATUS
+ {
+ OK, CHANGED, MISSING;
+ };
+
+ private TransactionService transactionService;
+ private AttributeService attributeService;
+ private Encryptor encryptor;
+ private List keyAliases;
+
+ public void setTransactionService(TransactionService transactionService)
+ {
+ this.transactionService = transactionService;
+ }
+
+ public void setAttributeService(AttributeService attributeService)
+ {
+ this.attributeService = attributeService;
+ }
+
+ public void setKeyAliases(List keyAliases)
+ {
+ this.keyAliases = keyAliases;
+ }
+
+ public void setEncryptor(Encryptor encryptor)
+ {
+ this.encryptor = encryptor;
+ }
+
+ private void removeKey(String keyAlias)
+ {
+ attributeService.removeAttributes(TOP_LEVEL_KEY);
+ logger.info("Removed registered key " + keyAlias);
+ }
+
+ private KEY_STATUS checkKey(String keyAlias)
+ {
+ if(attributeService.exists(TOP_LEVEL_KEY, keyAlias))
+ {
+ try
+ {
+ // check that the key has not changed by decrypting the encrypted guid attribute
+ // comparing against the guid
+ KeyCheck keyCheck = (KeyCheck)attributeService.getAttribute(TOP_LEVEL_KEY, keyAlias);
+ Serializable storedGUID = encryptor.unsealObject(keyAlias, keyCheck.getEncrypted());
+ return EqualsHelper.nullSafeEquals(storedGUID, keyCheck.getGuid()) ? KEY_STATUS.OK : KEY_STATUS.CHANGED;
+ }
+ catch(InvalidKeyException e)
+ {
+ // key exception indicates that the key has changed - it can't decrypt the
+ // previously-encrypted data
+ return KEY_STATUS.CHANGED;
+ }
+ }
+ else
+ {
+ // register the key by creating an attribute that stores a guid and its encrypted value
+ String guid = GUID.generate();
+ Serializable encrypted = encryptor.sealObject(keyAlias, null, guid);
+ KeyCheck keyCheck = new KeyCheck(guid, encrypted);
+ attributeService.createAttribute(keyCheck, TOP_LEVEL_KEY, keyAlias);
+ logger.info("Registered key " + keyAlias);
+ return KEY_STATUS.MISSING;
+ }
+ }
+
+ @Override
+ protected void onBootstrap(ApplicationEvent event)
+ {
+ RetryingTransactionHelper retryingTransactionHelper = transactionService.getRetryingTransactionHelper();
+ final RetryingTransactionCallback checkKeysCallback = new RetryingTransactionCallback()
+ {
+ public Void execute() throws Throwable
+ {
+ for(String keyAlias : keyAliases)
+ {
+ KEY_STATUS keyStatus = checkKey(keyAlias);
+ if(keyStatus == KEY_STATUS.CHANGED)
+ {
+ // Note: this will halt the application bootstrap.
+ throw new AlfrescoRuntimeException("The key with alias " + keyAlias + " has been changed, re-instate the previous keystore");
+ }
+ }
+
+ return null;
+ }
+ };
+ retryingTransactionHelper.doInTransaction(checkKeysCallback, false);
+ }
+
+ @Override
+ protected void onShutdown(ApplicationEvent event)
+ {
+
+ }
+
+ public void removeRegisteredKeys()
+ {
+ RetryingTransactionHelper retryingTransactionHelper = transactionService.getRetryingTransactionHelper();
+ final RetryingTransactionCallback removeKeysCallback = new RetryingTransactionCallback()
+ {
+ public Void execute() throws Throwable
+ {
+ for(String keyAlias : keyAliases)
+ {
+ removeKey(keyAlias);
+ }
+
+ return null;
+ }
+ };
+ retryingTransactionHelper.doInTransaction(removeKeysCallback, false);
+ }
+
+ /**
+ * A KeyCheck object stores a well-known guid and it's encrypted value.
+ *
+ * @since 4.0
+ *
+ */
+ private static class KeyCheck implements Serializable
+ {
+ private static final long serialVersionUID = 4514315444977162903L;
+
+ private String guid;
+ private Serializable encrypted;
+
+ public KeyCheck(String guid, Serializable encrypted)
+ {
+ super();
+ this.guid = guid;
+ this.encrypted = encrypted;
+ }
+
+ public String getGuid()
+ {
+ return guid;
+ }
+
+ public Serializable getEncrypted()
+ {
+ return encrypted;
+ }
+
+ public boolean equals(Object other)
+ {
+ if(this == other)
+ {
+ return true;
+ }
+
+ if(!(other instanceof KeyCheck))
+ {
+ return false;
+ }
+ KeyCheck keyCheck = (KeyCheck)other;
+ return EqualsHelper.nullSafeEquals(keyCheck.getGuid(), getGuid()) &&
+ EqualsHelper.nullSafeEquals(keyCheck.getEncrypted(), getEncrypted());
+ }
+ }
+
+}
diff --git a/source/java/org/alfresco/encryption/EncryptorTest.java b/source/java/org/alfresco/encryption/EncryptorTest.java
index 9e230ef805..7e2617d866 100644
--- a/source/java/org/alfresco/encryption/EncryptorTest.java
+++ b/source/java/org/alfresco/encryption/EncryptorTest.java
@@ -1,11 +1,30 @@
+/*
+ * Copyright (C) 2005-2011 Alfresco Software Limited.
+ *
+ * This file is part of Alfresco
+ *
+ * Alfresco is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Alfresco is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Alfresco. If not, see .
+ */
package org.alfresco.encryption;
import java.io.Serializable;
import java.security.AlgorithmParameters;
+import java.security.KeyException;
import junit.framework.TestCase;
-import org.alfresco.encryption.DefaultEncryptor;
+import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.util.Pair;
import org.bouncycastle.util.Arrays;
@@ -66,7 +85,15 @@ public class EncryptorTest extends TestCase
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);
+ try
+ {
+ Object output = encryptor.unsealObject("mykey2", sealedObject);
+ assertEquals("Encryption round trip failed. ", testObject, output);
+ }
+ catch(KeyException e)
+ {
+ throw new AlfrescoRuntimeException("", e)
+; }
+
}
}
diff --git a/source/java/org/alfresco/encryption/KeyStoreKeyProviderTest.java b/source/java/org/alfresco/encryption/KeyStoreKeyProviderTest.java
index 575ab39461..be451e34d9 100644
--- a/source/java/org/alfresco/encryption/KeyStoreKeyProviderTest.java
+++ b/source/java/org/alfresco/encryption/KeyStoreKeyProviderTest.java
@@ -1,7 +1,23 @@
+/*
+ * Copyright (C) 2005-2011 Alfresco Software Limited.
+ *
+ * This file is part of Alfresco
+ *
+ * Alfresco is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Alfresco is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Alfresco. If not, see .
+ */
package org.alfresco.encryption;
-import java.io.FileNotFoundException;
-import java.io.InputStream;
import java.security.Key;
import java.security.KeyStore;
import java.security.UnrecoverableKeyException;
@@ -11,8 +27,6 @@ import java.util.Map;
import junit.framework.TestCase;
-import org.alfresco.encryption.KeyStoreLoader;
-import org.alfresco.encryption.KeystoreKeyProvider;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.util.ApplicationContextHelper;
import org.springframework.context.ApplicationContext;
@@ -53,9 +67,9 @@ public class KeyStoreKeyProviderTest extends TestCase
return ks;
}
- protected static KeyStoreLoader getKeyStoreLoader()
+ protected static KeyResourceLoader getKeyStoreLoader()
{
- return new SpringKeyStoreLoader();
+ return new SpringKeyResourceLoader();
}
public void setUp() throws Exception
diff --git a/source/java/org/alfresco/repo/node/encryption/MetadataEncryptor.java b/source/java/org/alfresco/repo/node/encryption/MetadataEncryptor.java
index 911e045ace..4d88913ea2 100644
--- a/source/java/org/alfresco/repo/node/encryption/MetadataEncryptor.java
+++ b/source/java/org/alfresco/repo/node/encryption/MetadataEncryptor.java
@@ -1,6 +1,7 @@
package org.alfresco.repo.node.encryption;
import java.io.Serializable;
+import java.security.KeyException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
@@ -10,6 +11,7 @@ import javax.crypto.SealedObject;
import org.alfresco.encryption.Encryptor;
import org.alfresco.encryption.KeyProvider;
+import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.repo.security.authentication.AuthenticationException;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.service.cmr.dictionary.DataTypeDefinition;
@@ -107,9 +109,16 @@ public class MetadataEncryptor
{
return inbound;
}
- Serializable outbound = encryptor.unsealObject(KeyProvider.ALIAS_METADATA, inbound);
- // Done
- return outbound;
+ try
+ {
+ Serializable outbound = encryptor.unsealObject(KeyProvider.ALIAS_METADATA, inbound);
+ // Done
+ return outbound;
+ }
+ catch(KeyException e)
+ {
+ throw new AlfrescoRuntimeException("Invalid metadata decryption key", e);
+ }
}
/**
@@ -198,9 +207,16 @@ public class MetadataEncryptor
// We have already checked for nulls and conversions
Serializable value = inbound.get(propertyQName);
// Have to decrypt the value
- Serializable unencryptedValue = encryptor.unsealObject(KeyProvider.ALIAS_METADATA, value);
- // Store it back
- outbound.put(propertyQName, unencryptedValue);
+ try
+ {
+ Serializable unencryptedValue = encryptor.unsealObject(KeyProvider.ALIAS_METADATA, value);
+ // Store it back
+ outbound.put(propertyQName, unencryptedValue);
+ }
+ catch(KeyException e)
+ {
+ throw new AlfrescoRuntimeException("Invalid metadata decryption key", e);
+ }
}
// Done
return outbound;