You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@wicket.apache.org by sv...@apache.org on 2018/10/30 12:59:31 UTC

[52/52] [abbrv] wicket git commit: WICKET-6559 new encrypting page store

WICKET-6559 new encrypting page store


Project: http://git-wip-us.apache.org/repos/asf/wicket/repo
Commit: http://git-wip-us.apache.org/repos/asf/wicket/commit/bf72e279
Tree: http://git-wip-us.apache.org/repos/asf/wicket/tree/bf72e279
Diff: http://git-wip-us.apache.org/repos/asf/wicket/diff/bf72e279

Branch: refs/heads/WICKET-6563
Commit: bf72e27980070572b57c8ad0130c83a7d024aa0f
Parents: c43d3a3
Author: Sven Meier <sv...@apache.org>
Authored: Tue Oct 30 09:13:41 2018 +0100
Committer: Sven Meier <sv...@apache.org>
Committed: Tue Oct 30 13:56:24 2018 +0100

----------------------------------------------------------------------
 .../wicket/DefaultPageManagerProvider.java      |  11 +-
 .../wicket/pageStore/CryptingPageStore.java     | 146 +++++++++++++++++++
 .../wicket/pageStore/crypt/DefaultCrypter.java  | 108 ++++++++++++++
 .../apache/wicket/pageStore/crypt/ICrypter.java |  28 ++++
 .../apache/wicket/settings/StoreSettings.java   |  17 ++-
 .../wicket/pageStore/CryptingPageStoreTest.java |  87 +++++++++++
 6 files changed, 393 insertions(+), 4 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/wicket/blob/bf72e279/wicket-core/src/main/java/org/apache/wicket/DefaultPageManagerProvider.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/main/java/org/apache/wicket/DefaultPageManagerProvider.java b/wicket-core/src/main/java/org/apache/wicket/DefaultPageManagerProvider.java
index cf4f84a..36c1fac 100644
--- a/wicket-core/src/main/java/org/apache/wicket/DefaultPageManagerProvider.java
+++ b/wicket-core/src/main/java/org/apache/wicket/DefaultPageManagerProvider.java
@@ -21,6 +21,7 @@ import java.io.File;
 import org.apache.wicket.page.IPageManager;
 import org.apache.wicket.page.PageManager;
 import org.apache.wicket.pageStore.AsynchronousPageStore;
+import org.apache.wicket.pageStore.CryptingPageStore;
 import org.apache.wicket.pageStore.DiskPageStore;
 import org.apache.wicket.pageStore.FilePageStore;
 import org.apache.wicket.pageStore.GroupingPageStore;
@@ -41,8 +42,8 @@ import org.apache.wicket.util.lang.Bytes;
  * <ol>
  * <li>{@link RequestPageStore} caching pages until end of the request</li>
  * <li>{@link InSessionPageStore} keeping the last accessed page in the session</li>
