Skip to content

fix(security): close #135 — replace SHA1PRNG with default SecureRandom (SEC-02)#157

Open
NSchatz wants to merge 4 commits into
Innovar-Healthcare:bridgelink_developmentfrom
NSchatz:fix/issue-135-securerandom
Open

fix(security): close #135 — replace SHA1PRNG with default SecureRandom (SEC-02)#157
NSchatz wants to merge 4 commits into
Innovar-Healthcare:bridgelink_developmentfrom
NSchatz:fix/issue-135-securerandom

Conversation

@NSchatz
Copy link
Copy Markdown

@NSchatz NSchatz commented May 14, 2026

Summary

Closes #135.

Replaces SecureRandom.getInstance("SHA1PRNG") with new SecureRandom() at the two vendored com.mirth.commons.encryption salt-generator call sites. On FIPS-mode JVM 17 (RHEL 9 / AlmaLinux 9 with update-crypto-policies --set FIPS), the JVM rejects SHA1PRNG and the server cannot start — Digester.initialize() and PBEEncryptor.initialize() throw NoSuchAlgorithmException at startup. The default no-arg SecureRandom resolves to a JVM-default provider that is FIPS-approved (per OpenJDK SecureRandom javadoc and OpenLiberty FIPS issue #24469).

Changes

  • server/src/com/mirth/commons/encryption/Digester.java:179SecureRandom.getInstance("SHA1PRNG")new SecureRandom().
  • server/src/com/mirth/commons/encryption/PBEEncryptor.java:83 — same replacement.
  • server/test/com/mirth/commons/encryption/test/SecureRandomAlgorithmTest.java (new) — four JUnit 4 tests:
    • new SecureRandom().getAlgorithm() is not "SHA1PRNG".
    • Digester.initialize() does not throw; its salt generator algorithm is not "SHA1PRNG".
    • PBEEncryptor.initialize() does not throw; its salt generator algorithm is not "SHA1PRNG".
    • Two successive new SecureRandom() instances produce different byte arrays (probabilistic randomness sanity per threat T-02-02).

Surrounding try/catch blocks are intentionally preserved (minimal-diff lock — CONTEXT.md "two one-line changes only"). The NoSuchAlgorithmException catch in Digester.java is now unreachable but harmless; future refactor tracked under threat T-02-03.

Test plan

  • ant -f server/build.xml test -Dtest=SecureRandomAlgorithmTest — all four tests pass (CI).
  • ant -f server/build.xml test -Dtest=DigesterTest — existing tests pass, no regression (CI).
  • grep -rn "SHA1PRNG" server/src donkey/src client/src custom-extensions/ --include='*.java' — zero production hits (verified locally).
  • Second-set-of-eyes review (milestone constraint).

Rationale

Per OpenJDK SecureRandom javadoc (Java 17) and OpenLiberty FIPS issue #24469, FIPS-mode JVMs reject the SHA1PRNG algorithm name and select a FIPS-approved DRBG when constructed via the no-arg constructor. The phase plan (.planning/phases/01-security-cluster/01-CONTEXT.md §#135) explicitly locks unconditional new SecureRandom() — no DRBG try/catch, no Tomcat-style runtime detection, no bc-fips migration (SEC-V2-02 deferred).


Local test verification (2026-05-13)

Ant 1.10.14 installed via tarball into ~/.local/opt/ant. OpenJDK 17.0.18 used.

  • ant -f server/build.xml compileBUILD SUCCESSFUL (35s)
  • ant -f server/build.xml test-compileBUILD SUCCESSFUL (23s)
  • java org.junit.runner.JUnitCore com.mirth.commons.encryption.test.SecureRandomAlgorithmTest com.mirth.commons.encryption.test.DigesterTestOK (13 tests) in 18.6s
    • 4 new SecureRandomAlgorithmTest tests pass — newSecureRandomDoesNotResolveToSHA1PRNG, digesterSaltGeneratorIsNotSHA1PRNGAfterInitialize, pbeEncryptorSaltGeneratorIsNotSHA1PRNGAfterInitialize, twoCallsToNewSecureRandomProduceDifferentBytes
    • 9 existing DigesterTest tests pass (regression — no behavior change)
  • Production-code grep gate: grep -rn "SHA1PRNG" server/src donkey/src client/src custom-extensions/ --include='*.java' returns zero matches.

Follow-up commit in this verification cycle

  • 5fcc16c65fix(135): set BouncyCastleProvider in PBEEncryptor test. The original test omitted encryptor.setProvider(new BouncyCastleProvider()) before calling encryptor.initialize(), causing IllegalArgumentException: missing provider from SecretKeyFactory.getInstance(...). Mirrors the existing pattern in digesterSaltGeneratorIsNotSHA1PRNGAfterInitialize. Production code unchanged.

NSchatz and others added 4 commits May 13, 2026 22:16
…care#135

- Adds SecureRandomAlgorithmTest with four JUnit 4 cases:
  1. new SecureRandom().getAlgorithm() != "SHA1PRNG" on JDK 17
  2. Digester salt generator algorithm != "SHA1PRNG" after initialize()
  3. PBEEncryptor salt generator algorithm != "SHA1PRNG" after initialize()
  4. Probabilistic randomness sanity (two instances → different bytes)
- Tests 2 and 3 are EXPECTED TO FAIL before the production swap in 2.2
  because the current Digester.java:179 and PBEEncryptor.java:83 pin the
  salt generator to SecureRandom.getInstance("SHA1PRNG") (FIPS-rejected).
- Uses iteration count 1000 (test-speed; not asserting on iteration count).
- Follows DigesterTest.java pattern for BouncyCastleProvider setup.
…ult SecureRandom in Digester and PBEEncryptor

Replaces SecureRandom.getInstance("SHA1PRNG") with new SecureRandom() at
the two salt-generator call sites in com.mirth.commons.encryption. On
FIPS-mode JVM 17 (RHEL 9 / AlmaLinux 9 with `update-crypto-policies --set
FIPS`), the JVM rejects SHA1PRNG and Digester.initialize() /
PBEEncryptor.initialize() throw NoSuchAlgorithmException at server start,
preventing BridgeLink from running.

The no-arg new SecureRandom() resolves to a provider-appropriate algorithm
(NativePRNG on non-FIPS Linux, a FIPS-approved DRBG on FIPS JVMs) per
OpenJDK SecureRandom javadoc and OpenLiberty FIPS issue #24469.

- server/src/com/mirth/commons/encryption/Digester.java:179 — swap.
- server/src/com/mirth/commons/encryption/PBEEncryptor.java:83 — swap.

Surrounding try/catch blocks are intentionally preserved (CONTEXT.md
locks "two one-line changes only" — minimal diff). The NoSuchAlgorithmException
catch in Digester.java becomes unreachable but harmless; future refactor
tracked under threat T-02-03.

After this commit, all four cases in SecureRandomAlgorithmTest (commit
7768cc9) move from RED → GREEN.

Closes Innovar-Healthcare#135
After the swap from SecureRandom.getInstance("SHA1PRNG") to
new SecureRandom() the surrounding catch (NoSuchAlgorithmException)
catches an exception the body can no longer throw. SpotBugs /
SonarQube will flag the dead catch.

Drop the try/catch and the now-unused import. The EncryptionException
declaration is retained on initialize()'s signature for forward
compatibility with subclass overrides.
Without setProvider() the test threw IllegalArgumentException: missing
provider when PBEEncryptor.initialize() called SecretKeyFactory.getInstance.
Mirror the existing pattern from digesterSaltGeneratorIsNotSHA1PRNGAfterInitialize.

Verified locally: ant test-compile + targeted JUnit run — all 4 tests pass
plus all 9 existing DigesterTest tests.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[SECURITY] Java 17 NoSuchAlgorithmException SHA1PRNG when FIPS enabled

1 participant