On Thu, Jan 16, 2020 at 10:51 PM <papega...@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 <emond.papega...@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(); > >