Skip to content

Commit b2068e6

Browse files
Merge pull request #196 from xenit-eu/ACC-2372-alfresco-decryption
[ACC-2372] Add support for encrypted content migrated from Alfresco
2 parents 6efead7 + 30bb2b1 commit b2068e6

File tree

50 files changed

+1228
-59
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

50 files changed

+1228
-59
lines changed

contentgrid-appserver-autoconfigure/src/main/java/com/contentgrid/appserver/autoconfigure/contentstore/EncryptedContentStoreAutoConfiguration.java

Lines changed: 43 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,25 +5,32 @@
55
import com.contentgrid.appserver.contentstore.api.ContentStore;
66
import com.contentgrid.appserver.contentstore.impl.encryption.EncryptedContentStore;
77
import com.contentgrid.appserver.contentstore.impl.encryption.engine.AesCtrEncryptionEngine;
8+
import com.contentgrid.appserver.contentstore.impl.encryption.engine.AlfrescoCompatibleEncryptionEngine;
89
import com.contentgrid.appserver.contentstore.impl.encryption.engine.ContentEncryptionEngine;
910
import com.contentgrid.appserver.contentstore.impl.encryption.keys.DataEncryptionKeyAccessor;
1011
import com.contentgrid.appserver.contentstore.impl.encryption.keys.DataEncryptionKeyWrapper;
1112
import com.contentgrid.appserver.contentstore.impl.encryption.keys.TableStorageDataEncryptionKeyAccessor;
1213
import com.contentgrid.appserver.contentstore.impl.encryption.keys.UnencryptedSymmetricDataEncryptionKeyWrapper;
14+
1315
import java.util.List;
1416
import java.util.Set;
17+
1518
import org.jooq.DSLContext;
19+
import org.springframework.beans.factory.InitializingBean;
1620
import org.springframework.boot.autoconfigure.AutoConfiguration;
21+
import org.springframework.boot.autoconfigure.condition.AllNestedConditions;
1722
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
1823
import org.springframework.boot.autoconfigure.condition.ConditionalOnBooleanProperty;
1924
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
2025
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
21-
import org.springframework.boot.autoconfigure.jooq.JooqAutoConfiguration;
2226
import org.springframework.boot.context.properties.ConfigurationProperties;
2327
import org.springframework.boot.context.properties.EnableConfigurationProperties;
2428
import org.springframework.boot.context.properties.bind.DefaultValue;
2529
import org.springframework.context.annotation.Bean;
30+
import org.springframework.context.annotation.Conditional;
31+
import org.springframework.context.annotation.Configuration;
2632
import org.springframework.context.annotation.Primary;
33+
import org.springframework.context.annotation.ConfigurationCondition.ConfigurationPhase;
2734

