This is an automated email from the ASF dual-hosted git repository. papegaaij pushed a commit to branch crypt-unification in repository https://gitbox.apache.org/repos/asf/wicket.git
commit 6b85d7c81a1db49e976f74dd5cd4c6b5b919c561 Author: Emond Papegaaij <[email protected]> AuthorDate: Fri Jul 3 15:42:41 2026 +0200 WICKET-7190 Let the crypt scheme generate its own key A factory cannot know what key material an encryption scheme needs, so key generation is moved from the factories onto the scheme: - ICryptScheme gains an abstract generateKey(SecureRandom). The scheme owns the key material; a factory only decides where the key lives (per session, global, externally supplied). All schemes sharing one SchemeCrypt must produce compatible keys, since existing ciphertext is decrypted with the current key during migration. - New AbstractAesGcmCryptScheme carries everything the AES-256 GCM-family schemes share: the 256-bit AES key generation and the encrypt/decrypt flow (12-byte nonce, 128-bit tag, nonce||ciphertext||tag layout, marker authenticated as associated data). AesGcmCryptScheme and AesGcmSivCryptScheme now only supply id(), the Cipher and the AlgorithmParameterSpec, removing the duplication that existed between them. All four call sites that previously hardcoded CipherUtils.generateKey("AES", 256, ...) now delegate to the configured scheme: - AbstractCryptFactory gains a protected generateKey(random) helper resolving the scheme from SecuritySettings; KeyInSessionCryptFactory uses it. - ApplicationKeyCryptFactory(SecureRandom) generates its key lazily on first use (so the scheme is configured by then) and caches it; the (SecretKey) external-key constructor is unchanged. - SecuritySettings.getAuthenticationStrategy() and CryptingPageStore both ask the scheme for the key. Tests: SchemeCryptTest verifies generateKey yields a usable 256-bit AES key that round-trips; CryptFactoryTest covers the lazy application-random-key path. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> --- .../wicket/core/util/crypt/CryptFactoryTest.java | 15 +++++ .../wicket/core/util/crypt/SchemeCryptTest.java | 14 ++++ ...tScheme.java => AbstractAesGcmCryptScheme.java} | 73 ++++++++++++--------- .../core/util/crypt/AbstractCryptFactory.java | 15 +++++ .../wicket/core/util/crypt/AesGcmCryptScheme.java | 74 ++-------------------- .../core/util/crypt/AesGcmSivCryptScheme.java | 73 ++------------------- .../util/crypt/ApplicationKeyCryptFactory.java | 25 +++++--- .../wicket/core/util/crypt/ICryptScheme.java | 16 ++++- .../core/util/crypt/KeyInSessionCryptFactory.java | 17 ----- .../apache/wicket/pageStore/CryptingPageStore.java | 8 +-- .../apache/wicket/settings/SecuritySettings.java | 5 +- 11 files changed, 133 insertions(+), 202 deletions(-) diff --git a/wicket-core-tests/src/test/java/org/apache/wicket/core/util/crypt/CryptFactoryTest.java b/wicket-core-tests/src/test/java/org/apache/wicket/core/util/crypt/CryptFactoryTest.java index 0bea8d4ccc..fe60ed8a6e 100644 --- a/wicket-core-tests/src/test/java/org/apache/wicket/core/util/crypt/CryptFactoryTest.java +++ b/wicket-core-tests/src/test/java/org/apache/wicket/core/util/crypt/CryptFactoryTest.java @@ -44,6 +44,21 @@ class CryptFactoryTest extends WicketTestCase assertArrayEquals("hello".getBytes(UTF_8), factory.newCrypt().decrypt(encrypted)); } + /** + * The {@link ApplicationKeyCryptFactory#ApplicationKeyCryptFactory(java.security.SecureRandom) + * random-key} constructor generates its key lazily (via the configured scheme) and then keeps + * it stable across {@link ICrypt} instances. + */ + @Test + void applicationRandomKeyIsStableAcrossCryptInstances() + { + ApplicationKeyCryptFactory factory = new ApplicationKeyCryptFactory( + tester.getApplication().getSecuritySettings().getRandomSupplier().getRandom()); + + byte[] encrypted = factory.newCrypt().encrypt("hello".getBytes(UTF_8)); + assertArrayEquals("hello".getBytes(UTF_8), factory.newCrypt().decrypt(encrypted)); + } + /** * The per-session key is stable within a session, so data encrypted through one {@link ICrypt} * can be decrypted through another obtained from the same factory in the same session. diff --git a/wicket-core-tests/src/test/java/org/apache/wicket/core/util/crypt/SchemeCryptTest.java b/wicket-core-tests/src/test/java/org/apache/wicket/core/util/crypt/SchemeCryptTest.java index a8b77552e3..c8c6136711 100644 --- a/wicket-core-tests/src/test/java/org/apache/wicket/core/util/crypt/SchemeCryptTest.java +++ b/wicket-core-tests/src/test/java/org/apache/wicket/core/util/crypt/SchemeCryptTest.java @@ -77,6 +77,20 @@ public class SchemeCryptTest assertArrayEquals(plain, crypt.decrypt(crypt.encrypt(plain))); } + @ParameterizedTest + @MethodSource("schemes") + void generatedKeyIsUsableAes256(ICryptScheme scheme) + { + SecretKey key = scheme.generateKey(RANDOM); + assertEquals("AES", key.getAlgorithm()); + assertEquals(32, key.getEncoded().length); + + // a crypt built on the scheme-generated key round-trips + SchemeCrypt crypt = crypt(key, scheme); + byte[] plain = "generated-key".getBytes(StandardCharsets.UTF_8); + assertArrayEquals(plain, crypt.decrypt(crypt.encrypt(plain))); + } + @ParameterizedTest @MethodSource("schemes") void ciphertextStartsWithSchemeMarker(ICryptScheme scheme) diff --git a/wicket-core/src/main/java/org/apache/wicket/core/util/crypt/AesGcmCryptScheme.java b/wicket-core/src/main/java/org/apache/wicket/core/util/crypt/AbstractAesGcmCryptScheme.java similarity index 54% copy from wicket-core/src/main/java/org/apache/wicket/core/util/crypt/AesGcmCryptScheme.java copy to wicket-core/src/main/java/org/apache/wicket/core/util/crypt/AbstractAesGcmCryptScheme.java index 1ce04fb914..88545ddef9 100644 --- a/wicket-core/src/main/java/org/apache/wicket/core/util/crypt/AesGcmCryptScheme.java +++ b/wicket-core/src/main/java/org/apache/wicket/core/util/crypt/AbstractAesGcmCryptScheme.java @@ -18,49 +18,47 @@ package org.apache.wicket.core.util.crypt; import java.security.GeneralSecurityException; import java.security.SecureRandom; +import java.security.spec.AlgorithmParameterSpec; import java.util.Arrays; import javax.crypto.Cipher; import javax.crypto.SecretKey; -import javax.crypto.spec.GCMParameterSpec; +import javax.crypto.spec.SecretKeySpec; import org.apache.wicket.WicketRuntimeException; +import org.apache.wicket.util.crypt.CipherUtils; /** - * Authenticated encryption using JDK-native AES-256-GCM ({@code AES/GCM/NoPadding}). - * <p> - * This is the default scheme: it needs no extra dependencies and provides confidentiality and - * integrity. The ciphertext layout is {@code nonce(12) || ciphertext || tag(16)}; the scheme - * marker supplied by {@link SchemeCrypt} is authenticated as associated data. - * <p> - * A fresh random 96-bit nonce is generated per message. Because each key is used by a single - * session (or a single application), the number of encryptions under one key stays far below the - * NIST SP 800-38D safe-usage bound for random nonces. Deployments that need nonce-misuse - * resistance beyond that bound can switch to {@link AesGcmSivCryptScheme}. + * Base class for the AES-256 GCM-family {@link ICryptScheme}s ({@link AesGcmCryptScheme} and + * {@link AesGcmSivCryptScheme}). It owns everything the two variants share: + * <ul> + * <li>the 256-bit AES key they consume (see {@link #generateKey(SecureRandom)}) — the key is + * a property of the scheme, not of the factory that decides where the key lives;</li> + * <li>the ciphertext layout {@code nonce(12) || ciphertext || tag(16)} and the encrypt/decrypt + * flow (random 96-bit nonce, 128-bit authentication tag, marker authenticated as associated + * data).</li> + * </ul> + * Subclasses only supply the concrete {@link Cipher} and its {@link AlgorithmParameterSpec}, plus a + * stable {@link #id()}. */ -public class AesGcmCryptScheme implements ICryptScheme +public abstract class AbstractAesGcmCryptScheme implements ICryptScheme { - /** Stable marker id for this scheme. */ - public static final byte ID = 1; + /** AES key size in bits. */ + protected static final int KEY_LENGTH_BITS = 256; - private static final int NONCE_LENGTH = 12; + /** Nonce (IV) length in bytes; 96 bits is the GCM-family recommended size. */ + protected static final int NONCE_LENGTH = 12; - private static final int TAG_LENGTH_BITS = 128; - - /** - * @return the {@link Cipher} to use - * @throws GeneralSecurityException - * if the cipher is unavailable - */ - protected Cipher getCipher() throws GeneralSecurityException - { - return Cipher.getInstance("AES/GCM/NoPadding"); - } + /** Authentication tag length in bits. */ + protected static final int TAG_LENGTH_BITS = 128; @Override - public byte id() + public SecretKey generateKey(SecureRandom random) { - return ID; + // wrap in a SecretKeySpec so the key is guaranteed serializable (a per-session key is + // stored in the session's metadata) + byte[] encoded = CipherUtils.generateKey("AES", KEY_LENGTH_BITS, random).getEncoded(); + return new SecretKeySpec(encoded, "AES"); } @Override @@ -72,8 +70,7 @@ public class AesGcmCryptScheme implements ICryptScheme random.nextBytes(nonce); Cipher cipher = getCipher(); - cipher.init(Cipher.ENCRYPT_MODE, key, new GCMParameterSpec(TAG_LENGTH_BITS, nonce), - random); + cipher.init(Cipher.ENCRYPT_MODE, key, newParameterSpec(nonce), random); if (aad != null) { cipher.updateAAD(aad); @@ -104,7 +101,7 @@ public class AesGcmCryptScheme implements ICryptScheme byte[] nonce = Arrays.copyOfRange(ciphertext, 0, NONCE_LENGTH); Cipher cipher = getCipher(); - cipher.init(Cipher.DECRYPT_MODE, key, new GCMParameterSpec(TAG_LENGTH_BITS, nonce)); + cipher.init(Cipher.DECRYPT_MODE, key, newParameterSpec(nonce)); if (aad != null) { cipher.updateAAD(aad); @@ -118,4 +115,18 @@ public class AesGcmCryptScheme implements ICryptScheme return null; } } + + /** + * @return the {@link Cipher} to use + * @throws GeneralSecurityException + * if the cipher is unavailable + */ + protected abstract Cipher getCipher() throws GeneralSecurityException; + + /** + * @param nonce + * the freshly generated nonce + * @return the {@link AlgorithmParameterSpec} binding the nonce and tag length for this cipher + */ + protected abstract AlgorithmParameterSpec newParameterSpec(byte[] nonce); } diff --git a/wicket-core/src/main/java/org/apache/wicket/core/util/crypt/AbstractCryptFactory.java b/wicket-core/src/main/java/org/apache/wicket/core/util/crypt/AbstractCryptFactory.java index ed12284dde..775ba2266e 100644 --- a/wicket-core/src/main/java/org/apache/wicket/core/util/crypt/AbstractCryptFactory.java +++ b/wicket-core/src/main/java/org/apache/wicket/core/util/crypt/AbstractCryptFactory.java @@ -51,6 +51,21 @@ public abstract class AbstractCryptFactory implements ICryptFactory settings.getWhitelistedCryptSchemes()); } + /** + * Generates a fresh key for the application's configured + * {@link SecuritySettings#getCryptScheme() encryption scheme}. The scheme — not the + * factory — decides what key material to produce; the factory only decides where the key + * lives. + * + * @param random + * source of randomness + * @return a new secret key + */ + protected SecretKey generateKey(SecureRandom random) + { + return Application.get().getSecuritySettings().getCryptScheme().generateKey(random); + } + /** * @return the secret key to use; the key source (per-session, global, ...) is the concrete * factory's responsibility diff --git a/wicket-core/src/main/java/org/apache/wicket/core/util/crypt/AesGcmCryptScheme.java b/wicket-core/src/main/java/org/apache/wicket/core/util/crypt/AesGcmCryptScheme.java index 1ce04fb914..691824234e 100644 --- a/wicket-core/src/main/java/org/apache/wicket/core/util/crypt/AesGcmCryptScheme.java +++ b/wicket-core/src/main/java/org/apache/wicket/core/util/crypt/AesGcmCryptScheme.java @@ -17,15 +17,11 @@ package org.apache.wicket.core.util.crypt; import java.security.GeneralSecurityException; -import java.security.SecureRandom; -import java.util.Arrays; +import java.security.spec.AlgorithmParameterSpec; import javax.crypto.Cipher; -import javax.crypto.SecretKey; import javax.crypto.spec.GCMParameterSpec; -import org.apache.wicket.WicketRuntimeException; - /** * Authenticated encryption using JDK-native AES-256-GCM ({@code AES/GCM/NoPadding}). * <p> @@ -38,25 +34,11 @@ import org.apache.wicket.WicketRuntimeException; * NIST SP 800-38D safe-usage bound for random nonces. Deployments that need nonce-misuse * resistance beyond that bound can switch to {@link AesGcmSivCryptScheme}. */ -public class AesGcmCryptScheme implements ICryptScheme +public class AesGcmCryptScheme extends AbstractAesGcmCryptScheme { /** Stable marker id for this scheme. */ public static final byte ID = 1; - private static final int NONCE_LENGTH = 12; - - private static final int TAG_LENGTH_BITS = 128; - - /** - * @return the {@link Cipher} to use - * @throws GeneralSecurityException - * if the cipher is unavailable - */ - protected Cipher getCipher() throws GeneralSecurityException - { - return Cipher.getInstance("AES/GCM/NoPadding"); - } - @Override public byte id() { @@ -64,58 +46,14 @@ public class AesGcmCryptScheme implements ICryptScheme } @Override - public byte[] encrypt(byte[] plaintext, SecretKey key, byte[] aad, SecureRandom random) + protected Cipher getCipher() throws GeneralSecurityException { - try - { - byte[] nonce = new byte[NONCE_LENGTH]; - random.nextBytes(nonce); - - Cipher cipher = getCipher(); - cipher.init(Cipher.ENCRYPT_MODE, key, new GCMParameterSpec(TAG_LENGTH_BITS, nonce), - random); - if (aad != null) - { - cipher.updateAAD(aad); - } - - byte[] ciphertext = cipher.doFinal(plaintext); - - byte[] result = Arrays.copyOf(nonce, nonce.length + ciphertext.length); - System.arraycopy(ciphertext, 0, result, nonce.length, ciphertext.length); - return result; - } - catch (GeneralSecurityException ex) - { - throw new WicketRuntimeException(ex); - } + return Cipher.getInstance("AES/GCM/NoPadding"); } @Override - public byte[] decrypt(byte[] ciphertext, SecretKey key, byte[] aad) + protected AlgorithmParameterSpec newParameterSpec(byte[] nonce) { - try - { - if (ciphertext.length < NONCE_LENGTH) - { - return null; - } - - byte[] nonce = Arrays.copyOfRange(ciphertext, 0, NONCE_LENGTH); - - Cipher cipher = getCipher(); - cipher.init(Cipher.DECRYPT_MODE, key, new GCMParameterSpec(TAG_LENGTH_BITS, nonce)); - if (aad != null) - { - cipher.updateAAD(aad); - } - - return cipher.doFinal(ciphertext, NONCE_LENGTH, ciphertext.length - NONCE_LENGTH); - } - catch (GeneralSecurityException ex) - { - // authentication failure or malformed input - return null; - } + return new GCMParameterSpec(TAG_LENGTH_BITS, nonce); } } diff --git a/wicket-core/src/main/java/org/apache/wicket/core/util/crypt/AesGcmSivCryptScheme.java b/wicket-core/src/main/java/org/apache/wicket/core/util/crypt/AesGcmSivCryptScheme.java index d96cd90820..54ab50a367 100644 --- a/wicket-core/src/main/java/org/apache/wicket/core/util/crypt/AesGcmSivCryptScheme.java +++ b/wicket-core/src/main/java/org/apache/wicket/core/util/crypt/AesGcmSivCryptScheme.java @@ -17,13 +17,10 @@ package org.apache.wicket.core.util.crypt; import java.security.GeneralSecurityException; -import java.security.SecureRandom; -import java.util.Arrays; +import java.security.spec.AlgorithmParameterSpec; import javax.crypto.Cipher; -import javax.crypto.SecretKey; -import org.apache.wicket.WicketRuntimeException; import org.bouncycastle.jcajce.spec.AEADParameterSpec; /** @@ -38,25 +35,11 @@ import org.bouncycastle.jcajce.spec.AEADParameterSpec; * The ciphertext layout is {@code nonce(12) || ciphertext || tag(16)}; the scheme marker * supplied by {@link SchemeCrypt} is authenticated as associated data. */ -public class AesGcmSivCryptScheme implements ICryptScheme +public class AesGcmSivCryptScheme extends AbstractAesGcmCryptScheme { /** Stable marker id for this scheme. */ public static final byte ID = 2; - private static final int NONCE_LENGTH = 12; - - private static final int TAG_LENGTH_BITS = 128; - - /** - * @return the {@link Cipher} to use - * @throws GeneralSecurityException - * if the cipher is unavailable (e.g. Bouncy Castle not registered) - */ - protected Cipher getCipher() throws GeneralSecurityException - { - return Cipher.getInstance("AES/GCM-SIV/NoPadding"); - } - @Override public byte id() { @@ -64,58 +47,14 @@ public class AesGcmSivCryptScheme implements ICryptScheme } @Override - public byte[] encrypt(byte[] plaintext, SecretKey key, byte[] aad, SecureRandom random) + protected Cipher getCipher() throws GeneralSecurityException { - try - { - byte[] nonce = new byte[NONCE_LENGTH]; - random.nextBytes(nonce); - - Cipher cipher = getCipher(); - cipher.init(Cipher.ENCRYPT_MODE, key, new AEADParameterSpec(nonce, TAG_LENGTH_BITS), - random); - if (aad != null) - { - cipher.updateAAD(aad); - } - - byte[] ciphertext = cipher.doFinal(plaintext); - - byte[] result = Arrays.copyOf(nonce, nonce.length + ciphertext.length); - System.arraycopy(ciphertext, 0, result, nonce.length, ciphertext.length); - return result; - } - catch (GeneralSecurityException ex) - { - throw new WicketRuntimeException(ex); - } + return Cipher.getInstance("AES/GCM-SIV/NoPadding"); } @Override - public byte[] decrypt(byte[] ciphertext, SecretKey key, byte[] aad) + protected AlgorithmParameterSpec newParameterSpec(byte[] nonce) { - try - { - if (ciphertext.length < NONCE_LENGTH) - { - return null; - } - - byte[] nonce = Arrays.copyOfRange(ciphertext, 0, NONCE_LENGTH); - - Cipher cipher = getCipher(); - cipher.init(Cipher.DECRYPT_MODE, key, new AEADParameterSpec(nonce, TAG_LENGTH_BITS)); - if (aad != null) - { - cipher.updateAAD(aad); - } - - return cipher.doFinal(ciphertext, NONCE_LENGTH, ciphertext.length - NONCE_LENGTH); - } - catch (GeneralSecurityException ex) - { - // authentication failure or malformed input - return null; - } + return new AEADParameterSpec(nonce, TAG_LENGTH_BITS); } } diff --git a/wicket-core/src/main/java/org/apache/wicket/core/util/crypt/ApplicationKeyCryptFactory.java b/wicket-core/src/main/java/org/apache/wicket/core/util/crypt/ApplicationKeyCryptFactory.java index 16415a458b..045de2dfce 100644 --- a/wicket-core/src/main/java/org/apache/wicket/core/util/crypt/ApplicationKeyCryptFactory.java +++ b/wicket-core/src/main/java/org/apache/wicket/core/util/crypt/ApplicationKeyCryptFactory.java @@ -19,9 +19,7 @@ package org.apache.wicket.core.util.crypt; import java.security.SecureRandom; import javax.crypto.SecretKey; -import javax.crypto.spec.SecretKeySpec; -import org.apache.wicket.util.crypt.CipherUtils; import org.apache.wicket.util.lang.Args; /** @@ -31,12 +29,16 @@ import org.apache.wicket.util.lang.Args; * <p> * Use {@link #ApplicationKeyCryptFactory(SecretKey)} with a stable, externally-managed key to keep * data decryptable across application restarts. The - * {@link #ApplicationKeyCryptFactory(SecureRandom)} constructor generates a random key that lives - * only for the lifetime of this factory (so data does not survive a restart). + * {@link #ApplicationKeyCryptFactory(SecureRandom)} constructor instead generates a key (for the + * application's configured {@link org.apache.wicket.settings.SecuritySettings#getCryptScheme() + * scheme}) on first use; that key lives only for the lifetime of this factory, so data does not + * survive a restart. */ public class ApplicationKeyCryptFactory extends AbstractCryptFactory { - private final SecretKey key; + private final SecureRandom random; + + private SecretKey key; /** * @param key @@ -45,22 +47,29 @@ public class ApplicationKeyCryptFactory extends AbstractCryptFactory public ApplicationKeyCryptFactory(SecretKey key) { this.key = Args.notNull(key, "key"); + this.random = null; } /** - * Generates a random application-wide key. Data encrypted with it does not survive a restart. + * Generates a random application-wide key on first use. Data encrypted with it does not survive + * a restart. * * @param random * source of randomness */ public ApplicationKeyCryptFactory(SecureRandom random) { - this(new SecretKeySpec(CipherUtils.generateKey("AES", 256, random).getEncoded(), "AES")); + this.random = Args.notNull(random, "random"); } @Override - protected SecretKey getKey() + protected synchronized SecretKey getKey() { + if (key == null) + { + // generate the application-wide key lazily, once the scheme has been configured + key = generateKey(random); + } return key; } } diff --git a/wicket-core/src/main/java/org/apache/wicket/core/util/crypt/ICryptScheme.java b/wicket-core/src/main/java/org/apache/wicket/core/util/crypt/ICryptScheme.java index 9eeff286a2..ac8a124cdb 100644 --- a/wicket-core/src/main/java/org/apache/wicket/core/util/crypt/ICryptScheme.java +++ b/wicket-core/src/main/java/org/apache/wicket/core/util/crypt/ICryptScheme.java @@ -30,8 +30,11 @@ import javax.crypto.SecretKey; * the authentication tag and cannot be altered to force a different scheme. * <p> * Schemes must use authenticated (AEAD) encryption: {@link #decrypt} returns {@code null} when - * authentication fails. All keys are 256-bit AES {@link SecretKey}s generated by the factory; - * schemes do not derive keys. + * authentication fails. A scheme also produces the {@link SecretKey}s it consumes (see + * {@link #generateKey(SecureRandom)}) — the key material is a property of the scheme, whereas + * a {@link ICryptFactory factory} only decides <em>where</em> the key lives (per session, global, + * externally supplied). All schemes sharing one {@link SchemeCrypt} must produce mutually + * compatible keys, since existing ciphertext is decrypted with the current key during migration. * <p> * Implementations must be thread-safe and pick a unique, stable {@link #id()}. Wicket reserves * ids {@code 1..31}; custom schemes should use ids {@code >= 32}. @@ -43,6 +46,15 @@ public interface ICryptScheme */ byte id(); + /** + * Generate a new secret key suitable for this scheme. + * + * @param random + * source of randomness + * @return a new, serializable {@link SecretKey} + */ + SecretKey generateKey(SecureRandom random); + /** * Encrypt the given plaintext. * diff --git a/wicket-core/src/main/java/org/apache/wicket/core/util/crypt/KeyInSessionCryptFactory.java b/wicket-core/src/main/java/org/apache/wicket/core/util/crypt/KeyInSessionCryptFactory.java index 2b9eea54ab..4daf79bea0 100644 --- a/wicket-core/src/main/java/org/apache/wicket/core/util/crypt/KeyInSessionCryptFactory.java +++ b/wicket-core/src/main/java/org/apache/wicket/core/util/crypt/KeyInSessionCryptFactory.java @@ -16,15 +16,11 @@ */ package org.apache.wicket.core.util.crypt; -import java.security.SecureRandom; - import javax.crypto.SecretKey; -import javax.crypto.spec.SecretKeySpec; import org.apache.wicket.Application; import org.apache.wicket.MetaDataKey; import org.apache.wicket.Session; -import org.apache.wicket.util.crypt.CipherUtils; /** * The default {@link ICryptFactory}: it generates a fresh 256-bit AES key per user session and @@ -58,17 +54,4 @@ public class KeyInSessionCryptFactory extends AbstractCryptFactory } return key; } - - /** - * Generates a new (serializable) 256-bit AES key. - * - * @param random - * source of randomness - * @return a new key - */ - protected SecretKey generateKey(SecureRandom random) - { - byte[] encoded = CipherUtils.generateKey("AES", 256, random).getEncoded(); - return new SecretKeySpec(encoded, "AES"); - } } 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 e0a714ebcf..0a0abe9a25 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 @@ -18,10 +18,8 @@ package org.apache.wicket.pageStore; import java.io.Serializable; import java.nio.ByteBuffer; -import java.security.SecureRandom; import javax.crypto.SecretKey; -import javax.crypto.spec.SecretKeySpec; import org.apache.wicket.Application; import org.apache.wicket.MetaDataKey; @@ -30,7 +28,6 @@ import org.apache.wicket.core.util.crypt.ICrypt; import org.apache.wicket.core.util.crypt.SchemeCrypt; import org.apache.wicket.page.IManageablePage; import org.apache.wicket.settings.SecuritySettings; -import org.apache.wicket.util.crypt.CipherUtils; import org.apache.wicket.util.lang.Args; /** @@ -97,8 +94,9 @@ public class CryptingPageStore extends DelegatingPageStore private SecretKey generateKey() { - SecureRandom random = application.getSecuritySettings().getRandomSupplier().getRandom(); - return new SecretKeySpec(CipherUtils.generateKey("AES", 256, random).getEncoded(), "AES"); + SecuritySettings settings = application.getSecuritySettings(); + // the scheme decides what key to generate; the store only decides where it lives (session) + return settings.getCryptScheme().generateKey(settings.getRandomSupplier().getRandom()); } /** 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 ed12b61b39..cde0f5c8bf 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 @@ -33,7 +33,6 @@ import java.util.Collection; import java.util.List; import javax.crypto.SecretKey; -import javax.crypto.spec.SecretKeySpec; import org.apache.wicket.core.random.DefaultSecureRandomSupplier; import org.apache.wicket.core.random.ISecureRandomSupplier; @@ -43,7 +42,6 @@ import org.apache.wicket.core.util.crypt.ICryptFactory; import org.apache.wicket.core.util.crypt.ICryptScheme; import org.apache.wicket.core.util.crypt.KeyInSessionCryptFactory; import org.apache.wicket.core.util.crypt.SchemeCrypt; -import org.apache.wicket.util.crypt.CipherUtils; import org.apache.wicket.util.lang.Args; /** @@ -360,8 +358,7 @@ public class SecuritySettings // application-wide key. The key is random per boot; supply a custom strategy with a // stable key to keep cookies valid across application restarts. SecureRandom random = getRandomSupplier().getRandom(); - SecretKey key = new SecretKeySpec( - CipherUtils.generateKey("AES", 256, random).getEncoded(), "AES"); + SecretKey key = getCryptScheme().generateKey(random); ICrypt crypt = new SchemeCrypt(key, random, getCryptScheme(), getWhitelistedCryptSchemes()); authenticationStrategy = new DefaultAuthenticationStrategy("LoggedIn", crypt);
