You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@wicket.apache.org by pa...@apache.org on 2020/01/16 20:51:25 UTC

[wicket] branch csp updated: WICKET-6730: central SecureRandom setting

This is an automated email from the ASF dual-hosted git repository.

papegaaij pushed a commit to branch csp
in repository https://gitbox.apache.org/repos/asf/wicket.git


The following commit(s) were added to refs/heads/csp by this push:
     new f5ff7da  WICKET-6730: central SecureRandom setting
     new 44c027f  Merge remote-tracking branch 'origin/csp' into csp
f5ff7da is described below

commit f5ff7dad9b5b62e27d2c382d0a79128822753e59
Author: Emond Papegaaij <em...@topicus.nl>
AuthorDate: Thu Jan 16 21:50:06 2020 +0100

    WICKET-6730: central SecureRandom setting
---
 .../apache/wicket/DefaultPageManagerProvider.java  |  2 +-
 .../core/random/DefaultSecureRandomSupplier.java   | 50 +++++++++++++++++
 .../wicket/core/random/ISecureRandomSupplier.java  | 62 ++++++++++++++++++++++
 .../apache/wicket/pageStore/CryptingPageStore.java | 23 ++++----
 .../apache/wicket/settings/SecuritySettings.java   | 36 +++++++++++++
 .../csp/CSPSettingRequestCycleListenerTest.java    | 23 ++++----
 .../wicket/pageStore/CryptingPageStoreTest.java    |  8 +--
 7 files changed, 172 insertions(+), 32 deletions(-)

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 1e8ec6f..e128266 100644
--- a/wicket-core/src/main/java/org/apache/wicket/DefaultPageManagerProvider.java
+++ b/wicket-core/src/main/java/org/apache/wicket/DefaultPageManagerProvider.java
@@ -183,7 +183,7 @@ public class DefaultPageManagerProvider implements IPageManagerProvider
 		
 		if (storeSettings.isEncrypted())
 		{
-			pageStore = new CryptingPageStore(pageStore);
+			pageStore = new CryptingPageStore(pageStore, application);
 		}
 
 		return pageStore;
diff --git a/wicket-core/src/main/java/org/apache/wicket/core/random/DefaultSecureRandomSupplier.java b/wicket-core/src/main/java/org/apache/wicket/core/random/DefaultSecureRandomSupplier.java
new file mode 100644
index 0000000..cb00235
--- /dev/null
+++ b/wicket-core/src/main/java/org/apache/wicket/core/random/DefaultSecureRandomSupplier.java
@@ -0,0 +1,50 @@
+/*
+ * 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.core.random;
+
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+
+import org.apache.wicket.WicketRuntimeException;
+
+/**
+ * A very simple {@link ISecureRandomSupplier} that holds a strong {@code SecureRandom}.
+ * 
+ * @author papegaaij
+ */
+public class DefaultSecureRandomSupplier implements ISecureRandomSupplier
+{
+	private SecureRandom random;
+
+	public DefaultSecureRandomSupplier()
+	{
+		try
+		{
+			random = SecureRandom.getInstanceStrong();
+		}
+		catch (NoSuchAlgorithmException e)
+		{
+			throw new WicketRuntimeException(e);
+		}
+	}
+
+	@Override
+	public SecureRandom getRandom()
+	{
+		return random;
+	}
+}
diff --git a/wicket-core/src/main/java/org/apache/wicket/core/random/ISecureRandomSupplier.java b/wicket-core/src/main/java/org/apache/wicket/core/random/ISecureRandomSupplier.java
new file mode 100644
index 0000000..9b9fe14
--- /dev/null
+++ b/wicket-core/src/main/java/org/apache/wicket/core/random/ISecureRandomSupplier.java
@@ -0,0 +1,62 @@
+/*
+ * 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.core.random;
+
+import java.security.SecureRandom;
+import java.util.Base64;
+
+/**
+ * Supplies the Wicket application with random bytes.
+ * 
+ * @author papegaaij
+ */
+public interface ISecureRandomSupplier
+{
+	/**
+	 * Returns the actual {@code SecureRandom} being used as source.
+	 * 
+	 * @return The {@code SecureRandom}.
+	 */
+	public SecureRandom getRandom();
+
+	/**
+	 * Returns a byte array with random bytes of the given length.
+	 * 
+	 * @param length
+	 *            The number of bytes to return.
+	 * @return A byte array with random bytes of the given length.
+	 */
+	public default byte[] getRandomBytes(int length)
+	{
+		byte[] ret = new byte[length];
+		getRandom().nextBytes(ret);
+		return ret;
+	}
+	
+	/**
+	 * Returns a base64 encoded string with random content, base on {@code length} bytes. The length
+	 * of the returned string will be {@code length/3*4}.
+	 * 
+	 * @param length
+	 *            The number of random bytes to use as input.
+	 * @return A string with random base64 data.
+	 */
+	public default String getRandomBase64(int length)
+	{
+		return Base64.getUrlEncoder().encodeToString(getRandomBytes(length));
+	}
+}
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
index 91ac373..dc0d6eb 100644
--- a/wicket-core/src/main/java/org/apache/wicket/pageStore/CryptingPageStore.java
+++ b/wicket-core/src/main/java/org/apache/wicket/pageStore/CryptingPageStore.java
@@ -17,11 +17,11 @@
 package org.apache.wicket.pageStore;
 
 import java.io.Serializable;
