diff --git a/instrumentation/runtime-telemetry/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/runtimetelemetry/JarAnalyzer.java b/instrumentation/runtime-telemetry/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/runtimetelemetry/JarAnalyzer.java index 2de59441727e..228ecc2d37b8 100644 --- a/instrumentation/runtime-telemetry/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/runtimetelemetry/JarAnalyzer.java +++ b/instrumentation/runtime-telemetry/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/runtimetelemetry/JarAnalyzer.java @@ -220,9 +220,9 @@ static void processUrl(Logger logger, URL archiveUrl) { builder.put(PACKAGE_VERSION, jarDetails.version()); builder.put(PACKAGE_DESCRIPTION, jarDetails.packageDescription()); - String packageChecksum = jarDetails.computeSha1(); + String packageChecksum = jarDetails.computeSha256(); builder.put(PACKAGE_CHECKSUM, packageChecksum); - builder.put(PACKAGE_CHECKSUM_ALGORITHM, "SHA1"); + builder.put(PACKAGE_CHECKSUM_ALGORITHM, "SHA-256"); logger .logRecordBuilder() diff --git a/instrumentation/runtime-telemetry/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/runtimetelemetry/JarDetails.java b/instrumentation/runtime-telemetry/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/runtimetelemetry/JarDetails.java index 20532a406cd9..82cf4723eb07 100644 --- a/instrumentation/runtime-telemetry/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/runtimetelemetry/JarDetails.java +++ b/instrumentation/runtime-telemetry/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/runtimetelemetry/JarDetails.java @@ -11,7 +11,6 @@ import java.io.IOException; import java.io.InputStream; -import java.math.BigInteger; import java.net.URL; import java.security.DigestInputStream; import java.security.MessageDigest; @@ -37,17 +36,18 @@ class JarDetails { static final String JAR_EXTENSION = "jar"; static final String WAR_EXTENSION = "war"; static final String EAR_EXTENSION = "ear"; + private static final char[] HEX_DIGITS = "0123456789abcdef".toCharArray(); private static final Map EMBEDDED_FORMAT_TO_EXTENSION = Stream.of(JAR_EXTENSION, WAR_EXTENSION, EAR_EXTENSION) .collect( collectingAndThen( toMap(ext -> ('.' + ext + "!/"), identity()), Collections::unmodifiableMap)); - private static final ThreadLocal sha1 = + private static final ThreadLocal sha256 = ThreadLocal.withInitial( () -> { try { - return MessageDigest.getInstance("SHA1"); + return MessageDigest.getInstance("SHA-256"); } catch (NoSuchAlgorithmException e) { throw new IllegalStateException(e); } @@ -56,14 +56,14 @@ class JarDetails { private final URL url; @Nullable private final Properties pom; @Nullable private final Manifest manifest; - private final String sha1Checksum; + private final String sha256Checksum; private JarDetails( - URL url, @Nullable Properties pom, @Nullable Manifest manifest, String sha1Checksum) { + URL url, @Nullable Properties pom, @Nullable Manifest manifest, String sha256Checksum) { this.url = url; this.pom = pom; this.manifest = manifest; - this.sha1Checksum = sha1Checksum; + this.sha256Checksum = sha256Checksum; } static JarDetails forUrl(URL url) throws IOException { @@ -88,14 +88,14 @@ static JarDetails forUrl(URL url) throws IOException { url, getPom(jarFile, jarEntry), getManifest(jarFile, jarEntry), - computeDigest(jarFile, jarEntry, sha1.get())); + computeDigest(jarFile, jarEntry, sha256.get())); } } } } try (JarFile jarFile = new JarFile(UrlPaths.toFile(url))) { return new JarDetails( - url, getPom(jarFile), getManifest(jarFile), computeDigest(url, sha1.get())); + url, getPom(jarFile), getManifest(jarFile), computeDigest(url, sha256.get())); } } @@ -185,9 +185,9 @@ String packageDescription() { return name + " by " + vendor; } - /** Returns the SHA1 hash of this file, e.g. {@code 30d16ec2aef6d8094c5e2dce1d95034ca8b6cb42}. */ - String computeSha1() { - return sha1Checksum; + /** Returns the lowercase hex-encoded SHA-256 digest of this file as a 64-character string. */ + String computeSha256() { + return sha256Checksum; } private static String computeDigest(URL url, MessageDigest md) throws IOException { @@ -205,12 +205,23 @@ private static String computeDigest(JarFile jarFile, JarEntry jarEntry, MessageD private static String computeDigest(InputStream inputStream, MessageDigest md) throws IOException { - md.reset(); - DigestInputStream dis = new DigestInputStream(inputStream, md); - byte[] buffer = new byte[8192]; - while (dis.read(buffer) != -1) {} - byte[] digest = md.digest(); - return String.format(Locale.ROOT, "%040x", new BigInteger(1, digest)); + try (DigestInputStream digestInputStream = new DigestInputStream(inputStream, md)) { + byte[] buffer = new byte[8192]; + while (digestInputStream.read(buffer) != -1) {} + return toHex(md.digest()); + } finally { + md.reset(); + } + } + + static String toHex(byte[] bytes) { + char[] chars = new char[bytes.length * 2]; + for (int i = 0; i < bytes.length; i++) { + int value = bytes[i] & 0xff; + chars[i * 2] = HEX_DIGITS[value >>> 4]; + chars[i * 2 + 1] = HEX_DIGITS[value & 0x0f]; + } + return new String(chars); } @Nullable diff --git a/instrumentation/runtime-telemetry/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/runtimetelemetry/JarAnalyzerInstallerTest.java b/instrumentation/runtime-telemetry/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/runtimetelemetry/JarAnalyzerInstallerTest.java index 28c673f4da66..248a0cbb4d3b 100644 --- a/instrumentation/runtime-telemetry/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/runtimetelemetry/JarAnalyzerInstallerTest.java +++ b/instrumentation/runtime-telemetry/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/runtimetelemetry/JarAnalyzerInstallerTest.java @@ -46,10 +46,10 @@ void jarAnalyzerEnabled() { logRecord -> assertThat(logRecord.getAttributes()) .containsEntry("package.type", "jar") - .containsEntry("package.checksum_algorithm", "SHA1") + .containsEntry("package.checksum_algorithm", "SHA-256") .hasEntrySatisfying( stringKey("package.checksum"), - value -> assertThat(value).matches("[0-9a-f]{40}")) + value -> assertThat(value).matches("[0-9a-f]{64}")) .hasEntrySatisfying( stringKey("package.path"), value -> assertThat(value).isNotNull()) .satisfies( diff --git a/instrumentation/runtime-telemetry/testing/src/test/java/io/opentelemetry/javaagent/instrumentation/runtimetelemetry/JarAnalyzerTest.java b/instrumentation/runtime-telemetry/testing/src/test/java/io/opentelemetry/javaagent/instrumentation/runtimetelemetry/JarAnalyzerTest.java index 75ca185f69c1..2f534e7406be 100644 --- a/instrumentation/runtime-telemetry/testing/src/test/java/io/opentelemetry/javaagent/instrumentation/runtimetelemetry/JarAnalyzerTest.java +++ b/instrumentation/runtime-telemetry/testing/src/test/java/io/opentelemetry/javaagent/instrumentation/runtimetelemetry/JarAnalyzerTest.java @@ -71,9 +71,10 @@ private static Stream processUrlArguments() { .matches( "opentelemetry-javaagent-runtime-telemetry-[0-9a-zA-Z-\\.]+\\.jar")) .containsEntry(PACKAGE_DESCRIPTION, "javaagent by OpenTelemetry") - .containsEntry(PACKAGE_CHECKSUM_ALGORITHM, "SHA1") + .containsEntry(PACKAGE_CHECKSUM_ALGORITHM, "SHA-256") .hasEntrySatisfying( - PACKAGE_CHECKSUM, checksum -> assertThat(checksum).isNotEmpty()))), + PACKAGE_CHECKSUM, + checksum -> assertThat(checksum).matches("[0-9a-f]{64}")))), // dummy war Arguments.of( archiveUrl(new File(System.getenv("DUMMY_APP_WAR"))), @@ -83,9 +84,10 @@ private static Stream processUrlArguments() { .containsEntry(PACKAGE_TYPE, "war") .containsEntry(PACKAGE_PATH, "app.war") .containsEntry(PACKAGE_DESCRIPTION, "Dummy App by OpenTelemetry") - .containsEntry(PACKAGE_CHECKSUM_ALGORITHM, "SHA1") + .containsEntry(PACKAGE_CHECKSUM_ALGORITHM, "SHA-256") .hasEntrySatisfying( - PACKAGE_CHECKSUM, checksum -> assertThat(checksum).isNotEmpty()))), + PACKAGE_CHECKSUM, + checksum -> assertThat(checksum).matches("[0-9a-f]{64}")))), // io.opentelemetry:opentelemetry-api Arguments.of( archiveUrl(Tracer.class), @@ -99,9 +101,10 @@ private static Stream processUrlArguments() { assertThat(path) .matches("opentelemetry-api-[0-9a-zA-Z-\\.]+\\.jar")) .containsEntry(PACKAGE_DESCRIPTION, "all") - .containsEntry(PACKAGE_CHECKSUM_ALGORITHM, "SHA1") + .containsEntry(PACKAGE_CHECKSUM_ALGORITHM, "SHA-256") .hasEntrySatisfying( - PACKAGE_CHECKSUM, checksum -> assertThat(checksum).isNotEmpty()))), + PACKAGE_CHECKSUM, + checksum -> assertThat(checksum).matches("[0-9a-f]{64}")))), // org.springframework:spring-webmvc Arguments.of( archiveUrl(HttpRequest.class), @@ -115,9 +118,10 @@ private static Stream processUrlArguments() { PACKAGE_PATH, path -> assertThat(path).matches("spring-web-[0-9a-zA-Z-\\.]+\\.jar")) .containsEntry(PACKAGE_DESCRIPTION, "org.springframework.web") - .containsEntry(PACKAGE_CHECKSUM_ALGORITHM, "SHA1") + .containsEntry(PACKAGE_CHECKSUM_ALGORITHM, "SHA-256") .hasEntrySatisfying( - PACKAGE_CHECKSUM, checksum -> assertThat(checksum).isNotEmpty()))), + PACKAGE_CHECKSUM, + checksum -> assertThat(checksum).matches("[0-9a-f]{64}")))), // com.google.guava:guava Arguments.of( archiveUrl(ImmutableMap.class), @@ -131,9 +135,10 @@ private static Stream processUrlArguments() { .containsEntry(PACKAGE_NAME, "com.google.guava:guava") .hasEntrySatisfying( PACKAGE_VERSION, version -> assertThat(version).isNotEmpty()) - .containsEntry(PACKAGE_CHECKSUM_ALGORITHM, "SHA1") + .containsEntry(PACKAGE_CHECKSUM_ALGORITHM, "SHA-256") .hasEntrySatisfying( - PACKAGE_CHECKSUM, checksum -> assertThat(checksum).isNotEmpty())))); + PACKAGE_CHECKSUM, + checksum -> assertThat(checksum).matches("[0-9a-f]{64}"))))); } private static URL archiveUrl(File file) { diff --git a/instrumentation/runtime-telemetry/testing/src/test/java/io/opentelemetry/javaagent/instrumentation/runtimetelemetry/JarDetailsTest.java b/instrumentation/runtime-telemetry/testing/src/test/java/io/opentelemetry/javaagent/instrumentation/runtimetelemetry/JarDetailsTest.java index 4a0cd695c640..4acd6c710866 100644 --- a/instrumentation/runtime-telemetry/testing/src/test/java/io/opentelemetry/javaagent/instrumentation/runtimetelemetry/JarDetailsTest.java +++ b/instrumentation/runtime-telemetry/testing/src/test/java/io/opentelemetry/javaagent/instrumentation/runtimetelemetry/JarDetailsTest.java @@ -5,6 +5,7 @@ package io.opentelemetry.javaagent.instrumentation.runtimetelemetry; +import static java.nio.charset.StandardCharsets.UTF_8; import static org.assertj.core.api.Assertions.assertThat; import java.io.ByteArrayOutputStream; @@ -21,6 +22,11 @@ class JarDetailsTest { + @Test + void toHex_encodesBytes() { + assertThat(JarDetails.toHex("hello".getBytes(UTF_8))).isEqualTo("68656c6c6f"); + } + @Test void forUrl_handlesPathWithSpaces(@TempDir Path tempDir) throws IOException { Path dirWithSpaces = Files.createDirectories(tempDir.resolve("dir with spaces")); @@ -33,7 +39,7 @@ void forUrl_handlesPathWithSpaces(@TempDir Path tempDir) throws IOException { JarDetails details = JarDetails.forUrl(url); assertThat(details.packageDescription()).isEqualTo("Test Title by Test Vendor"); - assertThat(details.computeSha1()).isNotEmpty(); + assertThat(details.computeSha256()).matches("[0-9a-f]{64}"); } @Test @@ -59,7 +65,7 @@ void forUrl_handlesEmbeddedJar(@TempDir Path tempDir) throws IOException { JarDetails details = JarDetails.forUrl(url); assertThat(details.packageDescription()).isEqualTo("Inner Title by Inner Vendor"); - assertThat(details.computeSha1()).isNotEmpty(); + assertThat(details.computeSha256()).matches("[0-9a-f]{64}"); } @Test @@ -86,7 +92,7 @@ void forUrl_handlesEmbeddedJarWithSpaces(@TempDir Path tempDir) throws IOExcepti JarDetails details = JarDetails.forUrl(url); assertThat(details.packageDescription()).isEqualTo("Inner Title by Inner Vendor"); - assertThat(details.computeSha1()).isNotEmpty(); + assertThat(details.computeSha256()).matches("[0-9a-f]{64}"); } private static Manifest manifest(String title, String vendor) {