[java-shib-shared] branch main updated: Add a HexSupport wrapper around commons-codec routines.

Codeberg noreply at shibboleth.net
Thu Oct 22 16:16:07 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/4e81a2bdfeb6f50cf5741f414cff70133daf9e89

The following commit(s) were added to refs/heads/main by this push:
     new 4e81a2bd Add a HexSupport wrapper around commons-codec routines.
4e81a2bd is described below

commit 4e81a2bdfeb6f50cf5741f414cff70133daf9e89
Author: Scott Cantor <scott at restingparrotsoftware.com>
AuthorDate: Thu Oct 22 12:15:56 2026 -0400

    Add a HexSupport wrapper around commons-codec routines.
---
 .../shibboleth/shared/net/CookieManagerTest.java   | 13 ++--
 .../security/RandomIdentifierParameterSpec.java    |  3 +-
 .../impl/RandomIdentifierGenerationStrategy.java   | 47 +++++++-----
 .../SecureRandomIdentifierGenerationStrategy.java  |  4 +-
 ...cureRandomIdentifierGenerationStrategyTest.java |  3 +-
 .../net/shibboleth/shared/codec/HexSupport.java    | 84 ++++++++++++++++++++++
 .../shibboleth/shared/codec/StringDigester.java    |  9 ++-
 .../shared/codec/StringDigesterTest.java           |  1 +
 8 files changed, 125 insertions(+), 39 deletions(-)

diff --git a/shib-networking/src/test/java/net/shibboleth/shared/net/CookieManagerTest.java b/shib-networking/src/test/java/net/shibboleth/shared/net/CookieManagerTest.java
index 78b09b35..dc618247 100644
--- a/shib-networking/src/test/java/net/shibboleth/shared/net/CookieManagerTest.java
+++ b/shib-networking/src/test/java/net/shibboleth/shared/net/CookieManagerTest.java
@@ -22,10 +22,6 @@ import java.util.random.RandomGenerator;
 
 import javax.annotation.Nonnull;
 
-import org.apache.commons.codec.BinaryEncoder;
-import org.apache.commons.codec.EncoderException;
-import org.apache.commons.codec.binary.Hex;
-import org.apache.commons.codec.binary.StringUtils;
 import org.springframework.mock.web.MockHttpServletRequest;
 import org.springframework.mock.web.MockHttpServletResponse;
 import org.testng.Assert;
@@ -35,6 +31,7 @@ import org.testng.annotations.Test;
 import jakarta.servlet.http.Cookie;
 import jakarta.servlet.http.HttpServletRequest;
 import jakarta.servlet.http.HttpServletResponse;
+import net.shibboleth.shared.codec.HexSupport;
 import net.shibboleth.shared.collection.CollectionSupport;
 import net.shibboleth.shared.component.ComponentInitializationException;
 import net.shibboleth.shared.net.CookieManager.SameSiteValue;