-import java.security.GeneralSecurityException;
 import java.security.SecureRandom;
 
 import javax.crypto.SecretKey;
 
+import org.apache.wicket.Application;
 import org.apache.wicket.MetaDataKey;
 import org.apache.wicket.WicketRuntimeException;
 import org.apache.wicket.page.IManageablePage;
@@ -46,27 +46,20 @@ public class CryptingPageStore extends DelegatingPageStore
 		private static final long serialVersionUID = 1L;
 	};
 
-	private final SecureRandom random;
-
 	private final ICrypter crypter;
 
+	private final Application application;
+
 	/**
 	 * @param delegate
 	 *            store to delegate to
 	 * @param applicationName
 	 *            name of application
 	 */
-	public CryptingPageStore(IPageStore delegate)
+	public CryptingPageStore(IPageStore delegate, Application application)
 	{
 		super(delegate);
-		try
-		{
-			random = SecureRandom.getInstance("SHA1PRNG", "SUN");
-		}
-		catch (GeneralSecurityException ex)
-		{
-			throw new WicketRuntimeException(ex);
-		}
+		this.application = Args.notNull(application, "application");
 		crypter = newCrypter();
 	}
 
@@ -94,7 +87,8 @@ public class CryptingPageStore extends DelegatingPageStore
 
 	private SessionData getSessionData(IPageContext context)
 	{
-		return context.getSessionData(KEY, () -> new SessionData(crypter.generateKey(random)));
+		return context.getSessionData(KEY, () -> new SessionData(crypter
+			.generateKey(application.getSecuritySettings().getRandomSupplier().getRandom())));
 	}
 
 	/**
@@ -138,7 +132,8 @@ public class CryptingPageStore extends DelegatingPageStore
 		SerializedPage serializedPage = (SerializedPage) page;
 
 		byte[] decrypted = serializedPage.getData();
-		byte[] encrypted = getSessionData(context).encrypt(decrypted, crypter, random);
+		byte[] encrypted = getSessionData(context).encrypt(decrypted, crypter,
+			application.getSecuritySettings().getRandomSupplier().getRandom());
 
 		page = new SerializedPage(page.getPageId(), serializedPage.getPageType(), encrypted);
 
diff --git a/wicket-core/src/main/java/org/apache/wicket/settings/SecuritySettings.java b/wicket-core/src/main/java/org/apache/wicket/settings/SecuritySettings.java
index 94eadea..94e2390 100644
--- a/wicket-core/src/main/java/org/apache/wicket/settings/SecuritySettings.java
+++ b/wicket-core/src/main/java/org/apache/wicket/settings/SecuritySettings.java
@@ -24,6 +24,8 @@ import org.apache.wicket.authorization.IAuthorizationStrategy;
 import org.apache.wicket.authorization.IUnauthorizedComponentInstantiationListener;
 import org.apache.wicket.authorization.IUnauthorizedResourceRequestListener;
 import org.apache.wicket.authorization.UnauthorizedInstantiationException;
+import org.apache.wicket.core.random.DefaultSecureRandomSupplier;
+import org.apache.wicket.core.random.ISecureRandomSupplier;
 import org.apache.wicket.core.util.crypt.KeyInSessionSunJceCryptFactory;
 import org.apache.wicket.util.crypt.ICryptFactory;
 import org.apache.wicket.util.lang.Args;
@@ -55,6 +57,9 @@ public class SecuritySettings
 
 	/** factory for creating crypt objects */
 	private ICryptFactory cryptFactory;