2835
@AutoConfiguration
2936
@ConditionalOnClass(EncryptedContentStore.class)
@@ -32,8 +39,8 @@
3239
public class EncryptedContentStoreAutoConfiguration {
3340

3441
@Bean
35-
@ConditionalOnMissingBean
36-
DataEncryptionKeyAccessor tableStorageEncryptionKeyAccessor(DSLContext dslContext) {
42+
@ConditionalOnMissingBean(DataEncryptionKeyAccessor.class)
43+
TableStorageDataEncryptionKeyAccessor tableStorageEncryptionKeyAccessor(DSLContext dslContext) {
3744
return new TableStorageDataEncryptionKeyAccessor(dslContext);
3845
}
3946

@@ -55,6 +62,12 @@ ContentStore encryptedContentStore(ContentStore contentStore, DataEncryptionKeyA
5562
return new EncryptedContentStore(contentStore, encryptionKeyAccessor, encryptionKeyWrappers, encryptionEngines);
5663
}
5764

65+
@Bean
66+
@Conditional(TableInitializerCondition.class)
67+
TableInitializer dekTableInitializer(TableStorageDataEncryptionKeyAccessor encryptionKeyAccessor) {
68+
return new TableInitializer(encryptionKeyAccessor);
69+
}
70+
5871
private DataEncryptionKeyWrapper dataEncryptionKeyWrapperForAlgorithm(EncryptionKeyWrapperAlgorithm algorithm) {
5972
return switch (algorithm) {
6073
case NONE -> new UnencryptedSymmetricDataEncryptionKeyWrapper(true);
@@ -66,6 +79,7 @@ private ContentEncryptionEngine contentEncryptionEngineForAlgorithm(EncryptionEn
6679
case AES128_CTR -> new AesCtrEncryptionEngine(128);
6780
case AES192_CTR -> new AesCtrEncryptionEngine(192);
6881
case AES256_CTR -> new AesCtrEncryptionEngine(256);
82+
case ALFRESCO -> new AlfrescoCompatibleEncryptionEngine();
6983
};
7084
}
7185

@@ -88,6 +102,31 @@ enum EncryptionKeyWrapperAlgorithm {
88102
enum EncryptionEngineAlgorithm {
89103
AES128_CTR,
90104
AES192_CTR,
91-
AES256_CTR
105+
AES256_CTR,
106+
ALFRESCO
107+
}
108+
109+
private static class TableInitializerCondition extends AllNestedConditions {
110+
111+
public TableInitializerCondition() {
112+
super(ConfigurationPhase.REGISTER_BEAN);
113+
}
114+
115+
@ConditionalOnBean(TableStorageDataEncryptionKeyAccessor.class)
116+
static class UsesTableStorage {}
117+
118+
@ConditionalOnBooleanProperty("contentgrid.appserver.content.encryption.bootstrap-tables")
119+
static class TableBootstrapConfigured {}
120+
}
121+
122+
@lombok.Value
123+
private static class TableInitializer implements InitializingBean {
124+
125+
private TableStorageDataEncryptionKeyAccessor encryptionKeyAccessor;
126+
127+
@Override
128+
public void afterPropertiesSet() throws Exception {
129+
encryptionKeyAccessor.setupTables();
130+
}
92131
}
93132
}

contentgrid-appserver-autoconfigure/src/main/java/com/contentgrid/appserver/autoconfigure/query/engine/JOOQQueryEngineAutoConfiguration.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ TableCreator jooqTableCreator(DSLContextResolver dslContextResolver) {
6767
@Bean
6868
@ConditionalOnBean(ApplicationResolver.class)
6969
@ConditionalOnBooleanProperty("contentgrid.appserver.query-engine.bootstrap-tables")
70-
TableInitializer tableInitializer(TableCreator tableCreator, ApplicationResolver applicationResolver) {
70+
TableInitializer jooqTableInitializer(TableCreator tableCreator, ApplicationResolver applicationResolver) {
7171
return new TableInitializer(tableCreator, applicationResolver);
7272
}
7373

contentgrid-appserver-autoconfigure/src/test/java/com/contentgrid/appserver/autoconfigure/contentstore/EncryptedContentStoreAutoConfigurationTest.java

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import com.contentgrid.appserver.contentstore.impl.encryption.testing.InMemoryDataEncryptionKeyAccessor;
1313
import com.contentgrid.appserver.contentstore.impl.encryption.testing.XorTestEncryptionEngine;
1414
import com.contentgrid.appserver.contentstore.impl.fs.FilesystemContentStore;
15+
1516
import java.util.Set;
1617
import org.jooq.DSLContext;
1718
import org.junit.jupiter.api.Test;
@@ -50,6 +51,7 @@ void checkDefaults() {
5051
.run(context -> {
5152
assertThat(context).hasNotFailed();
5253
assertThat(context).hasSingleBean(EncryptedContentStore.class);
54+
assertThat(context).doesNotHaveBean("dekTableInitializer");
5355
});
5456
}
5557

@@ -123,7 +125,8 @@ void checkWithMultipleEncryptionKeyAlgorithms() {
123125
.withPropertyValues(
124126
"contentgrid.appserver.content.encryption.engine.algorithms[0]=AES128-CTR",
125127
"contentgrid.appserver.content.encryption.engine.algorithms[1]=AES192-CTR",
126-
"contentgrid.appserver.content.encryption.engine.algorithms[2]=AES256-CTR"
128+
"contentgrid.appserver.content.encryption.engine.algorithms[2]=AES256-CTR",
129+
"contentgrid.appserver.content.encryption.engine.algorithms[3]=ALFRESCO"
127130
)
128131
.run(context -> {
129132
assertThat(context).hasNotFailed();
@@ -171,6 +174,31 @@ void checkWithCustomEncryptionKeyAccessor() {
171174
});
172175
}
173176

177+
@Test
178+
void checkWithBootStrapTables() {
179+
contextRunner
180+
.withPropertyValues("contentgrid.appserver.content.encryption.bootstrap-tables=true")
181+
.run(context -> {
182+
assertThat(context).hasNotFailed();
183+
assertThat(context).hasSingleBean(EncryptedContentStore.class);
184+
assertThat(context).hasSingleBean(TableStorageDataEncryptionKeyAccessor.class);
185+
assertThat(context).hasBean("dekTableInitializer");
186+
});
187+
}
188+
189+
@Test
190+
void checkWithCustomEncryptionKeyAccessorAndBootStrapTables() {
191+
contextRunner
192+
.withUserConfiguration(CustomEncryptionKeyAccessorConfiguration.class)
193+
.withPropertyValues("contentgrid.appserver.content.encryption.bootstrap-tables=true")
194+
.run(context -> {
195+
assertThat(context).hasNotFailed();
196+
assertThat(context).hasSingleBean(EncryptedContentStore.class);
197+
assertThat(context).doesNotHaveBean(TableStorageDataEncryptionKeyAccessor.class);
198+
assertThat(context).doesNotHaveBean("dekTableInitializer");
199+
});
200+
}
201+
174202
@Configuration
175203
static class CustomEncryptionKeyWrapperConfiguration {
176204

contentgrid-appserver-autoconfigure/src/test/java/com/contentgrid/appserver/autoconfigure/query/engine/JOOQQueryEngineAutoConfigurationTest.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ void checkDefaults() {
4242
assertThat(context).hasNotFailed();
4343
assertThat(context).hasSingleBean(DSLContextResolver.class);
4444
assertThat(context).hasSingleBean(QueryEngine.class);
45-
assertThat(context).doesNotHaveBean("tableInitializer");
45+
assertThat(context).doesNotHaveBean("jooqTableInitializer");
4646
});
4747
}
4848

@@ -55,7 +55,7 @@ void checkWithBootStrapTables() {
5555
assertThat(context).hasNotFailed();
5656
assertThat(context).hasSingleBean(DSLContextResolver.class);
5757
assertThat(context).hasSingleBean(QueryEngine.class);
58-
assertThat(context).hasBean("tableInitializer");
58+
assertThat(context).hasBean("jooqTableInitializer");
5959
});
6060
}
6161

@@ -67,7 +67,7 @@ void checkWithBootStrapTables_noApplication() {
6767
assertThat(context).hasNotFailed();
6868
assertThat(context).hasSingleBean(DSLContextResolver.class);
6969
assertThat(context).hasSingleBean(QueryEngine.class);
70-
assertThat(context).doesNotHaveBean("tableInitializer");
70+
assertThat(context).doesNotHaveBean("jooqTableInitializer");
7171
});
7272
}
7373

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/bin/

contentgrid-appserver-contentstore-impl-encryption/src/main/java/com/contentgrid/appserver/contentstore/impl/encryption/engine/AesCtrEncryptionEngine.java

Lines changed: 9 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,10 @@
66
import com.contentgrid.appserver.contentstore.api.range.ResolvedContentRange;
77
import com.contentgrid.appserver.contentstore.impl.encryption.UndecryptableContentException;
88
import com.contentgrid.appserver.contentstore.impl.encryption.keys.KeyBytes;
9+
import com.contentgrid.appserver.contentstore.impl.utils.SkippableCipherInputStream;
910
import com.contentgrid.appserver.contentstore.impl.utils.SkippingInputStream;
1011
import com.contentgrid.appserver.contentstore.impl.utils.ZeroPrefixedInputStream;
12+
1113
import java.io.InputStream;
1214
import java.math.BigInteger;
1315
import java.security.InvalidAlgorithmParameterException;
@@ -18,14 +20,14 @@
1820
import java.util.List;
1921
import java.util.Objects;
2022
import java.util.stream.Collectors;
23+
2124
import javax.crypto.Cipher;
2225
import javax.crypto.CipherInputStream;
2326
import javax.crypto.NoSuchPaddingException;
2427
import javax.crypto.spec.IvParameterSpec;
25-
import javax.security.auth.Destroyable;
28+
2629
import lombok.RequiredArgsConstructor;
2730
import lombok.SneakyThrows;
28-
import lombok.experimental.Delegate;
2931

3032
/**
3133
* Symmetric data encryption engine using AES-CTR encryption mode
@@ -77,7 +79,7 @@ private Cipher initializeCipher(EncryptionParameters parameters, boolean forEncr
7779
try {
7880
cipher.init(
7981
forEncryption ? Cipher.ENCRYPT_MODE : Cipher.DECRYPT_MODE,
80-
new AESSecretKey(parameters.getSecretKey()),
82+
new SecretKey(parameters.getSecretKey(), "AES"),
8183
new IvParameterSpec(parameters.getInitializationVector())
8284
);
8385
} finally {
@@ -164,29 +166,6 @@ private byte[] adjustIvForOffset(byte[] iv, long offsetBlocks) {
164166
}
165167
}
166168

167-
@RequiredArgsConstructor
168-
private static class AESSecretKey implements javax.crypto.SecretKey {
169-
@Delegate(types = Destroyable.class)
170-
private final KeyBytes keyBytes;
171-
172-
@Override
173-
public String getAlgorithm() {
174-
return "AES";
175-
}
176-
177-
@Override
178-
public String getFormat() {
179-
return "RAW";
180-
}
181-
182-
@Override
183-
public byte[] getEncoded() {
184-
// This one needs to be a copy, because the AES engine clears it.
185-
// We don't want to have it destroy our KeyBytes copy
186-
return keyBytes.getKeyBytesCopy();
187-
}
188-
}
189-
190169
@RequiredArgsConstructor
191170
private static class DecryptingContentReader implements ContentReader {
192171

@@ -197,7 +176,10 @@ private static class DecryptingContentReader implements ContentReader {
197176
@Override
198177
public InputStream getContentInputStream() throws UnreadableContentException {
199178
return new ZeroPrefixedInputStream(
200-
new CipherInputStream(
179+
// CipherInputStream does not skip(n) into not-yet-decrypted data
180+
// Spring StreamUtils.copyRange needs that behaviour
181+
// so we use our SkippableCipherInputStream
182+
new SkippableCipherInputStream(
201183
new SkippingInputStream(
202184
delegate.getContentInputStream(),
203185
byteStartOffset

0 commit comments

Comments
 (0)