From 326da6bd4f25d711ad44456f91e72c860592caff Mon Sep 17 00:00:00 2001 From: Markus Winter Date: Fri, 24 Jan 2025 19:23:24 +0100 Subject: [PATCH] remove commons-codec usage from core Since Java 17 we have HexFormat so use it instead of commons-codec or the self-written implementation --- core/src/main/java/hudson/Util.java | 29 +++++++++---------- .../security/seed/UserSeedProperty.java | 4 +-- .../java/jenkins/telemetry/Telemetry.java | 4 +-- .../jenkins/util/JSONSignatureValidator.java | 9 +++--- .../java/jenkins/telemetry/TelemetryTest.java | 4 +-- 5 files changed, 23 insertions(+), 27 deletions(-) diff --git a/core/src/main/java/hudson/Util.java b/core/src/main/java/hudson/Util.java index 07d00a44d006..8e9f1002f3d9 100644 --- a/core/src/main/java/hudson/Util.java +++ b/core/src/main/java/hudson/Util.java @@ -92,6 +92,7 @@ import java.util.Collections; import java.util.Date; import java.util.EnumSet; +import java.util.HexFormat; import java.util.List; import java.util.Locale; import java.util.Map; @@ -115,7 +116,6 @@ import jenkins.util.MemoryReductionUtil; import jenkins.util.SystemProperties; import jenkins.util.io.PathRemover; -import org.apache.commons.codec.digest.DigestUtils; import org.apache.commons.io.IOUtils; import org.apache.commons.lang.time.FastDateFormat; import org.apache.tools.ant.BuildException; @@ -635,7 +635,6 @@ public static String ensureEndsWith(@CheckForNull String subject, @CheckForNull * The stream will be closed by this method at the end of this method. * @return * 32-char wide string - * @see DigestUtils#md5Hex(InputStream) */ @NonNull public static String getDigestOf(@NonNull InputStream source) throws IOException { @@ -710,13 +709,7 @@ public static SecretKey toAes128Key(@NonNull String s) { @NonNull public static String toHexString(@NonNull byte[] data, int start, int len) { - StringBuilder buf = new StringBuilder(); - for (int i = 0; i < len; i++) { - int b = data[start + i] & 0xFF; - if (b < 16) buf.append('0'); - buf.append(Integer.toHexString(b)); - } - return buf.toString(); + return HexFormat.of().formatHex(data, start, len); } @NonNull @@ -726,12 +719,7 @@ public static String toHexString(@NonNull byte[] bytes) { @NonNull public static byte[] fromHexString(@NonNull String data) { - if (data.length() % 2 != 0) - throw new IllegalArgumentException("data must have an even number of hexadecimal digits"); - byte[] r = new byte[data.length() / 2]; - for (int i = 0; i < data.length(); i += 2) - r[i / 2] = (byte) Integer.parseInt(data.substring(i, i + 2), 16); - return r; + return HexFormat.of().parseHex(data); } /** @@ -1992,9 +1980,18 @@ public static byte[] getSHA256DigestOf(@NonNull byte[] input) { * Returns Hex string of SHA-256 Digest of passed input */ @Restricted(NoExternalUse.class) - public static String getHexOfSHA256DigestOf(byte[] input) throws IOException { + public static String getHexOfSHA256DigestOf(byte[] input) { //get hex string of sha 256 of payload byte[] payloadDigest = Util.getSHA256DigestOf(input); return (payloadDigest != null) ? Util.toHexString(payloadDigest) : null; } + + + /** + * Returns Hex string of SHA-256 Digest of passed string + */ + @Restricted(NoExternalUse.class) + public static String getHexOfSHA256DigestOf(String input) { + return getHexOfSHA256DigestOf(input.getBytes(StandardCharsets.UTF_8)); + } } diff --git a/core/src/main/java/jenkins/security/seed/UserSeedProperty.java b/core/src/main/java/jenkins/security/seed/UserSeedProperty.java index 968ee9320f58..17d9dd4335c1 100644 --- a/core/src/main/java/jenkins/security/seed/UserSeedProperty.java +++ b/core/src/main/java/jenkins/security/seed/UserSeedProperty.java @@ -28,6 +28,7 @@ import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import hudson.BulkChange; import hudson.Extension; +import hudson.Util; import hudson.model.User; import hudson.model.UserProperty; import hudson.model.UserPropertyDescriptor; @@ -39,7 +40,6 @@ import jenkins.model.Jenkins; import jenkins.security.LastGrantedAuthoritiesProperty; import jenkins.util.SystemProperties; -import org.apache.commons.codec.binary.Hex; import org.jenkinsci.Symbol; import org.kohsuke.accmod.Restricted; import org.kohsuke.accmod.restrictions.DoNotUse; @@ -103,7 +103,7 @@ private void renewSeedInternal() { byte[] bytes = new byte[SEED_NUM_BYTES]; while (Objects.equals(newSeed, currentSeed)) { RANDOM.nextBytes(bytes); - newSeed = new String(Hex.encodeHex(bytes)); + newSeed = Util.toHexString(bytes); } this.seed = newSeed; } diff --git a/core/src/main/java/jenkins/telemetry/Telemetry.java b/core/src/main/java/jenkins/telemetry/Telemetry.java index de081c16954b..968ca0f08dba 100644 --- a/core/src/main/java/jenkins/telemetry/Telemetry.java +++ b/core/src/main/java/jenkins/telemetry/Telemetry.java @@ -32,6 +32,7 @@ import hudson.ExtensionPoint; import hudson.PluginWrapper; import hudson.ProxyConfiguration; +import hudson.Util; import hudson.model.AsyncPeriodicWork; import hudson.model.TaskListener; import hudson.model.UsageStatistics; @@ -51,7 +52,6 @@ import jenkins.model.Jenkins; import jenkins.util.SystemProperties; import net.sf.json.JSONObject; -import org.apache.commons.codec.digest.DigestUtils; import org.kohsuke.accmod.Restricted; import org.kohsuke.accmod.restrictions.NoExternalUse; @@ -221,7 +221,7 @@ protected void execute(TaskListener listener) throws IOException, InterruptedExc wrappedData.put("type", telemetry.getId()); wrappedData.put("payload", data); String correlationId = ExtensionList.lookupSingleton(Correlator.class).getCorrelationId(); - wrappedData.put("correlator", DigestUtils.sha256Hex(correlationId + telemetry.getId())); + wrappedData.put("correlator", Util.getHexOfSHA256DigestOf(correlationId + telemetry.getId())); String body = wrappedData.toString(); if (LOGGER.isLoggable(Level.FINEST)) { diff --git a/core/src/main/java/jenkins/util/JSONSignatureValidator.java b/core/src/main/java/jenkins/util/JSONSignatureValidator.java index e6f5429f43bc..d3f416a91fe3 100644 --- a/core/src/main/java/jenkins/util/JSONSignatureValidator.java +++ b/core/src/main/java/jenkins/util/JSONSignatureValidator.java @@ -1,6 +1,7 @@ package jenkins.util; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; +import hudson.Util; import hudson.util.FormValidation; import java.io.ByteArrayInputStream; import java.io.File; @@ -33,8 +34,6 @@ import java.util.logging.Logger; import jenkins.model.Jenkins; import net.sf.json.JSONObject; -import org.apache.commons.codec.DecoderException; -import org.apache.commons.codec.binary.Hex; import org.apache.commons.io.output.TeeOutputStream; import org.jvnet.hudson.crypto.CertificateUtil; import org.jvnet.hudson.crypto.SignatureOutputStream; @@ -221,10 +220,10 @@ private boolean verifySignature(Signature signature, String providedSignature) { // This approach might look unnecessarily clever, but short of having redundant Signature instances, // there doesn't seem to be a better approach for this. try { - if (signature.verify(Hex.decodeHex(providedSignature.toCharArray()))) { + if (signature.verify(Util.fromHexString(providedSignature))) { return true; } - } catch (SignatureException | DecoderException ignore) { + } catch (SignatureException | IllegalArgumentException ignore) { // ignore } @@ -242,7 +241,7 @@ private boolean verifySignature(Signature signature, String providedSignature) { * Utility method supporting both possible digest formats: Base64 and Hex */ private boolean digestMatches(byte[] digest, String providedDigest) { - return providedDigest.equalsIgnoreCase(Hex.encodeHexString(digest)) || providedDigest.equalsIgnoreCase(Base64.getEncoder().encodeToString(digest)); + return providedDigest.equalsIgnoreCase(Util.toHexString(digest)) || providedDigest.equalsIgnoreCase(Base64.getEncoder().encodeToString(digest)); } diff --git a/test/src/test/java/jenkins/telemetry/TelemetryTest.java b/test/src/test/java/jenkins/telemetry/TelemetryTest.java index 701ffa7e4d0e..d5606a484228 100644 --- a/test/src/test/java/jenkins/telemetry/TelemetryTest.java +++ b/test/src/test/java/jenkins/telemetry/TelemetryTest.java @@ -13,6 +13,7 @@ import edu.umd.cs.findbugs.annotations.CheckForNull; import edu.umd.cs.findbugs.annotations.NonNull; import hudson.ExtensionList; +import hudson.Util; import hudson.model.UnprotectedRootAction; import hudson.security.csrf.CrumbExclusion; import jakarta.servlet.FilterChain; @@ -31,7 +32,6 @@ import java.util.logging.Level; import java.util.regex.Pattern; import net.sf.json.JSONObject; -import org.apache.commons.codec.digest.DigestUtils; import org.apache.commons.io.IOUtils; import org.junit.Before; import org.junit.Rule; @@ -99,7 +99,7 @@ public void testPerTrialCorrelator() throws Exception { .atMost(10, TimeUnit.SECONDS) .until(() -> types, hasItem("test-data")); //90ecf3ce1cd5ba1e5ad3cde7ad08a941e884f2e4d9bd463361715abab8efedc5 - assertThat(correlators, hasItem(DigestUtils.sha256Hex(correlationId + "test-data"))); + assertThat(correlators, hasItem(Util.getHexOfSHA256DigestOf(correlationId + "test-data"))); } @Test