{"record":{"id":"df8fe55f276f3045","repo":"lingochamp/FileDownloader","slug":"huh-md5-should-be-supported","errorCode":null,"errorMessage":"Huh, MD5 should be supported?","messagePattern":"Huh, MD5 should be supported\\?","errorType":"exception","errorClass":"RuntimeException","httpStatus":null,"severity":"error","filePath":"library/src/main/java/com/liulishuo/filedownloader/util/FileDownloadUtils.java","lineNumber":234,"sourceCode":"     * @param url  The downloading URL.\n     * @param path If {@code pathAsDirectory} is {@code true}, {@code path} would be the absolute\n     *             directory to place the file;\n     *             If {@code pathAsDirectory} is {@code false}, {@code path} would be the absolute\n     *             file path.\n     * @return The download id.\n     */\n    public static int generateId(final String url, final String path,\n                                 final boolean pathAsDirectory) {\n        return CustomComponentHolder.getImpl().getIdGeneratorInstance()\n                .generateId(url, path, pathAsDirectory);\n    }\n\n    public static String md5(String string) {\n        byte[] hash;\n        try {\n            hash = MessageDigest.getInstance(\"MD5\").digest(string.getBytes(\"UTF-8\"));\n        } catch (NoSuchAlgorithmException e) {\n            throw new RuntimeException(\"Huh, MD5 should be supported?\", e);\n        } catch (UnsupportedEncodingException e) {\n            throw new RuntimeException(\"Huh, UTF-8 should be supported?\", e);\n        }\n\n        StringBuilder hex = new StringBuilder(hash.length * 2);\n        for (byte b : hash) {\n            if ((b & 0xFF) < 0x10) hex.append(\"0\");\n            hex.append(Integer.toHexString(b & 0xFF));\n        }\n        return hex.toString();\n    }\n\n\n    public static String getStack() {\n        return getStack(true);\n    }\n\n    public static String getStack(final boolean printLine) {","sourceCodeStart":216,"sourceCodeEnd":252,"githubUrl":"https://github.com/lingochamp/FileDownloader/blob/6237a8cac174bcc916e4342b14ab1ab72a5768d4/library/src/main/java/com/liulishuo/filedownloader/util/FileDownloadUtils.java#L216-L252","documentation":"md5() computes an MD5 digest of a UTF-8 string and wraps any NoSuchAlgorithmException in a RuntimeException. MD5 is guaranteed by the JCA spec on every JVM/Android runtime, so hitting this means the platform's security provider list is broken. The thrown message is intentionally rhetorical since the case should be impossible.","triggerScenarios":"MessageDigest.getInstance(\"MD5\") throws NoSuchAlgorithmException while generating a download filename hash — only possible when the JVM's crypto providers are misconfigured or stripped (e.g. custom runtime without the default provider).","commonSituations":"Highly customized/hardened Android builds or embedded JVMs with a reduced security provider set; classpath/shading conflicts that remove the default provider registration.","solutions":["Register a security provider: Security.addProvider(new BouncyCastleProvider()) or restore the default AndroidOpenSSL/BouncyCastle provider.","Inspect Security.getProviders() at startup to find which provider registration is missing and fix the runtime configuration.","As a last resort, catch the RuntimeException and fall back to a non-MD5 filename scheme (e.g. Base64-encoded URL)."],"exampleFix":"// before\nString name = FileDownloadUtils.generateFileName(url); // throws if MD5 missing\n\n// after\nif (Security.getProvider(\"BC\") == null) {\n    Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());\n}\nString name = FileDownloadUtils.generateFileName(url);","handlingStrategy":"try-catch","validationCode":"try {\n    MessageDigest.getInstance(\"MD5\");\n} catch (NoSuchAlgorithmException e) {\n    Security.addProvider(new BouncyCastleProvider());\n}","typeGuard":null,"tryCatchPattern":"try {\n    String name = FileDownloadUtils.generateFileName(url);\n} catch (RuntimeException e) {\n    Log.e(\"FileDownloader\", \"MD5 unavailable on this runtime\", e);\n    name = Base64.encodeToString(url.getBytes(), Base64.URL_SAFE);\n}","preventionTips":["Verify Security.getProviders() on custom/hardened devices before release.","Avoid shading or stripping JCA provider classes in ProGuard/R8 rules.","Test filename generation on all supported device profiles."],"tags":["android","crypto","md5","runtime-environment"],"backgroundTag":"internal-invariant-violation","analyzedSha":"6237a8cac174bcc916e4342b14ab1ab72a5768d4","analyzedAt":"2026-09-08T23:50:48.168Z","contentChangedAt":"2026-09-08T23:50:48.168Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}