+	
+	/** supplier of random data and SecureRandom */
+	private ISecureRandomSupplier randomSupplier;
 
 	/**
 	 * Whether mounts should be enforced. If {@code true}, requests for a page will be
@@ -115,6 +120,21 @@ public class SecuritySettings
 	}
 
 	/**
+	 * Returns the {@link ISecureRandomSupplier} to use for secure random data. If no supplier is
+	 * set, a {@link DefaultSecureRandomSupplier} is instantiated.
+	 * 
+	 * @return The {@link ISecureRandomSupplier} to use for secure random data.
+	 */
+	public ISecureRandomSupplier getRandomSupplier()
+	{
+		if (randomSupplier == null)
+		{
+			randomSupplier = new DefaultSecureRandomSupplier();
+		}
+		return randomSupplier;
+	}
+
+	/**
 	 * Gets whether page mounts should be enforced. If {@code true}, requests for a page will be
 	 * allowed only if the page has been explicitly mounted in {@link Application#init() MyApplication#init()}.
 	 *
@@ -163,6 +183,22 @@ public class SecuritySettings
 		this.cryptFactory = cryptFactory;
 		return this;
 	}
+	
+	/**
+	 * Sets the supplier of secure random data for Wicket. The implementation must use a strong
+	 * source of random data and be able to generate a lot of random data without running out of
+	 * entropy.
+	 * 
+	 * @param randomSupplier
+	 *            The new supplier, must not be null.
+	 * @return {@code this} object for chaining
+	 */
+	public SecuritySettings setRandomSupplier(ISecureRandomSupplier randomSupplier)
+	{
+		Args.notNull(randomSupplier, "randomSupplier");
+		this.randomSupplier = randomSupplier;
+		return this;
+	}
 
 	/**
 	 * Sets whether mounts should be enforced. If true, requests for mounted targets have to done
diff --git a/wicket-core/src/test/java/org/apache/wicket/csp/CSPSettingRequestCycleListenerTest.java b/wicket-core/src/test/java/org/apache/wicket/csp/CSPSettingRequestCycleListenerTest.java
index 14ba223..cdb1bc6 100644
--- a/wicket-core/src/test/java/org/apache/wicket/csp/CSPSettingRequestCycleListenerTest.java
+++ b/wicket-core/src/test/java/org/apache/wicket/csp/CSPSettingRequestCycleListenerTest.java
@@ -16,16 +16,16 @@
  */
 package org.apache.wicket.csp;
 
-import static org.apache.wicket.csp.CSPSettingRequestCycleListener.CSPDirective.CHILD_SRC;
-import static org.apache.wicket.csp.CSPSettingRequestCycleListener.CSPDirective.DEFAULT_SRC;
-import static org.apache.wicket.csp.CSPSettingRequestCycleListener.CSPDirective.FRAME_SRC;
-import static org.apache.wicket.csp.CSPSettingRequestCycleListener.CSPDirective.REPORT_URI;
-import static org.apache.wicket.csp.CSPSettingRequestCycleListener.CSPDirective.SANDBOX;
-import static org.apache.wicket.csp.CSPSettingRequestCycleListener.CSPDirectiveSandboxValue.ALLOW_FORMS;
-import static org.apache.wicket.csp.CSPSettingRequestCycleListener.CSPDirectiveSandboxValue.EMPTY;
-import static org.apache.wicket.csp.CSPSettingRequestCycleListener.CSPDirectiveSrcValue.NONE;
-import static org.apache.wicket.csp.CSPSettingRequestCycleListener.CSPDirectiveSrcValue.SELF;
-import static org.apache.wicket.csp.CSPSettingRequestCycleListener.CSPDirectiveSrcValue.WILDCARD;
+import static org.apache.wicket.csp.CSPDirective.CHILD_SRC;
+import static org.apache.wicket.csp.CSPDirective.DEFAULT_SRC;
+import static org.apache.wicket.csp.CSPDirective.FRAME_SRC;
+import static org.apache.wicket.csp.CSPDirective.REPORT_URI;
+import static org.apache.wicket.csp.CSPDirective.SANDBOX;
+import static org.apache.wicket.csp.CSPDirectiveSandboxValue.ALLOW_FORMS;
+import static org.apache.wicket.csp.CSPDirectiveSandboxValue.EMPTY;
+import static org.apache.wicket.csp.CSPDirectiveSrcValue.NONE;
+import static org.apache.wicket.csp.CSPDirectiveSrcValue.SELF;
+import static org.apache.wicket.csp.CSPDirectiveSrcValue.WILDCARD;
 
 import java.net.URI;
 import java.net.URISyntaxException;
