CVE-2026-85515 Overview
CVE-2026-85515 affects Bouncy Castle for Java before version 1.86. The flaw lets a truncated OpenPGP encrypted message be accepted without any error reported. On the Symmetrically Encrypted Integrity Protected Data (SEIPD) version 1 path, no integrity check is performed at all. On the Authenticated Encryption with Associated Data (AEAD) path (SEIPD version 2 and version 5 AEAD packets), the trailing message tag that authenticates total plaintext length is never reached. The result is that signed and encrypted messages can be read back as well-formed unsigned messages, with every packet following the literal silently dropped.
Critical Impact
On the SEIPD version 1 path, recipients receive CFB-decrypted plaintext with no integrity validation. Testing showed 136 distinct single-byte ciphertext modifications produced accepted, altered plaintext without any exception raised.
Affected Products
- Bouncy Castle for Java before 1.86
- Bouncy Castle for Java LTS before 2.73.13 (AEAD route only)
- Bouncy Castle for Java FIPS (BC-FJA) bcpg-fips before 1.0.14, 2.0.14.1, and 2.1.14 (AEAD route only)
Discovery Timeline
- 2026-10-03 - CVE-2026-85515 published to the National Vulnerability Database (NVD)
- 2026-10-06 - Last updated in NVD database
Technical Details for CVE-2026-85515
Vulnerability Analysis
The vulnerability is a missing truncation error in Bouncy Castle's high-level OpenPGP consumer streams [CWE-345]. RFC 9580 section 13.7 requires implementations to indicate a clear error when truncation is detected and to report suspect integrity on malleable ciphertext. Bouncy Castle detected the truncation but discarded it. When a message was truncated and the enclosing packet length left unchanged, BCPGInputStream.PartialInputStream raised an EOFException. BCPGInputStream.nextPacketTag() then reported that exception as a clean end of message, stopping the packet stream as though no packets remained.
On the AEAD path, when the literal data packet ended on a chunk boundary and the consumer read in increments smaller than one chunk, the look-ahead for the following packet triggered the truncated chunk read. BcAEADUtil and JceAEADUtil never reached the trailing message tag described in RFC 9580 section 5.13.2. Every byte released remained individually authenticated, making this a missing-error condition rather than a forgery.
Root Cause
On the SEIPD version 1 path, IntegrityProtectedInputStream verifies the modification detection code (MDC) from its close() method. That method was only reached when a read returned -1, which a truncated message never produces. As a result, PGPEncryptedData.verify() never ran, and no integrity check of any kind was performed on the CFB-decrypted plaintext. This condition is a residual of CVE-2026-12817, which closed the same outcome for attackers correcting the outer packet length.
Attack Vector
Reachability depends on the message shape rather than attacker-controlled input alone. The AEAD shape held for 3 of 131 consecutive payload lengths measured. The SEIPD version 1 shape held for one payload length in sixteen, at a truncation offset that did not move with the payload length. Callers that invoke PGPEncryptedData.verify() directly, or that read in increments of a whole AEAD chunk or more, are unaffected.
// Patch excerpt - IntegrityProtectedInputStream.java
private final PGPEncryptedData esk;
+ private boolean closed;
+
public IntegrityProtectedInputStream(InputStream in, PGPEncryptedData dataPacket)
{
super(in);
// Source: https://github.com/bcgit/bc-java/commit/ab7a235d1c20e3da28ce77167a96b5c14025efa7
// Patch excerpt - OpenPGPMessageInputStream.java
private final Layer layer; // the packet layer processed by this input stream
private InputStream in;
+ // this layer's IntegrityProtectedInputStream, if it has one - closed by close(), see there
+ private InputStream integrityProtectedIn;
private final List<PacketHandler> packetHandlers = new ArrayList<PacketHandler>()
// Source: https://github.com/bcgit/bc-java/commit/ab7a235d1c20e3da28ce77167a96b5c14025efa7
The patch makes IntegrityProtectedInputStream.close() idempotent, has OpenPGPMessageInputStream.close() close its layer's integrity-protected stream directly, and re-throws truncation EOFExceptions as plain IOExceptions that nextPacketTag() does not launder.
Detection Methods for CVE-2026-85515
Indicators of Compromise
- OpenPGP messages that decrypt successfully but return fewer packets than the sender produced, specifically a signed-and-encrypted message read back as unsigned.
- Applications using Bouncy Castle bcpg prior to 1.86 processing PGP messages received from untrusted or networked sources.
- Absence of PGPEncryptedData.verify() invocations in application code paths that consume high-level OpenPGP streams.
Detection Strategies
- Inventory Java dependencies for org.bouncycastle:bcpg-jdk18on and related artifacts and flag versions below 1.86, LTS below 2.73.13, or FIPS bcpg-fips below the fixed releases.
- Audit source code for consumers of OpenPGPMessageInputStream that read in increments smaller than a full AEAD chunk and do not call PGPEncryptedData.verify() explicitly.
- Add instrumentation around OpenPGP decryption to log cases where no signature packet is found on messages expected to be signed.
Monitoring Recommendations
- Monitor application logs for EOFException or truncated-read events in OpenPGP processing pipelines.
- Alert on unexpected drops in signature verification counts relative to encrypted message volume.
- Track Software Composition Analysis (SCA) reports for the affected Bouncy Castle artifacts across build pipelines.
How to Mitigate CVE-2026-85515
Immediate Actions Required
- Upgrade Bouncy Castle for Java to 1.86 or later as the primary remediation.
- Upgrade Bouncy Castle for Java LTS to 2.73.13 or later where the LTS edition is deployed.
- Upgrade Bouncy Castle FIPS bcpg-fips to 1.0.14, 2.0.14.1, or 2.1.14 depending on the series in use.
- Review application code that consumes high-level OpenPGP streams and confirm signature verification is enforced after decryption.
Patch Information
The fix is contained in commit ab7a235d1c20e3da28ce77167a96b5c14025efa7 in the bcgit/bc-java repository. AEAD decryption streams now re-throw the truncation EOFException as a plain IOException. OpenPGPMessageInputStream.close() closes the integrity-protected stream itself rather than relying on end-of-data detection. IntegrityProtectedInputStream.close() was made idempotent as required by java.io.Closeable. See the Bouncy Castle commit and the CVE-2026-85515 wiki entry.
Workarounds
- Call PGPEncryptedData.verify() directly from application code; the low-level API is unaffected.
- Read AEAD streams in increments of a whole chunk or larger to avoid the truncated look-ahead path.
- Validate expected signatures on decrypted payloads before trusting plaintext for business logic.
# Maven dependency pin example
mvn versions:use-dep-version \
-Dincludes=org.bouncycastle:bcpg-jdk18on \
-DdepVersion=1.86 \
-DforceVersion=true
Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.

