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 33eaf71733eb3bfb2f71b99b618585db95cb69a4 Author: Emond Papegaaij <[email protected]> AuthorDate: Fri Jul 3 16:04:56 2026 +0200 WICKET-7190 Update the user guide for the redesigned crypt API The "URLs encryption in detail" section still described the Wicket 10 crypt API (some of it already stale before this work). Bring it in line with the redesign: - ICrypt is shown from its real package org.apache.wicket.core.util.crypt with the byte-array + URL-safe-String signature and the null-on-failure contract. - The default implementation is SchemeCrypt (self-describing, authenticated ciphertext), not SunJceCrypt/PBEWithMD5AndDES. Document the pluggable ICryptScheme, the default AesGcmCryptScheme (JDK-native AES-256-GCM), the scheme whitelist / downgrade protection and migration, and the AES-256-GCM-SIV opt-in (requires Bouncy Castle). - The default factory is KeyInSessionCryptFactory (was KeyInSessionSunJceCrypt- Factory); the stateless note now points to ApplicationKeyCryptFactory. Also drop the outdated warning in the URL chapter that the default cipher "might not be strong enough for production": the default is now authenticated AES-256-GCM with a per-session key. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> --- .../src/main/asciidoc/security/security_4.adoc | 43 +++++++++++++++------- .../src/main/asciidoc/urls/urls_6.adoc | 4 +- 2 files changed, 31 insertions(+), 16 deletions(-) diff --git a/wicket-user-guide/src/main/asciidoc/security/security_4.adoc b/wicket-user-guide/src/main/asciidoc/security/security_4.adoc index 17c5939628..8e7dbd644c 100644 --- a/wicket-user-guide/src/main/asciidoc/security/security_4.adoc +++ b/wicket-user-guide/src/main/asciidoc/security/security_4.adoc @@ -1,27 +1,45 @@ -In chapter <<_generating_structured_and_clear_urls,10.6>> we have seen how to encrypt URLs using _CryptoMapper_ request mapper. To encrypt/decrypt page URLs _CryptoMapper_ uses an instance of _org.apache.wicket.util.crypt.ICrypt_ interface: +In chapter <<_generating_structured_and_clear_urls,10.6>> we have seen how to encrypt URLs using _CryptoMapper_ request mapper. To encrypt/decrypt page URLs _CryptoMapper_ uses an instance of the _org.apache.wicket.core.util.crypt.ICrypt_ interface: [source,java] ---- public interface ICrypt { - String encryptUrlSafe(final String plainText); + byte[] encrypt(byte[] plainBytes, byte[] associatedData); - String decryptUrlSafe(final String encryptedText); + byte[] decrypt(byte[] encryptedBytes, byte[] associatedData); + + // URL-safe (Base64) String convenience layer used by CryptoMapper + default String encryptUrlSafe(String plainText) { ... } + + default String decryptUrlSafe(String encryptedText) { ... } ... } ---- -The default implementation for this interface is class _org.apache.wicket.util.crypt.SunJceCrypt_. It provides password-based cryptography using _PBEWithMD5AndDES_ algorithm coming with the standard security providers in the Java Runtime Environment. +_ICrypt_ works on raw bytes and provides a URL-safe Base64 _String_ layer on top. The same interface is used everywhere Wicket encrypts data: encrypted URLs (_CryptoMapper_), the page store (see <<_page_storing,the chapter on page storing>>) and the "remember me" cookie. Decryption returns `null` on _any_ failure (unknown cipher, failed authentication, malformed input) instead of throwing, so undecryptable data is uniformly treated as absent. -NOTE: Since Java 9, unlimited strength cryptography is enabled by default. No additional configuration is needed. +The default implementation is _org.apache.wicket.core.util.crypt.SchemeCrypt_. It produces *self-describing, authenticated* ciphertext: every ciphertext is prefixed with a one-byte marker identifying the cipher (the "scheme") that produced it, and the payload is protected with authenticated encryption (AEAD) so any tampering is detected on decryption. The actual cipher is a pluggable _org.apache.wicket.core.util.crypt.ICryptScheme_; by default this is _AesGcmCryptScheme_, which uses JDK- [...] -By using _CryptoMapper(IRequestMapper wrappedMapper, Application application)_ constructor the mapper will use the configured _org.apache.wicket.util.crypt.ICryptFactory_ from _org.apache.wicket.settings.SecuritySettings.getCryptFactory()_. To use a stronger cryptography mechanism there are the following options: +NOTE: Since Java 9 unlimited strength cryptography is enabled by default, so AES-256 works out of the box with no additional configuration. -* The first option is to use constructor _CryptoMapper(IRequestMapper wrappedMapper, Supplier<ICrypt> cryptProvider)_ and give it an implementation of _java.util.function.Supplier_ that returns a custom _org.apache.wicket.util.crypt.ICrypt_. +The scheme is configured application-wide through _org.apache.wicket.settings.SecuritySettings_: -* The second option is to register a cipher factory at application level with method _setCryptFactory(ICryptFactory cryptFactory)_ of class _SecuritySettings_: +[source,java] +---- +@Override +public void init() { + super.init(); + // switch to AES-256-GCM-SIV (nonce-misuse resistant); requires Bouncy Castle + getSecuritySettings().setCryptScheme(new AesGcmSivCryptScheme()); + setRootRequestMapper(new CryptoMapper(getRootRequestMapper(), this)); +} +---- + +_AesGcmSivCryptScheme_ (AES-256-GCM-SIV) is also available but requires https://www.bouncycastle.org/[Bouncy Castle] on the classpath with its provider registered. On decryption, the scheme marker must match one of the schemes accepted by _SecuritySettings.setWhitelistedCryptSchemes_ (the encryption scheme is always accepted); unknown or non-whitelisted schemes are refused, which prevents downgrade attacks. To migrate to a stronger scheme without breaking existing data, temporarily white [...] + +While the scheme decides _how_ data is encrypted, an _org.apache.wicket.core.util.crypt.ICryptFactory_ decides _which key_ to use. The factory is configured with _SecuritySettings.setCryptFactory(ICryptFactory)_: [source,java] ---- @@ -33,11 +51,8 @@ public void init() { } ---- +The default factory is _org.apache.wicket.core.util.crypt.KeyInSessionCryptFactory_. It generates a unique, random 256-bit key for each user and stores it in her HTTP session. This way it helps to protect the application against https://owasp.org/www-community/attacks/csrf[CSRF] for each user of the application: the url itself serves as an https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html[encrypted token]. -Since version 6.19.0 Wicket uses _org.apache.wicket.core.util.crypt.KeyInSessionSunJceCryptFactory_ as a default factory for _ICrypt_ objects. This factory generates a unique key for each user that is stored in her HTTP -session. This way it helps to protect the application against https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF)[CSRF] -for each user of the application. The url itself serves as https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF)_Prevention_Cheat_Sheet#Encrypted_Token_Pattern[encrypted token] - -WARNING: _org.apache.wicket.core.util.crypt.KeyInSessionSunJceCryptFactory_ binds the http session if it is not already bound! If the application needs to run in stateless mode then the application will have to provide a custom -implementation of _ICryptFactory_ that stores the user specific keys by other means. +WARNING: _KeyInSessionCryptFactory_ binds the HTTP session if it is not already bound! If the application needs to run in stateless mode it must provide a factory that does not rely on the session — for example _org.apache.wicket.core.util.crypt.ApplicationKeyCryptFactory_ with a stable, application-wide key. Note that an application-wide key means encrypted URLs are no longer isolated per user. +Finally, _CryptoMapper_ offers two constructors: _CryptoMapper(IRequestMapper wrappedMapper, Application application)_ uses the _ICryptFactory_ configured on _SecuritySettings_, while _CryptoMapper(IRequestMapper wrappedMapper, Supplier<ICrypt> cryptProvider)_ lets you supply a specific _ICrypt_ instance directly. diff --git a/wicket-user-guide/src/main/asciidoc/urls/urls_6.adoc b/wicket-user-guide/src/main/asciidoc/urls/urls_6.adoc index 7f0ab3b16f..721ae8cee2 100644 --- a/wicket-user-guide/src/main/asciidoc/urls/urls_6.adoc +++ b/wicket-user-guide/src/main/asciidoc/urls/urls_6.adoc @@ -224,6 +224,6 @@ public void init() { As pointed out in the code above, pages and resources must be mounted after having set _CryptoMapper_ as root mapper, otherwise the mounted paths will not work. -WARNING: By default _CryptoMapper_ encrypts page URLs with a cipher that might not be strong enough for production environment. Paragraph -<<_security_with_wicket,"Security with Wicket">> will provide a more detailed description of how Wicket encrypts page URLs and we will see how to use stronger ciphers. +NOTE: By default _CryptoMapper_ encrypts page URLs with authenticated AES-256-GCM using a unique key per user session, which is suitable for production. Section +<<_urls_encryption_in_detail,"URLs encryption in detail">> describes how Wicket encrypts URLs and how to configure the cipher (scheme) and the key source.