- * <li>{@link AsynchronousPageStore} moving storage of pages to a worker thread (if enabled in
- * {@link StoreSettings#isAsynchronous()})</li>
+ * <li>{@link AsynchronousPageStore} moving storage of pages to a worker thread (if enabled in {@link StoreSettings#isAsynchronous()})</li>
+ * <li>{@link CryptingPageStore} encrypting all pages (if enabled in {@link StoreSettings#isEncrypted()})</li>
  * <li>{@link DiskPageStore} keeping all pages, configured according to {@link StoreSettings}</li>
  * </ol>
  * An alternative chain with all pages held in-memory could be:
@@ -166,6 +167,10 @@ public class DefaultPageManagerProvider implements IPageManagerProvider
 		Bytes maxSizePerSession = storeSettings.getMaxSizePerSession();
 		File fileStoreFolder = storeSettings.getFileStoreFolder();
 
-		return new DiskPageStore(application.getName(), fileStoreFolder, maxSizePerSession, getSerializer());
+		if (storeSettings.isEncrypted()) {
+			return new SerializingPageStore(new CryptingPageStore(new DiskPageStore(application.getName(), fileStoreFolder, maxSizePerSession)), getSerializer());
+		} else {
+			return new DiskPageStore(application.getName(), fileStoreFolder, maxSizePerSession, getSerializer());
+		}
 	}
 }

http://git-wip-us.apache.org/repos/asf/wicket/blob/bf72e279/wicket-core/src/main/java/org/apache/wicket/pageStore/CryptingPageStore.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/main/java/org/apache/wicket/pageStore/CryptingPageStore.java b/wicket-core/src/main/java/org/apache/wicket/pageStore/CryptingPageStore.java
new file mode 100644
index 0000000..2b27e80
--- /dev/null
+++ b/wicket-core/src/main/java/org/apache/wicket/pageStore/CryptingPageStore.java
@@ -0,0 +1,146 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.wicket.pageStore;
+
+import java.io.Serializable;
+
+import org.apache.wicket.MetaDataKey;
+import org.apache.wicket.WicketRuntimeException;
+import org.apache.wicket.page.IManageablePage;
+import org.apache.wicket.pageStore.crypt.DefaultCrypter;
+import org.apache.wicket.pageStore.crypt.ICrypter;
+import org.apache.wicket.serialize.ISerializer;
+
+/**
+ * A store that encrypts all pages before delegating and vice versa.
+ * <p>
+ * All pages passing through this store are restricted to be {@link SerializedPage}s. You can
+ * achieve this with
+ * <ul>
+ * <li>a {@link SerializingPageStore} delegating to this store and</li>
+ * <li>delegating to a store that does not deserialize its pages, e.g. a {@link DiskPageStore}
+ * without {@link ISerializer}</li>.
+ * </ul>
+ */
+public class CryptingPageStore extends DelegatingPageStore
+{
+	private static final MetaDataKey<SessionData> KEY = new MetaDataKey<SessionData>()
+	{
+	};
+
+	/**
+	 * @param delegate
+	 *            store to delegate to
+	 * @param applicationName
+	 *            name of application
+	 */
+	public CryptingPageStore(IPageStore delegate)
+	{
+		super(delegate);
+	}
+
+	/**
+	 * Supports asynchronous add if the delegate supports it.
+	 */
+	@Override
+	public boolean canBeAsynchronous(IPageContext context)
+	{
+		context.bind();
+
+		getSessionData(context);
+
+		return getDelegate().canBeAsynchronous(context);
+	}
+
+	private SessionData getSessionData(IPageContext context)
+	{
+		SessionData data = context.getSessionData(KEY);
+		if (data == null)
+		{
+			data = context.setSessionData(KEY, new SessionData(newCrypter(context)));
+		}
+		return data;
+	}
+
+	/**
+	 * Create a new {@link ICrypter} for the given context.
+	 */
+	protected ICrypter newCrypter(IPageContext context) {
+		return new DefaultCrypter();
+	}
+
+	@Override
+	public IManageablePage getPage(IPageContext context, int id)
+	{
+		IManageablePage page = super.getPage(context, id);
+
+		if (page != null)
+		{
+			if (page instanceof SerializedPage == false)
+			{
+				throw new WicketRuntimeException("CryptingPageStore expects serialized pages");
+			}
+			SerializedPage serializedPage = (SerializedPage)page;
+
+			byte[] encrypted = serializedPage.getData();
+			byte[] decrypted = getSessionData(context).decrypt(encrypted);
+
+			page = new SerializedPage(page.getPageId(), serializedPage.getPageType(), decrypted);
+		}
+
+		return page;
+	}
+
+	@Override
+	public void addPage(IPageContext context, IManageablePage page)
+	{
+		if (page instanceof SerializedPage == false)
+		{
+			throw new WicketRuntimeException("CryptingPageStore works with serialized pages only");
+		}
+
+		SerializedPage serializedPage = (SerializedPage)page;
+
+		byte[] decrypted = serializedPage.getData();
+		byte[] encrypted = getSessionData(context).encrypt(decrypted);
+
+		page = new SerializedPage(page.getPageId(), serializedPage.getPageType(), encrypted);
+
+		super.addPage(context, page);
+	}
+
+	private static class SessionData implements Serializable
+	{
+
+		private ICrypter cypter;
+
+		public SessionData(ICrypter crypter)
+		{
+			this.cypter= crypter;
+		}
+
+		public byte[] encrypt(byte[] decrypted)
+		{
+			return cypter.encrypt(decrypted);
+		}
+
+		public byte[] decrypt(byte[] encrypted)
+		{
+			return cypter.decrypt(encrypted);
+		}
+	}
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/wicket/blob/bf72e279/wicket-core/src/main/java/org/apache/wicket/pageStore/crypt/DefaultCrypter.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/main/java/org/apache/wicket/pageStore/crypt/DefaultCrypter.java b/wicket-core/src/main/java/org/apache/wicket/pageStore/crypt/DefaultCrypter.java
new file mode 100644
index 0000000..53c63be
--- /dev/null
+++ b/wicket-core/src/main/java/org/apache/wicket/pageStore/crypt/DefaultCrypter.java
@@ -0,0 +1,108 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.wicket.pageStore.crypt;
+
+import java.security.AlgorithmParameters;
+import java.security.GeneralSecurityException;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.util.Arrays;
+
+import javax.crypto.Cipher;
+import javax.crypto.KeyGenerator;
+import javax.crypto.NoSuchPaddingException;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.IvParameterSpec;
+
+import org.apache.wicket.WicketRuntimeException;
+
+/**
+ * Default encryption and decryption implementation. 
+ */
+public class DefaultCrypter implements ICrypter
+{
+	private final SecureRandom random;
+
+	private final SecretKey key;
+
+	public DefaultCrypter()
+	{
+		try
+		{
+			random = SecureRandom.getInstance("SHA1PRNG", "SUN");
+
+			KeyGenerator generator = KeyGenerator.getInstance("AES");
+			generator.init(256, random);
+			key = generator.generateKey();
+		}
+		catch (GeneralSecurityException ex)
+		{
+			throw new WicketRuntimeException(ex);
+		}
+	}
+
+	protected Cipher getCipher() throws GeneralSecurityException
+	{
+		return Cipher.getInstance("AES/CBC/PKCS5Padding");
+	}
+
+	@Override
+	public byte[] encrypt(byte[] decrypted)
+	{
+		try
+		{
+			Cipher cipher = getCipher();
+			cipher.init(Cipher.ENCRYPT_MODE, key, random);
+
+			AlgorithmParameters params = cipher.getParameters();
+			byte[] iv = params.getParameterSpec(IvParameterSpec.class).getIV();
+
+			byte[] ciphertext = cipher.doFinal(decrypted);
+			
+			byte[] encrypted = Arrays.copyOf(iv, iv.length + ciphertext.length);
+			System.arraycopy(ciphertext, 0, encrypted, iv.length, ciphertext.length);
+			
+			return encrypted;
+		}
+		catch (GeneralSecurityException ex)
+		{
+			throw new WicketRuntimeException(ex);
+		}
+	}
+
+	@Override
+	public byte[] decrypt(byte[] encrypted)
+	{
+		try
+		{
+			byte[] iv = new byte[16];
+			byte[] ciphertext = new byte[encrypted.length - 16];
+			System.arraycopy(encrypted, 0, iv, 0, iv.length);
+			System.arraycopy(encrypted, 16, ciphertext, 0, ciphertext.length);
+	
+			Cipher cipher = getCipher();
+			cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(iv));
+			byte[] decrypted = cipher.doFinal(ciphertext);
+			
+			return decrypted;
+		}
+		catch (GeneralSecurityException ex)
+		{
+			throw new WicketRuntimeException(ex);
+		}
+	}
+}

http://git-wip-us.apache.org/repos/asf/wicket/blob/bf72e279/wicket-core/src/main/java/org/apache/wicket/pageStore/crypt/ICrypter.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/main/java/org/apache/wicket/pageStore/crypt/ICrypter.java b/wicket-core/src/main/java/org/apache/wicket/pageStore/crypt/ICrypter.java
new file mode 100644
index 0000000..1cdbeaf
--- /dev/null
+++ b/wicket-core/src/main/java/org/apache/wicket/pageStore/crypt/ICrypter.java
@@ -0,0 +1,28 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.wicket.pageStore.crypt;
+
+import java.io.Serializable;
+
+/**
+ * An encrypter and decrypter of pages.
+ */
+public interface ICrypter extends Serializable {
+	byte[] encrypt(byte[] bytes);
+	
+	byte[] decrypt(byte[] bytes);
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/wicket/blob/bf72e279/wicket-core/src/main/java/org/apache/wicket/settings/StoreSettings.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/main/java/org/apache/wicket/settings/StoreSettings.java b/wicket-core/src/main/java/org/apache/wicket/settings/StoreSettings.java
index 7c32101..965258c 100644
--- a/wicket-core/src/main/java/org/apache/wicket/settings/StoreSettings.java
+++ b/wicket-core/src/main/java/org/apache/wicket/settings/StoreSettings.java
@@ -49,6 +49,8 @@ public class StoreSettings
 
 	private boolean asynchronous = true;
 	
+	private boolean encrypted = false;
+
 	/**
 	 * Construct.
 	 * 
@@ -179,4 +181,17 @@ public class StoreSettings
 	{
 		return asynchronous;
 	}
-}
\ No newline at end of file
+	
+	public void setEncrypted(boolean encrypted)
+	{
+		this.encrypted = encrypted;
+	}
+	
+	/**
+	 * @return {@code true} if the storing of page is encrypted
+	 */
+	public boolean isEncrypted()
+	{
+		return encrypted;
+	}
+}

http://git-wip-us.apache.org/repos/asf/wicket/blob/bf72e279/wicket-core/src/test/java/org/apache/wicket/pageStore/CryptingPageStoreTest.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/test/java/org/apache/wicket/pageStore/CryptingPageStoreTest.java b/wicket-core/src/test/java/org/apache/wicket/pageStore/CryptingPageStoreTest.java
new file mode 100644
index 0000000..a4b547e
--- /dev/null
+++ b/wicket-core/src/test/java/org/apache/wicket/pageStore/CryptingPageStoreTest.java
@@ -0,0 +1,87 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.wicket.pageStore;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.security.GeneralSecurityException;
+
+import org.apache.wicket.MockPage;
+import org.apache.wicket.WicketRuntimeException;
+import org.apache.wicket.mock.MockPageStore;
+import org.apache.wicket.serialize.java.JavaSerializer;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Test for {@link CryptingPageStore}.
+ * 
+ * @author svenmeier
+ */
+public class CryptingPageStoreTest
+{
+
+	@Test
+	void test()
+	{
+		CryptingPageStore store = new CryptingPageStore(new MockPageStore());
+		JavaSerializer serializer = new JavaSerializer("test");
+
+		IPageContext context = new DummyPageContext();
+
+		for (int p = 0; p < 10; p++)
+		{
+			MockPage add = new MockPage(p);
+			SerializedPage serializedAdd = new SerializedPage(p, "foo", serializer.serialize(add));
+			store.addPage(context, serializedAdd);
+
+			SerializedPage serializedGot = (SerializedPage)store.getPage(context, p);
+			MockPage got = (MockPage)serializer.deserialize(serializedGot.getData());
+			assertEquals(p, got.getPageId());
+		}
+	}
+
+	@Test
+	void testFail()
+	{
+		CryptingPageStore store = new CryptingPageStore(new MockPageStore());
+		JavaSerializer serializer = new JavaSerializer("test");
+
+		DummyPageContext context = new DummyPageContext();
+
+		int p = 42;
+
+		MockPage add = new MockPage(p);
+		SerializedPage serializedAdd = new SerializedPage(p, "foo", serializer.serialize(add));
+		store.addPage(context, serializedAdd);
+
+		// remove key from session
+		context.clearSession();
+
+		try
+		{
+			SerializedPage serializedGot = (SerializedPage)store.getPage(context, p);
+
+			MockPage got = (MockPage)serializer.deserialize(serializedGot.getData());
+			assertEquals(p, got.getPageId());
+		}
+		catch (WicketRuntimeException ex)
+		{
+			assertTrue("unable to decrypt with new key", ex.getCause() instanceof GeneralSecurityException);
+		}
+	}
+}