@@ -37,9 +37,6 @@ import java.util.Set;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
-import org.apache.wicket.csp.CSPSettingRequestCycleListener.CSPDirective;
-import org.apache.wicket.csp.CSPSettingRequestCycleListener.CSPDirectiveSandboxValue;
-import org.apache.wicket.csp.CSPSettingRequestCycleListener.CSPDirectiveSrcValue;
 import org.apache.wicket.mock.MockHomePage;
 import org.apache.wicket.util.tester.WicketTester;
 import org.junit.jupiter.api.Assertions;
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
index 755a000..48e3def 100644
--- a/wicket-core/src/test/java/org/apache/wicket/pageStore/CryptingPageStoreTest.java
+++ b/wicket-core/src/test/java/org/apache/wicket/pageStore/CryptingPageStoreTest.java
@@ -23,10 +23,10 @@ import java.io.StreamCorruptedException;
 import java.security.GeneralSecurityException;
 
 import org.apache.wicket.MockPage;
-import org.apache.wicket.WicketRuntimeException;
 import org.apache.wicket.mock.MockPageContext;
 import org.apache.wicket.mock.MockPageStore;
 import org.apache.wicket.serialize.java.JavaSerializer;
+import org.apache.wicket.util.tester.WicketTester;
 import org.junit.jupiter.api.Test;
 
 /**
@@ -34,13 +34,13 @@ import org.junit.jupiter.api.Test;
  * 
  * @author svenmeier
  */
-public class CryptingPageStoreTest
+public class CryptingPageStoreTest extends WicketTester
 {
 
 	@Test
 	void test()
 	{
-		CryptingPageStore store = new CryptingPageStore(new MockPageStore());
+		CryptingPageStore store = new CryptingPageStore(new MockPageStore(), getApplication());
 		JavaSerializer serializer = new JavaSerializer("test");
 
 		IPageContext context = new MockPageContext();
@@ -60,7 +60,7 @@ public class CryptingPageStoreTest
 	@Test
 	void testFail()
 	{
-		CryptingPageStore store = new CryptingPageStore(new MockPageStore());
+		CryptingPageStore store = new CryptingPageStore(new MockPageStore(), getApplication());
 		JavaSerializer serializer = new JavaSerializer("test");
 
 		MockPageContext context = new MockPageContext();


Re: [wicket] branch csp updated: WICKET-6730: central SecureRandom setting

Posted by Martin Grigorov <mg...@apache.org>.
On Fri, Jan 17, 2020 at 10:57 AM Emond Papegaaij <em...@gmail.com>
wrote:

> > > +                       random = SecureRandom.getInstanceStrong();
> > >
> >
> > Since we have now CryptingPageStore it is possible that an application
> > store some pages with algo X.
> > If the application is clustered and rolling upgrade is done with a new
> > version of JDK it is possible that the new best strong algo is now Y.
> > In this case the application won't be able to decrypt the old pages.
>
> The implementation of the random source does not matter for encryption
> and decryption of pages. The only thing that matters is that the
> random source provides secure random data for the keys.
>
> > > +public interface ISecureRandomSupplier
> >
> > nit: no need of `public` in interfaces
>
> I know, but I don't like it when an interface is not public. I never
> understood why the public keyword is optional for interfaces anyway.
> All interfaces in the JDK are public as well.
>

I meant the (abstract) methods in the interfaces. They are public implicitly


>
> I've pushed a commit with the other improvements. I don't know why I
> used WicketTester as superclass, it did work :)
>

Magic! :-)

Thank you, Emond!


>
> Best regards,
> Emond
>

Re: [wicket] branch csp updated: WICKET-6730: central SecureRandom setting

Posted by Emond Papegaaij <em...@gmail.com>.
> > +                       random = SecureRandom.getInstanceStrong();
> >
>
> Since we have now CryptingPageStore it is possible that an application
> store some pages with algo X.
> If the application is clustered and rolling upgrade is done with a new
> version of JDK it is possible that the new best strong algo is now Y.
> In this case the application won't be able to decrypt the old pages.

The implementation of the random source does not matter for encryption
and decryption of pages. The only thing that matters is that the
random source provides secure random data for the keys.

> > +public interface ISecureRandomSupplier
>
> nit: no need of `public` in interfaces

I know, but I don't like it when an interface is not public. I never
understood why the public keyword is optional for interfaces anyway.
All interfaces in the JDK are public as well.

I've pushed a commit with the other improvements. I don't know why I
used WicketTester as superclass, it did work :)

