{"record":{"id":"96694a33b7ccae05","repo":"apache/pulsar","slug":"md5-algorithm-not-found","errorCode":null,"errorMessage":"MD5 algorithm not found","messagePattern":"MD5 algorithm not found","errorType":"exception","errorClass":"RuntimeException","httpStatus":null,"severity":"error","filePath":"pulsar-broker-common/src/main/java/org/apache/pulsar/common/configuration/anonymizer/DefaultRoleAnonymizerType.java","lineNumber":73,"sourceCode":"      }\n   },\n   MD5 {\n      private static final String PREFIX = \"MD5:\";\n      private static final FastThreadLocal<MessageDigest> DIGEST = new FastThreadLocal<MessageDigest>() {\n         @Override\n         protected MessageDigest initialValue() throws Exception {\n            // codeql[java/weak-cryptographic-algorithm] - md5 is sufficient for this use case\n            return MessageDigest.getInstance(\"MD5\");\n         }\n      };\n\n      @Override\n      public String anonymize(String role) {\n         try {\n            byte[] hash = DIGEST.get().digest(role.getBytes());\n            return PREFIX + Base64.getEncoder().encodeToString(hash);\n         } catch (Exception e) {\n            throw new RuntimeException(\"MD5 algorithm not found\", e);\n         }\n      }\n   };\n\n   private static final String REDACTED_VALUE = \"[REDACTED]\";\n   public abstract String anonymize(String role);\n}\n","sourceCodeStart":55,"sourceCodeEnd":81,"githubUrl":"https://github.com/apache/pulsar/blob/820761864ed8e2a7d2e52dd9763ad2ae117c1395/pulsar-broker-common/src/main/java/org/apache/pulsar/common/configuration/anonymizer/DefaultRoleAnonymizerType.java#L55-L81","documentation":"The MD5 role anonymizer hashes the role string with MessageDigest.getInstance(\"MD5\") and returns a Base64-encoded 'MD5:'-prefixed digest. Like the SHA-256 variant, every standard JDK ships MD5, so this RuntimeException signals a broken JCE environment rather than an input problem. It exists purely as a checked-exception bridge since anonymize() has no throws clause.","triggerScenarios":"Calling anonymize(role) on the MD5 anonymizer variant when MessageDigest.getInstance(\"MD5\") (via the FastThreadLocal DIGEST) throws NoSuchAlgorithmException — effectively only on a JRE without the standard providers.","commonSituations":"Same as SHA-256 variant: stripped/custom JRE, edited java.security removing the SUN provider, GraalVM native-image without registered MessageDigest services.","solutions":["Restore default providers in $JAVA_HOME/conf/security/java.security (ensure sun.security.provider.Sun is registered).","Use a full standard JDK distribution instead of a trimmed one.","For native-image builds, register MD5 in the security services configuration.","Consider migrating anonymization config to the SHA256 variant anyway, since MD5 is cryptographically weak."],"exampleFix":"// before\nroleAnonymizer = MD5\n// after (client and server config)\nroleAnonymizer = SHA256","handlingStrategy":"try-catch","validationCode":"boolean md5Available;\ntry {\n    java.security.MessageDigest.getInstance(\"MD5\");\n    md5Available = true;\n} catch (java.security.NoSuchAlgorithmException e) {\n    md5Available = false;\n}","typeGuard":null,"tryCatchPattern":"try {\n    String anon = anonymizer.anonymize(role);\n} catch (RuntimeException e) {\n    throw new IllegalStateException(\"JRE is missing MD5 provider; prefer SHA256 anonymizer anyway\", e);\n}","preventionTips":["Keep default JCE providers intact in java.security.","Prefer the SHA256 anonymizer variant — MD5 is weak and adds no availability benefit.","Test crypto providers on the exact deployment image (containers may use trimmed JREs).","Use full JDK distributions for broker tooling, not jlink-minimized runtimes missing crypto services."],"tags":["java","security","crypto","md5"],"backgroundTag":"missing-crypto-algorithm","analyzedSha":"820761864ed8e2a7d2e52dd9763ad2ae117c1395","analyzedAt":"2026-09-06T00:14:20.138Z","contentChangedAt":"2026-09-06T00:14:20.138Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}