[java-shib-shared] branch main updated: JSSH-84 - Base64Support decode rejects input with commons-codec 1.23.0
Codeberg
noreply at shibboleth.net
Thu Oct 22 15:38:10 UTC 2026
This is an automated email from the git hooks/post-receive script.
codeberg pushed a commit to branch main
in repository java-shib-shared.
View the commit online:
https://codeberg.org/Shibboleth/java-shib-shared/commit/d7d970c8c8eb1d910916cf68c3e7219945ac21c7
The following commit(s) were added to refs/heads/main by this push:
new d7d970c8 JSSH-84 - Base64Support decode rejects input with commons-codec 1.23.0
d7d970c8 is described below
commit d7d970c8c8eb1d910916cf68c3e7219945ac21c7
Author: Scott Cantor <scott at restingparrotsoftware.com>
AuthorDate: Thu Oct 22 11:37:58 2026 -0400
JSSH-84 - Base64Support decode rejects input with commons-codec 1.23.0
https://shibboleth.atlassian.net/browse/JSSH-84
Add parallel workaround to Base32Support class.
---
.../net/shibboleth/shared/codec/Base32Support.java | 43 ++++++++++++++++++++--
.../net/shibboleth/shared/codec/Base64Support.java | 4 +-
.../shibboleth/shared/codec/Base32SupportTest.java | 9 ++++-
3 files changed, 50 insertions(+), 6 deletions(-)
diff --git a/shib-support/src/main/java/net/shibboleth/shared/codec/Base32Support.java b/shib-support/src/main/java/net/shibboleth/shared/codec/Base32Support.java
index 242ac253..90427892 100644
--- a/shib-support/src/main/java/net/shibboleth/shared/codec/Base32Support.java
+++ b/shib-support/src/main/java/net/shibboleth/shared/codec/Base32Support.java
@@ -16,6 +16,7 @@ package net.shibboleth.shared.codec;
import javax.annotation.Nonnull;
+import net.shibboleth.shared.annotation.constraint.NotEmpty;
import net.shibboleth.shared.logic.Constraint;
import net.shibboleth.shared.primitive.StringSupport;
@@ -30,14 +31,30 @@ import org.apache.commons.codec.binary.Base32;
* instances of the {@link Base32} for every operation. It also provides the helper method to produce both chunked and
* unchunked encoded content as strings.
* </p>
+ *
+ * <p>Most importantly this insulates calling code so we can swap in a different Base32 implementation, as commons-codec
+ * and many if not most others have a very inconvenient stance on whitespace, which is absolutely necessary to ignore, so
+ * changes to the implementation are likely to continue.</p>
+ *
+ * <p>As of 9.3, a new system property has been introduced ({@link #SANITIZE_WHITESPACE_PROPERTY_NAME}) to enable
+ * compatibility with newer commons-codec versions being used under this library at the cost of a whitespace removal scan.
+ * We do not support use of the library with that version, but the property provides a workaround for that case without
+ * incurring additional overhead for the project's own use. A future major update may obviate the need for that workaround.</p>
*/
public final class Base32Support {
+ /** System property name to enable whitespace sanitization on decode for commons-codec 1.23 compatibility. */
+ @Nonnull @NotEmpty public static final String SANITIZE_WHITESPACE_PROPERTY_NAME =
+ "net.shibboleth.shared.codec.base32.sanitizeWhitespace";
+
/** Chunk the encoded data into 76-character lines broken by CRLF characters. */
public static final boolean CHUNKED = true;
/** Do not chunk encoded data. */
public static final boolean UNCHUNKED = false;
+
+ /** Triggers whitespace sanitization of input when decoding, per JSSH-84. */
+ public static final boolean sanitize;
/** Encoder used to produce chunked output. */
@Nonnull private static final Base32 CHUNKED_ENCODER = new Base32(76, new byte[] { '\n' },
@@ -47,6 +64,11 @@ public final class Base32Support {
@Nonnull private static final Base32 UNCHUNKED_ENCODER = new Base32(0, new byte[] { '\n' },
false, (byte)'=', CodecPolicy.STRICT);
+ static {
+ final String prop = System.getProperty(SANITIZE_WHITESPACE_PROPERTY_NAME);
+ sanitize = "1".equals(prop) || "true".equalsIgnoreCase(prop);
+ }
+
/** Constructor. */
private Base32Support() {
@@ -63,7 +85,7 @@ public final class Base32Support {
* or the output is null.
*/
@Nonnull public static String encode(@Nonnull final byte[] data, final boolean chunked) throws EncodingException {
- Constraint.isNotNull(data, "Binary data to be encoded can not be null");
+ Constraint.isNotNull(data, "Binary data to be encoded cannot be null");
try {
String encoded = null;
@@ -94,9 +116,24 @@ public final class Base32Support {
* the underlying decoder, or the output is null.
*/
@Nonnull public static byte[] decode(@Nonnull final String data) throws DecodingException {
- Constraint.isNotNull(data, "Base32 encoded data can not be null");
+ Constraint.isNotNull(data, "Base32 encoded data cannot be null");
try {
- final byte[] decoded = CHUNKED_ENCODER.decode(data);
+ final byte[] decoded;
+ if (sanitize) {
+ // This is per JSSH-84, to provide compatibility when third parties use
+ // commons-codec >= 1.23 with this library (though not supported by us).
+ // The STRICT unchunked encoder "works" but will reject whitespace so a
+ // regex is applied to remove any from the input first.
+ decoded = UNCHUNKED_ENCODER.decode(data.replaceAll("\\s+", ""));
+ } else {
+ // This probably should be using the unchunked encoder, but in order to
+ // prevent any unexpected changes when using the existing, supported
+ // commons-codec version, I'm leaving it as is until such time as we
+ // replace the implementation outright to control the behavior.
+ // The older decoder even in STRICT mode ignored whitespace (as it
+ // should), and tolerated the trim() our encode methods perform.
+ decoded = CHUNKED_ENCODER.decode(data);
+ }
//TODO: can this ever be null, do we need to check for null?
if (null == decoded) {
throw new DecodingException("Base32 decoded data was null");
diff --git a/shib-support/src/main/java/net/shibboleth/shared/codec/Base64Support.java b/shib-support/src/main/java/net/shibboleth/shared/codec/Base64Support.java
index 08095959..4ec502a9 100644
--- a/shib-support/src/main/java/net/shibboleth/shared/codec/Base64Support.java
+++ b/shib-support/src/main/java/net/shibboleth/shared/codec/Base64Support.java
@@ -85,7 +85,7 @@ public final class Base64Support {
* or the output is null.
*/
@Nonnull public static String encode(@Nonnull final byte[] data, final boolean chunked) throws EncodingException{
- Constraint.isNotNull(data, "Binary data to be encoded can not be null");
+ Constraint.isNotNull(data, "Binary data to be encoded cannot be null");
try {
String encoded = null;
@@ -116,7 +116,7 @@ public final class Base64Support {
* the underlying decoder, or the output is null.
*/
@Nonnull public static byte[] decode(@Nonnull final String data) throws DecodingException{
- Constraint.isNotNull(data, "Base64 encoded data can not be null");
+ Constraint.isNotNull(data, "Base64 encoded data cannot be null");
try {
final byte[] decoded;
if (sanitize) {
diff --git a/shib-support/src/test/java/net/shibboleth/shared/codec/Base32SupportTest.java b/shib-support/src/test/java/net/shibboleth/shared/codec/Base32SupportTest.java
index c824b6fc..acf161d2 100644
--- a/shib-support/src/test/java/net/shibboleth/shared/codec/Base32SupportTest.java
+++ b/shib-support/src/test/java/net/shibboleth/shared/codec/Base32SupportTest.java
@@ -37,7 +37,6 @@ public class Base32SupportTest {
/** Invalid base32, should not produce a result.*/
private final static String INVALID_BASE32="MC======";
-
/**
* Test that an invalid base32 input string does not return a response, instead throwing a {@link DecodingException}.
*
@@ -68,4 +67,12 @@ public class Base32SupportTest {
Assert.assertEquals(ENCODED_TEXT, Base32Support.encode(PLAIN_TEXT.getBytes(), false));
}
+ /** Test decoding content.
+ *
+ * @throws DecodingException on decoding failure.
+ */
+ @Test public void testDecode() throws DecodingException {
+ Assert.assertEquals(new String(Base32Support.decode(ENCODED_TEXT)), PLAIN_TEXT);
+ }
+
}
\ No newline at end of file
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
More information about the commits
mailing list