Best regards,
Emond

Re: [wicket] branch csp updated: WICKET-6730: central SecureRandom setting

Posted by Martin Grigorov <mg...@apache.org>.
On Thu, Jan 16, 2020 at 10:51 PM <pa...@apache.org> wrote:

> This is an automated email from the ASF dual-hosted git repository.
>
> papegaaij pushed a commit to branch csp
> in repository https://gitbox.apache.org/repos/asf/wicket.git
>
>
> The following commit(s) were added to refs/heads/csp by this push:
>      new f5ff7da  WICKET-6730: central SecureRandom setting
>      new 44c027f  Merge remote-tracking branch 'origin/csp' into csp
> f5ff7da is described below
>
> commit f5ff7dad9b5b62e27d2c382d0a79128822753e59
> Author: Emond Papegaaij <em...@topicus.nl>
> AuthorDate: Thu Jan 16 21:50:06 2020 +0100
>
>     WICKET-6730: central SecureRandom setting
> ---
>  .../apache/wicket/DefaultPageManagerProvider.java  |  2 +-
>  .../core/random/DefaultSecureRandomSupplier.java   | 50 +++++++++++++++++
>  .../wicket/core/random/ISecureRandomSupplier.java  | 62
> ++++++++++++++++++++++
>  .../apache/wicket/pageStore/CryptingPageStore.java | 23 ++++----
>  .../apache/wicket/settings/SecuritySettings.java   | 36 +++++++++++++
>  .../csp/CSPSettingRequestCycleListenerTest.java    | 23 ++++----
>  .../wicket/pageStore/CryptingPageStoreTest.java    |  8 +--
>  7 files changed, 172 insertions(+), 32 deletions(-)
>
> 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 1e8ec6f..e128266 100644
> ---
> a/wicket-core/src/main/java/org/apache/wicket/DefaultPageManagerProvider.java
> +++
> b/wicket-core/src/main/java/org/apache/wicket/DefaultPageManagerProvider.java
> @@ -183,7 +183,7 @@ public class DefaultPageManagerProvider implements
> IPageManagerProvider
>
>                 if (storeSettings.isEncrypted())
>                 {
> -                       pageStore = new CryptingPageStore(pageStore);
> +                       pageStore = new CryptingPageStore(pageStore,
> application);
>                 }
>
>                 return pageStore;
> diff --git
> a/wicket-core/src/main/java/org/apache/wicket/core/random/DefaultSecureRandomSupplier.java
> b/wicket-core/src/main/java/org/apache/wicket/core/random/DefaultSecureRandomSupplier.java
> new file mode 100644
> index 0000000..cb00235
> --- /dev/null
> +++
> b/wicket-core/src/main/java/org/apache/wicket/core/random/DefaultSecureRandomSupplier.java
> @@ -0,0 +1,50 @@
> +/*
> + * 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.core.random;
> +
> +import java.security.NoSuchAlgorithmException;
> +import java.security.SecureRandom;
> +
> +import org.apache.wicket.WicketRuntimeException;
> +
> +/**
> + * A very simple {@link ISecureRandomSupplier} that holds a strong {@code
> SecureRandom}.
> + *
> + * @author papegaaij
> + */
> +public class DefaultSecureRandomSupplier implements ISecureRandomSupplier
> +{
> +       private SecureRandom random;
>

final


> +
> +       public DefaultSecureRandomSupplier()
> +       {
> +               try
> +               {
> +                       random = SecureRandom.getInstanceStrong();
>

Since we have now CryptingPageStore it is possible that an application
store some pages with algo X.
If the application is clustered and rolling upgrade is done with a new
version of JDK it is possible that the new best strong algo is now Y.
In this case the application won't be able to decrypt the old pages.


> +               }
> +               catch (NoSuchAlgorithmException e)
> +               {
> +                       throw new WicketRuntimeException(e);
> +               }
> +       }
> +
> +       @Override
> +       public SecureRandom getRandom()
> +       {
> +               return random;
> +       }
> +}
> diff --git
> a/wicket-core/src/main/java/org/apache/wicket/core/random/ISecureRandomSupplier.java
> b/wicket-core/src/main/java/org/apache/wicket/core/random/ISecureRandomSupplier.java
> new file mode 100644
> index 0000000..9b9fe14
> --- /dev/null
> +++
> b/wicket-core/src/main/java/org/apache/wicket/core/random/ISecureRandomSupplier.java
> @@ -0,0 +1,62 @@
> +/*
> + * 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.core.random;
> +
> +import java.security.SecureRandom;
> +import java.util.Base64;
> +
> +/**
> + * Supplies the Wicket application with random bytes.
> + *
> + * @author papegaaij
> + */
> +public interface ISecureRandomSupplier
> +{
> +       /**
> +        * Returns the actual {@code SecureRandom} being used as source.
> +        *
> +        * @return The {@code SecureRandom}.
> +        */
> +       public SecureRandom getRandom();
>

nit: no need of `public` in interfaces


> +
> +       /**
> +        * Returns a byte array with random bytes of the given length.
> +        *
> +        * @param length
> +        *            The number of bytes to return.
> +        * @return A byte array with random bytes of the given length.
> +        */
> +       public default byte[] getRandomBytes(int length)
> +       {
> +               byte[] ret = new byte[length];
> +               getRandom().nextBytes(ret);
> +               return ret;
> +       }
> +
> +       /**
> +        * Returns a base64 encoded string with random content, base on
> {@code length} bytes. The length
> +        * of the returned string will be {@code length/3*4}.
>

The javadoc should be improved to say that this is about *url* encoding.


> +        *
> +        * @param length
> +        *            The number of random bytes to use as input.
> +        * @return A string with random base64 data.
> +        */
> +       public default String getRandomBase64(int length)
> +       {
> +               return
> Base64.getUrlEncoder().encodeToString(getRandomBytes(length));
> +       }
> +}
> 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
> index 91ac373..dc0d6eb 100644
> ---
> a/wicket-core/src/main/java/org/apache/wicket/pageStore/CryptingPageStore.java
> +++
> b/wicket-core/src/main/java/org/apache/wicket/pageStore/CryptingPageStore.java
> @@ -17,11 +17,11 @@
>  package org.apache.wicket.pageStore;
>
>  import java.io.Serializable;
> -import java.security.GeneralSecurityException;
>  import java.security.SecureRandom;
>
>  import javax.crypto.SecretKey;
>
> +import org.apache.wicket.Application;
>  import org.apache.wicket.MetaDataKey;
>  import org.apache.wicket.WicketRuntimeException;
>  import org.apache.wicket.page.IManageablePage;
> @@ -46,27 +46,20 @@ public class CryptingPageStore extends
> DelegatingPageStore
>                 private static final long serialVersionUID = 1L;
>         };
>
> -       private final SecureRandom random;
> -
>         private final ICrypter crypter;
>
> +       private final Application application;
> +
>         /**
>          * @param delegate
>          *            store to delegate to
>          * @param applicationName
>          *            name of application
>          */
> -       public CryptingPageStore(IPageStore delegate)
> +       public CryptingPageStore(IPageStore delegate, Application
> application)
>         {
>                 super(delegate);
> -               try
> -               {
> -                       random = SecureRandom.getInstance("SHA1PRNG",
> "SUN");
> -               }
> -               catch (GeneralSecurityException ex)
> -               {
> -                       throw new WicketRuntimeException(ex);
> -               }
> +               this.application = Args.notNull(application,
> "application");
>                 crypter = newCrypter();
>         }
>
> @@ -94,7 +87,8 @@ public class CryptingPageStore extends
> DelegatingPageStore
>
>         private SessionData getSessionData(IPageContext context)
>         {
> -               return context.getSessionData(KEY, () -> new
> SessionData(crypter.generateKey(random)));
> +               return context.getSessionData(KEY, () -> new
> SessionData(crypter
> +
>  .generateKey(application.getSecuritySettings().getRandomSupplier().getRandom())));
>         }
>
>         /**
> @@ -138,7 +132,8 @@ public class CryptingPageStore extends
> DelegatingPageStore
>                 SerializedPage serializedPage = (SerializedPage) page;
>
>                 byte[] decrypted = serializedPage.getData();
> -               byte[] encrypted =
> getSessionData(context).encrypt(decrypted, crypter, random);
> +               byte[] encrypted =
> getSessionData(context).encrypt(decrypted, crypter,
> +
>  application.getSecuritySettings().getRandomSupplier().getRandom());
>
>                 page = new SerializedPage(page.getPageId(),
> serializedPage.getPageType(), encrypted);
>
> diff --git
> a/wicket-core/src/main/java/org/apache/wicket/settings/SecuritySettings.java
> b/wicket-core/src/main/java/org/apache/wicket/settings/SecuritySettings.java
> index 94eadea..94e2390 100644
> ---
> a/wicket-core/src/main/java/org/apache/wicket/settings/SecuritySettings.java
> +++
> b/wicket-core/src/main/java/org/apache/wicket/settings/SecuritySettings.java
> @@ -24,6 +24,8 @@ import
> org.apache.wicket.authorization.IAuthorizationStrategy;
>  import
> org.apache.wicket.authorization.IUnauthorizedComponentInstantiationListener;
>  import
> org.apache.wicket.authorization.IUnauthorizedResourceRequestListener;
>  import org.apache.wicket.authorization.UnauthorizedInstantiationException;
> +import org.apache.wicket.core.random.DefaultSecureRandomSupplier;
> +import org.apache.wicket.core.random.ISecureRandomSupplier;
>  import org.apache.wicket.core.util.crypt.KeyInSessionSunJceCryptFactory;
>  import org.apache.wicket.util.crypt.ICryptFactory;
>  import org.apache.wicket.util.lang.Args;
> @@ -55,6 +57,9 @@ public class SecuritySettings
>
>         /** factory for creating crypt objects */
>         private ICryptFactory cryptFactory;
> +
> +       /** supplier of random data and SecureRandom */
> +       private ISecureRandomSupplier randomSupplier;
>
>         /**
>          * Whether mounts should be enforced. If {@code true}, requests
> for a page will be
> @@ -115,6 +120,21 @@ public class SecuritySettings
>         }
>
>         /**
> +        * Returns the {@link ISecureRandomSupplier} to use for secure
> random data. If no supplier is
> +        * set, a {@link DefaultSecureRandomSupplier} is instantiated.
> +        *
> +        * @return The {@link ISecureRandomSupplier} to use for secure
> random data.
> +        */
> +       public ISecureRandomSupplier getRandomSupplier()
>