@@ -45,12 +42,10 @@ import net.shibboleth.shared.primitive.NonnullSupplier;
 public class CookieManagerTest {
 
     private RandomGenerator random;
-    private BinaryEncoder encoder;
     
     @BeforeClass
     public void setUp() {
         random = new SecureRandom();
-        encoder = new Hex();
     }
     
     @Test public void testInitFailure() {
@@ -143,7 +138,7 @@ public class CookieManagerTest {
         Assert.assertEquals(cookie.getAttribute("SameSite"), SameSiteValue.Lax.getValue());
     }
     
-    @Test public void testPurge() throws ComponentInitializationException, InterruptedException, EncoderException {
+    @Test public void testPurge() throws ComponentInitializationException, InterruptedException {
         MockHttpServletRequest request = new MockHttpServletRequest();
         MockHttpServletResponse response = new MockHttpServletResponse();
 
@@ -170,13 +165,13 @@ public class CookieManagerTest {
         Assert.assertTrue(respCookies[0].getName().compareTo(respCookies[1].getName()) > 0);
     }
 
-    @Nonnull private String getCookieName() throws EncoderException {
+    @Nonnull private String getCookieName() {
         final byte[] buf = new byte[16];
         random.nextBytes(buf);
 
         final Instant now = Instant.now();
         
-        return "_test_" + now.toEpochMilli() + '_' + StringUtils.newStringUsAscii(encoder.encode(buf));
+        return "_test_" + now.toEpochMilli() + '_' + HexSupport.encodeLowerCase(buf);
     }
 
 }
\ No newline at end of file
diff --git a/shib-security/src/main/java/net/shibboleth/shared/security/RandomIdentifierParameterSpec.java b/shib-security/src/main/java/net/shibboleth/shared/security/RandomIdentifierParameterSpec.java
index 5c3d7163..1a3c0dc4 100644
--- a/shib-security/src/main/java/net/shibboleth/shared/security/RandomIdentifierParameterSpec.java
+++ b/shib-security/src/main/java/net/shibboleth/shared/security/RandomIdentifierParameterSpec.java
@@ -20,7 +20,6 @@ import java.util.random.RandomGenerator;
 import javax.annotation.Nullable;
 
 import org.apache.commons.codec.BinaryEncoder;
-import org.apache.commons.codec.binary.Hex;
 
 import net.shibboleth.shared.annotation.ParameterName;
 import net.shibboleth.shared.annotation.constraint.NonNegative;
@@ -32,7 +31,7 @@ import net.shibboleth.shared.security.IdentifierGenerationStrategy.ParameterSpec
  * 
  * @param source random generator (this MUST be thread-safe), defaults to standard Java {@link SecureRandom} 
  * @param identifierSize size of identifiers to generate, defaults to 16
- * @param identifierEncoder an encoder to apply to the random data, defaults to {@link Hex}
+ * @param identifierEncoder an encoder to apply to the random data, defaults to hex encoding
  */
 public record RandomIdentifierParameterSpec(@Nullable @ParameterName(name="source") RandomGenerator source,
         @Nullable @NonNegative @ParameterName(name="identifierSize") Integer identifierSize,
diff --git a/shib-security/src/main/java/net/shibboleth/shared/security/impl/RandomIdentifierGenerationStrategy.java b/shib-security/src/main/java/net/shibboleth/shared/security/impl/RandomIdentifierGenerationStrategy.java
index 6e272a87..2f1682a1 100644
--- a/shib-security/src/main/java/net/shibboleth/shared/security/impl/RandomIdentifierGenerationStrategy.java
+++ b/shib-security/src/main/java/net/shibboleth/shared/security/impl/RandomIdentifierGenerationStrategy.java
@@ -14,26 +14,27 @@
 
 package net.shibboleth.shared.security.impl;
 
+import java.nio.charset.StandardCharsets;
 import java.security.InvalidAlgorithmParameterException;
 import java.security.SecureRandom;
 import java.util.Random;
 import java.util.random.RandomGenerator;
 
 import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
 import javax.annotation.concurrent.ThreadSafe;
 
 import net.shibboleth.shared.annotation.constraint.NotEmpty;
+import net.shibboleth.shared.codec.HexSupport;
 import net.shibboleth.shared.security.IdentifierGenerationStrategy;
 import net.shibboleth.shared.security.RandomIdentifierParameterSpec;
 
 import org.apache.commons.codec.BinaryEncoder;
 import org.apache.commons.codec.EncoderException;
-import org.apache.commons.codec.binary.Hex;
-import org.apache.commons.codec.binary.StringUtils;
 
 /**
  * Generates a random number of bytes via a {@link Random} source and encodes them into a string using a
- * {@link BinaryEncoder} ({@link Hex} by default).
+ * {@link BinaryEncoder}, or hex-encoding by default.
  */
 @ThreadSafe
 public class RandomIdentifierGenerationStrategy implements IdentifierGenerationStrategy {
@@ -45,18 +46,18 @@ public class RandomIdentifierGenerationStrategy implements IdentifierGenerationS
     private final int sizeOfIdentifier;
 
     /** Encoder used to convert the random bytes in to a string. */
-    @Nonnull private final BinaryEncoder encoder;
+    @Nullable private final BinaryEncoder encoder;
     
     /**
      * Default constructor.
      * 
      * <p>Initializes the random number source to a new {@link SecureRandom}, size of identifier is set to 16
-     * bytes, and the encoder is set to a {@link Hex}.</p>
+     * bytes, and the encoder is unset to default to hex-encoding.</p>
      */
     public RandomIdentifierGenerationStrategy() {
         random = new SecureRandom();
         sizeOfIdentifier = 16;
-        encoder = new Hex();
+        encoder = null;
     }
 
     /**
@@ -86,12 +87,7 @@ public class RandomIdentifierGenerationStrategy implements IdentifierGenerationS
                 sizeOfIdentifier = 16;
             }
             
-            final BinaryEncoder enc = spec.identifierEncoder();
-            if (enc != null) {
-                encoder = enc;
-            } else {
-                encoder = new Hex();
-            }
+            encoder = spec.identifierEncoder();
         } else {
             throw new InvalidAlgorithmParameterException("Invalid or unrecognized RandomParameterSpec");
         }
@@ -106,13 +102,28 @@ public class RandomIdentifierGenerationStrategy implements IdentifierGenerationS
     @Nonnull @NotEmpty public String generateIdentifier(final boolean xmlSafe) {
         final byte[] buf = new byte[sizeOfIdentifier];
         random.nextBytes(buf);
-        try {
-            if (xmlSafe) {
-                return "_" + StringUtils.newStringUsAscii(encoder.encode(buf));
+        if (xmlSafe) {
+            return "_" + encode(buf);
+        }
+        return encode(buf);
+    }
+    
+    /**
+     * Perform the specified encoding, or default to hex.
+     * 
+     * @param data data to encode
+     * 
+     * @return the encoded string
+     */
+    @Nonnull private String encode(@Nonnull final byte[] data) {
+        if (encoder != null) {
+            try {
+                return new String(encoder.encode(data), StandardCharsets.US_ASCII);
+            } catch (final EncoderException e) {
+                throw new RuntimeException(e);
             }
-            return StringUtils.newStringUsAscii(encoder.encode(buf));
-        } catch (final EncoderException e) {
-            throw new RuntimeException(e);
+        } else {
+            return HexSupport.encodeLowerCase(data);
         }
     }
 
diff --git a/shib-security/src/main/java/net/shibboleth/shared/security/impl/SecureRandomIdentifierGenerationStrategy.java b/shib-security/src/main/java/net/shibboleth/shared/security/impl/SecureRandomIdentifierGenerationStrategy.java
index 9c84d5de..4bef925b 100644
--- a/shib-security/src/main/java/net/shibboleth/shared/security/impl/SecureRandomIdentifierGenerationStrategy.java
+++ b/shib-security/src/main/java/net/shibboleth/shared/security/impl/SecureRandomIdentifierGenerationStrategy.java
@@ -20,8 +20,6 @@ import java.security.SecureRandom;
 import javax.annotation.Nonnull;
 import javax.annotation.concurrent.ThreadSafe;
 
-import org.apache.commons.codec.binary.Hex;
-
 import net.shibboleth.shared.security.RandomIdentifierParameterSpec;
 
 /**
@@ -35,7 +33,7 @@ public class SecureRandomIdentifierGenerationStrategy extends RandomIdentifierGe
      * Default constructor.
      * 
      * <p>Initializes the random number source to a new {@link SecureRandom}, size of identifier is set to 16
-     * bytes, and the encoder is set to a {@link Hex}.</p>
+     * bytes, and the encoder is unset to default to hex-encoding.</p>
      */
     public SecureRandomIdentifierGenerationStrategy() {
         
diff --git a/shib-security/src/test/java/net/shibboleth/shared/security/impl/SecureRandomIdentifierGenerationStrategyTest.java b/shib-security/src/test/java/net/shibboleth/shared/security/impl/SecureRandomIdentifierGenerationStrategyTest.java
index 25fce4b6..7247a61d 100644
--- a/shib-security/src/test/java/net/shibboleth/shared/security/impl/SecureRandomIdentifierGenerationStrategyTest.java
+++ b/shib-security/src/test/java/net/shibboleth/shared/security/impl/SecureRandomIdentifierGenerationStrategyTest.java
@@ -23,7 +23,6 @@ import java.util.Set;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
-import org.apache.commons.codec.binary.Hex;
 import org.testng.Assert;
 import org.testng.annotations.Test;
 
@@ -71,7 +70,7 @@ public class SecureRandomIdentifierGenerationStrategyTest {
         final Pattern ncNamePattern = Pattern.compile("^[a-zA-Z_][a-zA-Z0-9_\\-\\.]+$");
         final IdentifierGenerationStrategy strat =
                 IdentifierGenerationStrategy.getInstance(ProviderType.RANDOM,
-                        new RandomIdentifierParameterSpec(SecureRandom.getInstance("SHA1PRNG"), 16, new Hex()));
+                        new RandomIdentifierParameterSpec(SecureRandom.getInstance("SHA1PRNG"), 16, null));
         final int howMany = 1000;
         final Set<String> values = new HashSet<>(1000);
         for (int iteration = 1; iteration <= howMany; iteration++) {
diff --git a/shib-support/src/main/java/net/shibboleth/shared/codec/HexSupport.java b/shib-support/src/main/java/net/shibboleth/shared/codec/HexSupport.java
new file mode 100644
index 00000000..1207425a
--- /dev/null
+++ b/shib-support/src/main/java/net/shibboleth/shared/codec/HexSupport.java
@@ -0,0 +1,84 @@
+/*
+ * Licensed 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 net.shibboleth.shared.codec;
+
+import javax.annotation.Nonnull;
+
+import net.shibboleth.shared.logic.Constraint;
+
+import org.apache.commons.codec.DecoderException;
+import org.apache.commons.codec.binary.Hex;
+
+/**
+ * Helper class for working with hex encoding/decoding of binary data.
+ *
+ * <p>This wraps the current commons-codec implementation to insulate
+ * calling code from the specific implementation for future revision.</p>
+ * 
+ * @since 9.3.0
+ */
+public final class HexSupport {
+    
+    /** Constructor. */
+    private HexSupport() {
+
+    }
+    
+    /**
+     * Hex-encodes the given binary data into lower case characters.
+     * 
+     * @param data data to encode
+     * 
+     * @return the hex-encoded data
+     */
+    @Nonnull public static String encodeLowerCase(@Nonnull final byte[] data) {
+        Constraint.isNotNull(data, "Binary data to be encoded cannot be null");
+        
+        return Hex.encodeHexString(data, true);
+    }
+
+    /**
+     * Hex-encodes the given binary data into upper case characters.
+     * 
+     * @param data data to encode
+     * 
+     * @return the hex-encoded data
+     */
+    @Nonnull public static String encodeUpperCase(@Nonnull final byte[] data) {
+        Constraint.isNotNull(data, "Binary data to be encoded cannot be null");
+        
+        return Hex.encodeHexString(data, false);
+    }
+    
+    /**
+     * Decodes hex-encoded data back into binary.
+     * 
+     * @param data hex-encoded string
+     * 
+     * @return the decoded data
+     * 
+     * @throws DecodingException if unable to decode 
+     */
+    @Nonnull public static byte[] decode(@Nonnull final String data) throws DecodingException{
+        Constraint.isNotNull(data, "Base64 encoded data cannot be null");
+        
+        try {
+            return Hex.decodeHex(data);
+        } catch (final DecoderException e) {
+            throw new DecodingException(e);
+        }
+    }
+    
+}
\ No newline at end of file
diff --git a/shib-support/src/main/java/net/shibboleth/shared/codec/StringDigester.java b/shib-support/src/main/java/net/shibboleth/shared/codec/StringDigester.java
index 1c9a34da..23b39b01 100644
--- a/shib-support/src/main/java/net/shibboleth/shared/codec/StringDigester.java
+++ b/shib-support/src/main/java/net/shibboleth/shared/codec/StringDigester.java
@@ -15,6 +15,7 @@
 package net.shibboleth.shared.codec;
 
 import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
 import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
 import java.util.function.Function;
@@ -28,8 +29,6 @@ import net.shibboleth.shared.logic.Constraint;
 import net.shibboleth.shared.primitive.LoggerFactory;
 import net.shibboleth.shared.primitive.StringSupport;
 
-import org.apache.commons.codec.CharEncoding;
-import org.apache.commons.codec.binary.Hex;
 import org.slf4j.Logger;
 
 /**
@@ -53,7 +52,7 @@ public class StringDigester implements Function<String,String> {
     
     /** The default input character set.*/
     @SuppressWarnings("null")
-    @Nonnull public static final Charset DEFAULT_INPUT_CHARSET = Charset.forName(CharEncoding.UTF_8);
+    @Nonnull public static final Charset DEFAULT_INPUT_CHARSET = StandardCharsets.UTF_8;
     
     /** Logger. */
     @Nonnull private final Logger log = LoggerFactory.getLogger(StringDigester.class);
@@ -206,9 +205,9 @@ public class StringDigester implements Function<String,String> {
                     return null;
                 }
             case HEX_LOWER:
-                return new String(Hex.encodeHex(digestedBytes, true));
+                return HexSupport.encodeLowerCase(digestedBytes);
             case HEX_UPPER:
-                return new String(Hex.encodeHex(digestedBytes, false));
+                return HexSupport.encodeUpperCase(digestedBytes);
             default:
                 break;
         }
diff --git a/shib-support/src/test/java/net/shibboleth/shared/codec/StringDigesterTest.java b/shib-support/src/test/java/net/shibboleth/shared/codec/StringDigesterTest.java
index fad8097c..aeed7d2c 100644
--- a/shib-support/src/test/java/net/shibboleth/shared/codec/StringDigesterTest.java
+++ b/shib-support/src/test/java/net/shibboleth/shared/codec/StringDigesterTest.java
@@ -25,6 +25,7 @@ import org.testng.annotations.Test;
 /**
  * Test {@link StringDigester} functionality.
  */
+ at SuppressWarnings("javadoc")
 public class StringDigesterTest {
     
     private StringDigester digester;

-- 
To stop receiving notification emails like this one, please contact
the administrator of this repository.


More information about the commits mailing list