Bouncy Castle is a widely used cryptography library for Java, especially in environments that require Federal Information Processing Standards (FIPS). In late 2022, a vulnerability was reported (CVE-2022-45146) that affected the Bouncy Castle FIPS Java API (BC-FJA), particularly when running on newer Java versions (13+). This case is a classic example of how changes in dependent platforms can introduce subtle security issues in third-party libraries.
If you’re running older, FIPS-compliant Java (7, 8, 11), you’re safe. But if your environment drifts into the world of Java 13 or newer, be warned: keys you think are secure may be erased from memory prematurely, causing unpredictable behavior—or worse, data loss.
Let’s break down how this flaw happens, provide some technical insight with code examples, and point to further resources for remediation.
How the Vulnerability Happens
In FIPS modules, cryptographic keys are usually stored in memory for only as long as absolutely necessary. Good security hygiene: overwrite them (zero them out) as soon as you’re done. The Bouncy Castle FIPS module for Java does this with temporary keys. But with Java 13 and above, a pattern change in the JVM’s garbage collector meant that a key could be zeroed before the module was done with it.
This means:
Temporary keys were being erased from memory, even while still in use.
- Errors could crop up: cryptographic operations fail unexpectedly, or (though less likely) remnants of old keys might become available somewhere else.
- FIPS compliance isn't at risk for compliant environments because they only certify Java 7, 8, and 11, but everyone else could see info loss or crashes.
Let’s say you’ve got a class like this (simplified for clarity)
public class CryptoOperation {
private byte[] tempKey;
public void initKey() {
tempKey = generateRandomKey();
}
public byte[] operate(byte[] data) {
// Use tempKey in crypto process
return encryptWithKey(data, tempKey);
}
protected void finalize() throws Throwable {
// Zero out the key on object cleanup
Arrays.fill(tempKey, (byte) x00);
super.finalize();
}
}
Where’s the problem?
With newer JVMs, the garbage collector may free up the CryptoOperation instance before operate() finishes (especially if references are lost early). When that happens, finalize() runs too soon, filling tempKey with zeros. As a result, operate() might try to run with a zeroed-out key, causing failures or, in edge cases, leaking timing information due to exceptions.
Let’s emulate the problem with a simple example
public class Demo {
public static void main(String[] args) throws Exception {
System.out.println(encryptWithFinalizedKey());
}
static byte[] encryptWithFinalizedKey() throws Exception {
CryptoOperation op = new CryptoOperation();
op.initKey();
byte[] result = null;
new Object() {
protected void finalize() throws Throwable {
op.finalize(); // Simulate GC's finalize call
}
};
System.gc(); // Hint GC to try collect (not guaranteed)
result = op.operate("Hello".getBytes());
return result; // May fail or be gibberish with zeroed key
}
}
Note: In reality, GC timing is nondeterministic, but rapid object loss and explicit GC calls make this situation possible.
Who’s Affected?
- FIPS Users (Java 7, 8, 11): Safe! These are the only Java versions covered by the module’s FIPS certificate.
Non-FIPS builds of Bouncy Castle: Not affected. The issue is only in the special FIPS module.
Key takeaway: The design assumption (that finalizers ran only after all use) broke when the Java platform introduced changes in object finalization and GC.
Fixes and Mitigation
Bouncy Castle fixed this bug in BC-FJA version 1..2.4
Patch notes: https://github.com/bcgit/bc-java/releases/tag/r1rv1024
If you must stay on older versions, stick with Java 7, 8, or 11 for FIPS mode.
3. Avoid relying on object finalizers (finalize()) for key erasure. Use explicit lifecycle management instead.
This is not a remote code execution bug, but it can
- Cause cryptographic failures (encryption/decryption might break mid-operation)
Cause loss of data availability or reliability
- In rare cases, leave behind fragments of key memory in places that might be leaked or reused, though post-zeroing risk is considered low
No working “exploit” in the wild, but if you’re running a sensitive system (eg. hardware tokens, managed key stores), you might see error logs like:
java.lang.IllegalStateException: Key has been zeroed
at org.bouncycastle.fips.module.crypto.engine(Unknown Source)
...
Or, operations silently corrupt output if keys get erased too soon.
References and Further Reading
- CVE-2022-45146 NVD Entry
- Bouncy Castle BC-FJA 1..2.4 Release Notes
- Bouncy Castle Official Advisory
- FIPS certification (NIST's CMVP listing)
Conclusion
CVE-2022-45146 is a lesson in crypto hygiene and platform dependencies. If you rely on Bouncy Castle for FIPS crypto and move to Java 13+, update your FIPS module or risk both outages and subtle data loss! For best results, always manage key zeroing explicitly and keep your main platforms and libraries in the compatibility matrix tested by your vendor.
Timeline
Published on: 11/21/2022 10:15:00 UTC
Last modified on: 11/30/2022 15:14:00 UTC