synchronized ?


> +       {
> +               if (randomSupplier == null)
> +               {
> +                       randomSupplier = new DefaultSecureRandomSupplier();
> +               }
> +               return randomSupplier;
> +       }
> +
> +       /**
>          * Gets whether page mounts should be enforced. If {@code true},
> requests for a page will be
>          * allowed only if the page has been explicitly mounted in {@link
> Application#init() MyApplication#init()}.
>          *
> @@ -163,6 +183,22 @@ public class SecuritySettings
>                 this.cryptFactory = cryptFactory;
>                 return this;
>         }
> +
> +       /**
> +        * Sets the supplier of secure random data for Wicket. The
> implementation must use a strong
> +        * source of random data and be able to generate a lot of random
> data without running out of
> +        * entropy.
> +        *
> +        * @param randomSupplier
> +        *            The new supplier, must not be null.
> +        * @return {@code this} object for chaining
> +        */
> +       public SecuritySettings setRandomSupplier(ISecureRandomSupplier
> randomSupplier)
>

synchronized ?


> +       {
> +               Args.notNull(randomSupplier, "randomSupplier");
> +               this.randomSupplier = randomSupplier;
> +               return this;
> +       }
>
>         /**
>          * Sets whether mounts should be enforced. If true, requests for
> mounted targets have to done
> diff --git
> a/wicket-core/src/test/java/org/apache/wicket/csp/CSPSettingRequestCycleListenerTest.java
> b/wicket-core/src/test/java/org/apache/wicket/csp/CSPSettingRequestCycleListenerTest.java
> index 14ba223..cdb1bc6 100644
> ---
> a/wicket-core/src/test/java/org/apache/wicket/csp/CSPSettingRequestCycleListenerTest.java
> +++
> b/wicket-core/src/test/java/org/apache/wicket/csp/CSPSettingRequestCycleListenerTest.java
> @@ -16,16 +16,16 @@
>   */
>  package org.apache.wicket.csp;
>
> -import static
> org.apache.wicket.csp.CSPSettingRequestCycleListener.CSPDirective.CHILD_SRC;
> -import static
> org.apache.wicket.csp.CSPSettingRequestCycleListener.CSPDirective.DEFAULT_SRC;
> -import static
> org.apache.wicket.csp.CSPSettingRequestCycleListener.CSPDirective.FRAME_SRC;
> -import static
> org.apache.wicket.csp.CSPSettingRequestCycleListener.CSPDirective.REPORT_URI;
> -import static
> org.apache.wicket.csp.CSPSettingRequestCycleListener.CSPDirective.SANDBOX;
> -import static
> org.apache.wicket.csp.CSPSettingRequestCycleListener.CSPDirectiveSandboxValue.ALLOW_FORMS;
> -import static
> org.apache.wicket.csp.CSPSettingRequestCycleListener.CSPDirectiveSandboxValue.EMPTY;
> -import static
> org.apache.wicket.csp.CSPSettingRequestCycleListener.CSPDirectiveSrcValue.NONE;
> -import static
> org.apache.wicket.csp.CSPSettingRequestCycleListener.CSPDirectiveSrcValue.SELF;
> -import static
> org.apache.wicket.csp.CSPSettingRequestCycleListener.CSPDirectiveSrcValue.WILDCARD;
> +import static org.apache.wicket.csp.CSPDirective.CHILD_SRC;
> +import static org.apache.wicket.csp.CSPDirective.DEFAULT_SRC;
> +import static org.apache.wicket.csp.CSPDirective.FRAME_SRC;
> +import static org.apache.wicket.csp.CSPDirective.REPORT_URI;
> +import static org.apache.wicket.csp.CSPDirective.SANDBOX;
> +import static org.apache.wicket.csp.CSPDirectiveSandboxValue.ALLOW_FORMS;
> +import static org.apache.wicket.csp.CSPDirectiveSandboxValue.EMPTY;
> +import static org.apache.wicket.csp.CSPDirectiveSrcValue.NONE;
> +import static org.apache.wicket.csp.CSPDirectiveSrcValue.SELF;
> +import static org.apache.wicket.csp.CSPDirectiveSrcValue.WILDCARD;
>
>  import java.net.URI;
>  import java.net.URISyntaxException;
> @@ -37,9 +37,6 @@ import java.util.Set;
>  import java.util.stream.Collectors;
>  import java.util.stream.Stream;
>
> -import org.apache.wicket.csp.CSPSettingRequestCycleListener.CSPDirective;
> -import
> org.apache.wicket.csp.CSPSettingRequestCycleListener.CSPDirectiveSandboxValue;
> -import
> org.apache.wicket.csp.CSPSettingRequestCycleListener.CSPDirectiveSrcValue;
>  import org.apache.wicket.mock.MockHomePage;
>  import org.apache.wicket.util.tester.WicketTester;
>  import org.junit.jupiter.api.Assertions;
> 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
> index 755a000..48e3def 100644
> ---
> a/wicket-core/src/test/java/org/apache/wicket/pageStore/CryptingPageStoreTest.java
> +++
> b/wicket-core/src/test/java/org/apache/wicket/pageStore/CryptingPageStoreTest.java
> @@ -23,10 +23,10 @@ import java.io.StreamCorruptedException;
>  import java.security.GeneralSecurityException;
>
>  import org.apache.wicket.MockPage;
> -import org.apache.wicket.WicketRuntimeException;
>  import org.apache.wicket.mock.MockPageContext;
>  import org.apache.wicket.mock.MockPageStore;
>  import org.apache.wicket.serialize.java.JavaSerializer;
> +import org.apache.wicket.util.tester.WicketTester;
>  import org.junit.jupiter.api.Test;
>
>  /**
> @@ -34,13 +34,13 @@ import org.junit.jupiter.api.Test;
>   *
>   * @author svenmeier
>   */
> -public class CryptingPageStoreTest
> +public class CryptingPageStoreTest extends WicketTester
>

extends WicketTester ?!
maybe you meant WicketTestCase ?


>  {
>
>         @Test
>         void test()
>         {
> -               CryptingPageStore store = new CryptingPageStore(new
> MockPageStore());
> +               CryptingPageStore store = new CryptingPageStore(new
> MockPageStore(), getApplication());
>                 JavaSerializer serializer = new JavaSerializer("test");
>
>                 IPageContext context = new MockPageContext();
> @@ -60,7 +60,7 @@ public class CryptingPageStoreTest
>         @Test
>         void testFail()
>         {
> -               CryptingPageStore store = new CryptingPageStore(new
> MockPageStore());
> +               CryptingPageStore store = new CryptingPageStore(new
> MockPageStore(), getApplication());
>                 JavaSerializer serializer = new JavaSerializer("test");
>
>                 MockPageContext context = new MockPageContext();
>
>