CVE-2025-14813 is a cryptographic implementation vulnerability in Bouncy Castle for Java that turns what should be a large GOST CTR counter space into an effectively one-byte counter.
The flaw affects the G3413CTRBlockCipher implementation used for GOST R 34.13-2015 counter mode. Instead of incrementing the complete counter field, vulnerable versions of Bouncy Castle increment only the last byte. Once that byte wraps, Bouncy Castle begins generating keystream from counter values it has already used.
That is a fundamental failure for CTR mode.
Bouncy Castle describes the issue as the GOSTCTR implementation being unable to process more than 255 blocks correctly. The vendor currently lists BC 1.59 through 1.80.1, BC 1.81, and BC 1.82 through 1.83 as affected, with fixes available in 1.80.2, 1.81.1, and 1.84. (GitHub)
The underlying patch is remarkably small. In the vulnerable implementation, incrementing the counter was essentially equivalent to:
CTR[CTR.length - 1]++;
The corrected implementation performs carry propagation across the counter bytes and prevents the counter from crossing into the IV portion of the block. (GitHub)
Small code change. Large cryptographic consequence.
A CTR cipher depends on never reusing the same keystream under the same key. Once a counter repeats, that guarantee disappears. An attacker capable of observing ciphertext can XOR ciphertext segments encrypted with the repeated keystream and cancel the keystream completely.
The vulnerability therefore does not need to “break GOST” or recover the encryption key. The cryptographic primitive can remain mathematically sound while the surrounding mode implementation destroys confidentiality.
Bouncy Castle CVE-2025-14813 vendor advisory
CVE-2025-14813 at a Glance
| שדה | פרטים |
|---|---|
| CVE | CVE-2025-14813 |
| מוצר | Bouncy Castle BC-JAVA |
| רכיב | bcprov core |
| Vulnerable class | G3413CTRBlockCipher |
| Mode | GOST R 34.13-2015 CTR |
| Primary weakness | Counter wrap and keystream reuse |
| CWE | CWE-327, Use of a Broken or Risky Cryptographic Algorithm |
| Affected | BC 1.59–1.80.1, 1.81.0, 1.82–1.83 |
| Fixed | 1.80.2, 1.81.1, 1.84 |
| CVSS v4 | 9.3 Critical |
| Primary security impact | Loss of confidentiality |
| Published | April 15, 2026 |
| Public exploitation evidence | No confirmed exploitation indicated by current CISA enrichment |
| CISA KEV | Not currently listed in the sources reviewed |
The CVE record credits the XlabAI Team of Tencent Xuanwu Lab, the Atuin Automated Vulnerability Discovery Engine, Lili Tang, Guannan Wang, and Guancheng Li with finding the issue. (OpenCVE)
GitHub’s reviewed advisory assigns the vulnerability a Critical rating of 9.3 and describes the security consequence explicitly as keystream reuse after the GOST CTR counter wraps. (GitHub)
What Is Bouncy Castle GOST CTR?
Bouncy Castle is one of the most widely deployed third-party cryptographic libraries in the Java ecosystem. Its bcprov provider contains implementations of symmetric encryption, public-key algorithms, certificates, signatures, key derivation mechanisms, post-quantum cryptography, and numerous national and international cryptographic standards.
Among those implementations is:
org.bouncycastle.crypto.modes.G3413CTRBlockCipher
The class implements the GOST 3412/3413 CTR mode known as GCTR. Bouncy Castle’s current API documentation still identifies G3413CTRBlockCipher as the implementation of the GOST 3412 2015 CTR counter mode. (Bouncy Castle Downloads)
GOST R 34.12-2015 defines block ciphers including Kuznyechik and Magma, while GOST R 34.13-2015 specifies modes of operation such as CTR.
The important point for CVE-2025-14813 is not a weakness in the underlying GOST block cipher.
The weakness exists in the state-management logic surrounding it.
CTR mode converts a block cipher into something conceptually similar to a stream cipher. Rather than encrypting the plaintext directly, the cipher encrypts successive counter values:
K0 = E(key, counter0)
K1 = E(key, counter1)
K2 = E(key, counter2)
...
Each generated value becomes keystream.
Encryption is then:
C0 = P0 XOR K0
C1 = P1 XOR K1
C2 = P2 XOR K2
Decryption performs the same XOR:
P0 = C0 XOR K0
CTR works because every counter value produces a different pseudorandom keystream block.
But that requirement is absolute.
If:
counter0 == counter256
then:
K0 == K256
And once that happens, CTR’s confidentiality guarantees begin collapsing.

The Root Cause Inside G3413CTRBlockCipher
The vulnerable Bouncy Castle 1.83 source makes the problem unusually easy to see.
During initialization, Bouncy Castle constructs the CTR state from the supplied IV and a zeroed counter portion. In the vulnerable source, an IV is required to occupy half of the underlying block size:
IV = Arrays.clone(ivParam.getIV());
if (IV.length != blockSize / 2)
{
throw new IllegalArgumentException(
"Parameter IV length must be == blockSize/2"
);
}
System.arraycopy(IV, 0, CTR, 0, IV.length);
for (int i = IV.length; i < blockSize; i++)
{
CTR[i] = 0;
}
So conceptually the state looks like:
+----------------------+----------------------+
| IV | COUNTER |
+----------------------+----------------------+
The right half is supposed to function as a counter.
Yet when the implementation advances the counter, vulnerable releases contain:
private void generateCRT()
{
CTR[CTR.length - 1]++;
}
That is the heart of CVE-2025-14813. The historical Bouncy Castle 1.83 source shows exactly this implementation. (GitHub)
Only one byte is incremented.
No carry propagates to the previous byte.
An expected sequence resembles:
0000000000000000
0000000000000001
0000000000000002
...
00000000000000ff
0000000000000100
0000000000000101
...
The vulnerable sequence behaves like:
0000000000000000
0000000000000001
0000000000000002
...
00000000000000ff
0000000000000000
0000000000000001
...
The effective counter space therefore collapses to only eight bits.
That is the difference between a cryptographic counter and a byte variable.
Why “More Than 255 Blocks” Is Dangerous
Bouncy Castle’s advisory says the implementation cannot correctly process more than 255 blocks. The exact boundary can look slightly confusing because an unsigned byte has 256 possible values: 0x00 through 0xff.
The security-relevant fact is simpler.
Only 256 distinct values can exist in that final byte before it returns to a previously used state.
If we define the initial state as counter zero, then after traversing the one-byte space the next counter value repeats an earlier one.
For a configuration processing 16 bytes of plaintext per counter state, this means the state space corresponds to roughly:
256 × 16 bytes = 4096 bytes
Only about 4 KiB of stream before a counter state starts repeating.
The regression test added by Bouncy Castle’s patch uses GOST3412_2015Engine with a 128-bit s value and explicitly processes hundreds and eventually tens of thousands of blocks to verify that ciphertext no longer repeats prematurely. (GitHub)
This demonstrates why the vulnerability is more serious than the phrase “cannot process large files correctly” might suggest.
A few kilobytes is not large.
Configuration objects, encrypted protocol messages, certificate-related structures, database fields, archives, application payloads, authentication material, and serialized business objects can all exceed such a threshold.
Why CTR Keystream Reuse Breaks Confidentiality
Assume plaintext block P1 is encrypted with keystream K:
C1 = P1 XOR K
Later, because the counter repeats, another plaintext block P2 uses exactly the same keystream:
C2 = P2 XOR K
An attacker observes both ciphertext blocks and computes:
C1 XOR C2
Substitute the equations:
(P1 XOR K) XOR (P2 XOR K)
Because:
K XOR K = 0
the keystream disappears:
C1 XOR C2 = P1 XOR P2
The encryption key has not been recovered.
The block cipher has not been cryptanalytically broken.
But the relationship between the two plaintexts is now exposed.
This is the classic “two-time pad” failure.
CTR keystream reuse turns strong encryption into a structure-leaking XOR problem.
CVE-2025-14813 Can Leak Data Within a Single Ciphertext
One particularly important aspect of CVE-2025-14813 is that applications do not necessarily need to reuse an IV across separate encryption operations to become vulnerable.
Counter repetition can occur inside a single sufficiently long CTR stream.
Suppose vulnerable Bouncy Castle generates:
K0
K1
...
K255
K0
K1
...
Then:
C0 = P0 XOR K0
C256 = P256 XOR K0
An observer can calculate:
C0 XOR C256 = P0 XOR P256
That gives the attacker a relationship between two areas of the same plaintext.
This matters because many real-world encrypted objects contain predictable information.
Examples could include:
JSON punctuation
XML elements
protocol headers
file signatures
known field names
fixed version strings
serialized object headers
timestamps
repeated padding structures
HTTP-like metadata
database record layouts
If one of the two plaintext positions becomes known or can be guessed reliably:
P256 = P0 XOR C0 XOR C256
The attacker can recover the corresponding bytes directly.
The vulnerability therefore does not depend exclusively on a catastrophic operational error such as globally reusing an IV.
The library itself creates internal counter reuse once the effective eight-bit space is exhausted.
A Simple Cryptographic Example
Imagine the attacker knows one repeated plaintext segment is:
"Content-Type: ap"
Suppose this segment was encrypted using keystream K.
At another offset that uses the same counter state:
C_unknown = P_unknown XOR K
The known ciphertext gives:
K = C_known XOR P_known
The attacker can therefore derive:
P_unknown = C_unknown XOR K
or equivalently:
P_unknown =
C_unknown XOR C_known XOR P_known
No brute force is required.
No 256-bit encryption key needs to be guessed.
The attacker exploits state reuse rather than attacking the cipher mathematically.
That distinction is important when evaluating CVE-2025-14813.
The Patch Shows Exactly What Went Wrong
Bouncy Castle’s primary fix changed the counter update mechanism from:
CTR[CTR.length - 1]++;
to logic that propagates overflow through the available counter bytes.
The patch begins at the final byte:
int start = CTR.length - 1;
and increments while handling wrap:
while (++CTR[start] == 0)
{
start--;
if (start == IV.length - 1)
{
throw new IllegalStateException(
"attempt to process too many blocks"
);
}
}
A follow-up patch corrected a one-off error in that implementation by removing an unnecessary additional increment. (GitHub)
Together, these changes address two security requirements.
First, carry now propagates through the complete counter area instead of being trapped inside the last byte.
Second, Bouncy Castle prevents counter overflow from eventually modifying the IV portion of the state.
This is what a CTR implementation should have done from the beginning: treat the counter as a multi-byte integer with a clearly defined maximum range.
The two official commits are available directly from the Bouncy Castle repository:
Primary G3413CTRBlockCipher counter fix
Why Counter Overflow Is a Security Boundary
Developers sometimes treat counter handling as ordinary bookkeeping.
In cryptography, it is part of the security proof.
A secure CTR construction relies on the uniqueness of each block-cipher input under a given key.
Conceptually:
E(K, IV || 0)
E(K, IV || 1)
E(K, IV || 2)
...
Each input must remain distinct.
Once an implementation generates:
E(K, IV || 0)
twice, it generates the same keystream twice.
This is why the counter width is not simply a performance or scalability property.
It establishes how much data can safely be encrypted under one IV/key context.
A correct implementation must either:
- maintain the full permitted counter space, or
- stop encryption before the counter can repeat.
Silently wrapping is the unsafe option.
CVE-2025-14813 did exactly that.
Affected Bouncy Castle Versions
Bouncy Castle’s updated vendor advisory provides a more precise affected-version matrix than the early disclosure.
| Bouncy Castle version | Status |
|---|---|
| Earlier than 1.59 | Not affected by this CVE according to current range |
| 1.59–1.80.1 | פגיע |
| 1.80.2 | Patched backport |
| 1.81.0 | פגיע |
| 1.81.1 | Patched backport |
| 1.82 | פגיע |
| 1.83 | פגיע |
| 1.84 | Fixed |
| Later maintained releases | Include the fix |
The vendor originally described the vulnerable range more broadly as 1.59 through 1.83, with 1.84 as the fix. Bouncy Castle subsequently published security backports for the 1.80 and 1.81 lines and updated the advisory to identify 1.80.2 and 1.81.1 as fixed versions. (GitHub)
The Bouncy Castle download infrastructure also explicitly identifies 1.80.2 as a security patch release for CVEs fixed in 1.84 and similarly provides 1.81.1.
Bouncy Castle Java 1.84 was publicly announced in May 2026 and included CVE-2025-14813 among several fixed security issues. (Bouncycastle)
CVSS 9.3 Does Not Mean Remote Code Execution
CVE-2025-14813 carries a CVSS v4 base score of 9.3 Critical in the CNA data.
Its vector is:
CVSS:4.0/AV:L/AC:L/AT:N/PR:N/UI:N/
VC:H/VI:H/VA:N/SC:H/SI:H/SA:N
Other scoring systems have represented the flaw differently. For example, CVSS v3.1 scoring commonly appears as 7.5 High with a confidentiality-focused impact profile.
This discrepancy is a useful reminder that CVSS scores should not replace technical analysis.
CVE-2025-14813 is not:
unauthenticated RCE
memory corruption
command injection
deserialization RCE
arbitrary file write
network authentication bypass
Instead, it is a cryptographic implementation failure.
The dangerous outcome is that data an application believes is encrypted may no longer possess the confidentiality properties developers assumed.
Whether that becomes catastrophic depends on how Bouncy Castle GOST CTR is used.
What Does an Attacker Actually Need?
A practical attacker generally needs several conditions.
The target application must use a vulnerable Bouncy Castle version.
Simply finding:
bcprov-jdk18on-1.83.jar
on a server does not prove exploitability.
The application must also exercise:
G3413CTRBlockCipher
or an affected path that ultimately uses that implementation.
Then encryption needs to process enough stream segments for the counter value to cycle.
Finally, an attacker must obtain useful ciphertext.
This could occur through stored encrypted objects, network traffic, encrypted exports, application APIs, backup data, files, or other observable encrypted material.
The CVE does not automatically create a network endpoint.
It compromises a cryptographic guarantee inside applications that already expose relevant encrypted data.
Realistic Attack Scenario One: Encrypted Structured Records
Consider an enterprise Java service encrypting structured records using GOST CTR.
A record might conceptually contain:
{
"type": "payment_record",
"version": "3",
"account": "....",
"timestamp": "....",
"authorization": "....",
"payload": "...."
}
Much of the structure is predictable.
An attacker obtains encrypted records from storage but does not possess the key.
Ordinarily that should not expose meaningful plaintext.
But if the vulnerable Bouncy Castle implementation causes the same keystream position to repeat inside the record, predictable JSON syntax and field names can provide known plaintext at one location.
The corresponding keystream can then reveal data encrypted at the repeated counter position.
The attacker does not need to derive the master key.
They exploit relationships created by keystream reuse.
Realistic Attack Scenario Two: Known Protocol Structures
Many binary protocols begin with predictable structures.
A message may include:
magic bytes
version
message type
length
reserved values
fixed identifiers
If those bytes occupy a position whose keystream later repeats, the known header effectively exposes the keystream for the corresponding later position.
The attacker can calculate:
keystream = ciphertext_header XOR known_header
and apply it to:
plaintext_later =
ciphertext_later XOR keystream
The same principle applies to common file formats.
Knowledge of a file signature or predictable prefix can provide a crib against another block protected by the repeated keystream.
Realistic Attack Scenario Three: Repeated Business Templates
Enterprise software frequently serializes data using templates.
A plaintext might repeatedly contain phrases such as:
"customer_id"
"transaction_id"
"status"
"created_at"
"certificate"
"signature"
"metadata"
Repeated counter states plus repeated plaintext structure make statistical and crib-based recovery significantly easier.
An attacker may not immediately recover every encrypted byte.
But cryptographic confidentiality is supposed to prevent attackers from obtaining precisely these relationships in the first place.
The existence of exploitable partial leakage is sufficient to invalidate the security assumption.
What CVE-2025-14813 Does Not Automatically Allow
Security teams should also avoid overstating the vulnerability.
The bug does not mean that anyone who encounters a Bouncy Castle application can instantly decrypt every GOST-encrypted object.
An attacker still needs relevant ciphertext and suitable reuse conditions.
The bug also does not directly reveal the secret key.
Recovering:
K_stream
for one counter position is not the same as recovering:
K_master
Nor does the CVE automatically provide authenticity or message forgery capabilities equivalent to defeating a secure authenticated-encryption design.
CTR encryption alone is inherently malleable unless combined with authentication, but the exact integrity consequences depend on the surrounding protocol.
The clearest direct impact of CVE-2025-14813 is therefore loss of confidentiality through repeated CTR keystream.
Is CVE-2025-14813 Being Exploited in the Wild?
As of the sources reviewed for this article, there is no strong evidence that CVE-2025-14813 is undergoing widespread real-world exploitation.
CISA’s ADP enrichment associated with the CVE records:
Exploitation: none
Automatable: no
Technical Impact: total
The same current vulnerability data shows an extremely low EPSS signal. Tenable reported an EPSS value around 0.00009 in its most recently indexed data, while other aggregators describe it as below 1%. (OpenCVE)
Current sources also do not place CVE-2025-14813 in the CISA Known Exploited Vulnerabilities catalog. (NotCVE)
That does לא make patching unnecessary.
EPSS answers a different question from CVSS.
CVSS describes potential technical severity.
EPSS estimates observed or predicted exploitation likelihood.
A cryptographic flaw can have a low exploitation probability while remaining unacceptable in systems where confidentiality matters.
For organizations that actually use Bouncy Castle GOST CTR, dependency reachability should outweigh generic threat popularity.
How to Check Whether You Have an Affected Bouncy Castle Version
For Maven projects, start with:
mvn dependency:tree -Dincludes=org.bouncycastle
Look for dependencies such as:
org.bouncycastle:bcprov-jdk18on
org.bouncycastle:bcprov-jdk15to18
org.bouncycastle:bcprov-jdk14
Then verify the resolved version.
A vulnerable dependency could appear directly:
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk18on</artifactId>
<version>1.83</version>
</dependency>
Or transitively through another library.
For Gradle:
./gradlew dependencies | grep -i bouncycastle
For a more targeted dependency inspection:
./gradlew dependencyInsight \
--dependency bcprov \
--configuration runtimeClasspath
Containerized Java applications can also be searched for Bouncy Castle JARs:
find / -type f \
\( -name "bcprov*.jar" -o -name "bcpkix*.jar" \) \
2>/dev/null
But version detection alone is only the first phase.
Reachability Matters More Than Dependency Presence
A security scanner may produce:
CVE-2025-14813
org.bouncycastle:bcprov-jdk18on:1.83
CRITICAL
That is useful, but incomplete.
Now determine whether the vulnerable class is reachable.
Search the application source:
grep -R "G3413CTRBlockCipher" \
--include="*.java" \
--include="*.kt" .
Also search for GOST-related configuration:
grep -R -iE \
"GOST|G3413|GCTR|GOST3412|Kuznyechik|Magma" \
src/
Applications frequently hide cryptographic construction behind wrappers, factories, or configuration layers, so direct class references are not guaranteed.
Useful investigation targets include:
Cipher factories
CryptoProvider wrappers
custom KeyStore code
encryption services
document encryption
database field encryption
protocol libraries
certificate utilities
legacy interoperability modules
government or regional cryptographic compatibility layers
The most useful classification is therefore not:
Vulnerable library present: yes/no
but:
Vulnerable version present?
↓
Vulnerable class reachable?
↓
GOST CTR actually selected?
↓
Encrypted stream long enough?
↓
Ciphertext exposed to attacker?
This transforms a dependency alert into an exposure assessment.
Safe Local Verification of CVE-2025-14813
A local regression test can make the vulnerability visible without attacking any external system.
The idea is simple:
- initialize vulnerable
G3413CTRBlockCipher; - encrypt hundreds of identical plaintext blocks;
- compare ciphertext generated before and after the one-byte counter cycles.
For educational and defensive testing, a simplified Java regression test can look like this:
import java.util.Arrays;
import org.bouncycastle.crypto.StreamBlockCipher;
import org.bouncycastle.crypto.engines.GOST3412_2015Engine;
import org.bouncycastle.crypto.modes.G3413CTRBlockCipher;
import org.bouncycastle.crypto.params.KeyParameter;
import org.bouncycastle.crypto.params.ParametersWithIV;
public class GostCtrRegressionTest {
public static void main(String[] args) {
byte[] key = hex(
"8899aabbccddeeff0011223344556677" +
"fedcba98765432100123456789abcdef"
);
byte[] iv = hex("0001020304050607");
StreamBlockCipher ctr =
new G3413CTRBlockCipher(
new GOST3412_2015Engine(),
128
);
ctr.init(
true,
new ParametersWithIV(
new KeyParameter(key),
iv
)
);
byte[] plaintextBlock = new byte[16];
byte[][] ciphertext = new byte[257][16];
for (int i = 0; i < ciphertext.length; i++) {
ctr.processBytes(
plaintextBlock,
0,
plaintextBlock.length,
ciphertext[i],
0
);
}
System.out.println(
"First and post-cycle block equal: " +
Arrays.equals(ciphertext[0], ciphertext[256])
);
}
private static byte[] hex(String s) {
byte[] out = new byte[s.length() / 2];
for (int i = 0; i < out.length; i++) {
out[i] = (byte) Integer.parseInt(
s.substring(i * 2, i * 2 + 2),
16
);
}
return out;
}
}
The exact block boundary reported by vendor materials is conventionally described as the implementation failing beyond 255 blocks. What matters for regression testing is that an affected implementation only has an eight-bit effective counter space and therefore eventually produces a previously used CTR state.
The official Bouncy Castle patch added very similar tests using GOST3412_2015Engine, repeatedly encrypting identical blocks and explicitly failing if ciphertext repeats. (GitHub)
Run regression tests only against controlled local builds and test keys.
Testing the Cryptographic Property Directly
A generic test does not even need to know the encryption key once ciphertext has been generated.
For blocks i ו j:
def xor_bytes(a, b):
return bytes(x ^ y for x, y in zip(a, b))
relationship = xor_bytes(ciphertext_i, ciphertext_j)
If the same keystream was used:
relationship = plaintext_i XOR plaintext_j
For identical plaintext:
plaintext_i == plaintext_j
therefore:
plaintext_i XOR plaintext_j == 0
which implies:
ciphertext_i == ciphertext_j
That is why encrypting a long sequence of identical blocks is an effective regression test for premature keystream repetition.
It turns a subtle cryptographic state bug into a directly observable invariant.
The Bouncy Castle Fix
The safest remediation is straightforward: upgrade.
Bouncy Castle currently recognizes these fixed releases:
BC 1.80.2
BC 1.81.1
BC 1.84
for their corresponding vulnerable branches. (GitHub)
Organizations running 1.82 or 1.83 should move to at least 1.84 rather than attempting to locally patch G3413CTRBlockCipher.
Bouncy Castle’s official 1.84 announcement explicitly lists CVE-2025-14813 among the resolved vulnerabilities. (Bouncycastle)
If application compatibility allows it, moving to a currently supported newer Bouncy Castle release is generally preferable to targeting only the minimum fixed version.
The official Java download page now exposes newer 1.85-series releases in addition to the 1.84 security baseline. (Bouncycastle)

Example Maven Remediation
A vulnerable dependency might look like:
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk18on</artifactId>
<version>1.83</version>
</dependency>
The minimum mainstream correction would be:
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk18on</artifactId>
<version>1.84</version>
</dependency>
If your application is maintained against a newer supported Bouncy Castle line, use the release approved by your compatibility and security testing instead of automatically stopping at 1.84.
After updating, verify the dependency that actually reaches runtime:
mvn dependency:tree -Dincludes=org.bouncycastle
A common failure mode is updating one declared dependency while another component continues pulling an older bcprov transitively.
Do Not Assume Updating bcpkix Automatically Updates bcprov
Bouncy Castle applications often depend on several related artifacts, for example:
bcprov
bcpkix
bcutil
bctls
bcpg
Security teams should inspect the fully resolved dependency graph rather than the pom.xml alone.
A project may declare a patched package while dependency management, shading, an application server, or a vendor product still supplies an older BC provider.
Useful checks include:
mvn dependency:tree
and:
jar tf application.jar | grep -i bouncycastle
For Spring Boot fat JARs:
jar tf app.jar | grep "bcprov"
For container images:
find /app /opt /usr/share \
-type f \
-iname "bcprov*.jar" \
2>/dev/null
The objective is to confirm the version the JVM loads at runtime.
Transitive Dependencies Are a Major Exposure Problem
CVE-2025-14813 illustrates a recurring cryptographic supply-chain issue.
An engineering team may never explicitly choose Bouncy Castle GOST CTR.
Instead:
Application
↓
Framework
↓
Security component
↓
Bouncy Castle
A vulnerable bcprov release enters the application indirectly.
Even more importantly, the team may not know whether some dependency invokes GOST-related functionality under a specific configuration.
Software composition analysis can identify the vulnerable package, but code reachability analysis and runtime inspection determine whether the relevant cryptographic path is actually exercised.
Organizations with strict confidentiality requirements should maintain a cryptographic inventory alongside their ordinary SBOM.
Such an inventory should answer:
Which cryptographic providers are present?
Which algorithms are enabled?
Which modes are actually used?
Which keys protect which data?
Which code path performs encryption?
What IV or nonce policy is implemented?
What are the maximum message sizes?
Are encryption and authentication combined correctly?
Those questions are more valuable than simply knowing that bcprov.jar exists.
What About Previously Encrypted Data?
Upgrading Bouncy Castle prevents future encryption from using the vulnerable counter implementation.
It does not retroactively repair ciphertext already generated by a vulnerable release.
This distinction matters.
If historical ciphertext was produced using G3413CTRBlockCipher and exceeded the vulnerable counter range, some cryptographic relationships may already have been exposed.
Security teams should therefore identify:
when the vulnerable version entered production
which applications used GOST CTR
which keys were active
which encrypted objects exceeded the threshold
where ciphertext was stored
who could access that ciphertext
whether ciphertext traversed observable networks
Depending on the sensitivity of the data, remediation may require more than replacing a JAR.
It can include:
decrypting affected historical data
re-encrypting it with a patched implementation
rotating encryption keys
rotating IV-generation state
reviewing backups
reviewing data exports
reviewing replicas
reviewing archives
Key rotation alone does not erase ciphertext already obtained by an attacker.
If the old encrypted material remains accessible, the historical cryptographic weakness remains relevant.
Should You Rotate Keys?
A Bouncy Castle upgrade is mandatory for the implementation problem.
Whether key rotation is necessary depends on exposure.
The vulnerability does not directly recover the underlying master key, so key compromise is not automatic.
However, rotation becomes more reasonable when:
vulnerable CTR encryption was definitely used
sensitive long plaintext was encrypted
ciphertext was externally accessible
known plaintext was likely available
an attacker may have retained ciphertext
the affected key remains active
For high-value environments, treating affected keys as potentially associated with compromised confidentiality is often safer than assuming no one exploited the historical ciphertext.
Why Authenticated Encryption Still Matters
Even after fixing CVE-2025-14813, engineering teams should review whether CTR mode is appropriate for new designs.
CTR supplies encryption.
By itself, it does not authenticate ciphertext.
A raw CTR construction therefore does not inherently protect against attacker modification.
Modern designs frequently prefer authenticated encryption with associated data, or another construction that combines confidentiality and integrity under a carefully defined protocol.
That does not mean every GOST-based system can simply replace its cryptographic mode overnight. Interoperability, regulation, legacy systems, hardware requirements, and protocol specifications can constrain algorithm choices.
But where architects control the protocol, cryptographic agility should be considered.
A safe migration plan is stronger than simply replacing one vulnerable library version while retaining an unnecessarily fragile protocol design forever.
Why IV Uniqueness Does Not Fix CVE-2025-14813
CTR security normally depends heavily on avoiding nonce or IV reuse.
That can create a misleading remediation idea:
“We’ll just generate better IVs.”
Better IV management is good practice, but it does not correct this bug.
CVE-2025-14813 causes the counter state to repeat inside one encryption operation.
Even with a perfectly random and globally unique IV:
IV = unique
the vulnerable implementation eventually creates:
IV || counter_x
again because only the lowest byte changes.
So:
better IV generation != patch
The implementation itself must be upgraded.
Why Limiting Message Size Is Not a Sufficient Long-Term Fix
Another possible workaround is:
Never encrypt more than 255 blocks.
In theory, enforcing a strict bound below the repetition point can prevent the vulnerable state from being reached.
In practice, this is a fragile mitigation.
Applications evolve.
Payload sizes change.
Serialization formats gain fields.
Compression behavior changes.
Data may be streamed rather than buffered.
Future developers may remove or bypass the length check.
And multiple wrappers can make the relationship between application-level “message length” and cryptographic segment count unclear.
For that reason, a message-size restriction should only be treated as temporary risk reduction when an immediate dependency update is impossible.
The durable fix is using a patched Bouncy Castle implementation.
How to Validate the Upgrade
After upgrading, run three levels of validation.
First, confirm dependency resolution:
mvn dependency:tree -Dincludes=org.bouncycastle
Second, confirm no vulnerable JAR remains in the deployment artifact:
find . -iname "bcprov*.jar"
Third, repeat the regression test.
A patched build should no longer repeat ciphertext merely because the lowest counter byte has wrapped.
For applications with official GOST test vectors, also run interoperability tests against those vectors.
Cryptographic patches should be validated for:
correct encryption
correct decryption
interoperability
counter carry
streaming behavior
partial blocks
reset behavior
IV handling
large-message behavior
This is especially important because the Bouncy Castle fix itself required a follow-up commit to correct an off-by-one problem in the first counter refactor. (GitHub)
That history demonstrates why regression coverage around boundary conditions matters.
A Useful Security Test for Every CTR Implementation
CVE-2025-14813 suggests a general test that cryptographic libraries should apply to every CTR-like mode.
Generate many blocks under one IV.
Record every block-cipher input or resulting keystream.
Assert uniqueness.
Conceptually:
seen = set()
for counter in generated_counter_values:
assert counter not in seen
seen.add(counter)
At counter boundaries, explicitly test carry:
000000ff
↓
00000100
not:
000000ff
↓
00000000
Tests should exercise boundaries such as:
0xff
0xffff
0xffffff
0xffffffff
where applicable.
Cryptographic bugs disproportionately live in these boundary transitions because ordinary test vectors tend to encrypt only short messages.
A four-block official vector can verify algorithm compatibility while never revealing a counter bug that appears hundreds of blocks later.
Why Traditional Test Vectors Can Miss This Vulnerability
Cryptographic implementations are frequently tested against known-answer vectors.
לדוגמה:
given key K
given IV V
given plaintext P
expect ciphertext C
If the test encrypts only several blocks, vulnerable and correct implementations may produce identical output.
The defect exists outside the tested range.
For CVE-2025-14813:
block 0 correct
block 1 correct
block 2 correct
...
early blocks correct
...
counter wrap security failure
This is why conformance testing alone is insufficient.
Security testing needs properties.
דוגמאות לכך כוללות:
Counter values never repeat.
Different counter values produce independent keystream.
Counter carry works across byte boundaries.
Maximum allowed message size is enforced.
IV state is not overwritten.
Reset restores expected state.
Property-oriented cryptographic testing could have exposed the eight-bit counter much earlier.
Why This Bug Is Easy to Miss in Code Review
Consider again:
CTR[CTR.length - 1]++;
At first glance, the line looks reasonable.
CTR mode needs a counter.
The final byte is part of the counter.
Incrementing it seems logical.
The subtle issue is that the code implements:
increment byte
rather than:
increment multi-byte integer
A reviewer focused on memory safety, Java exceptions, object lifecycle, or standard test vectors may not immediately recognize the cryptographic consequence.
This type of bug demonstrates why cryptographic code review requires reasoning about invariants rather than syntax.
The reviewer should ask:
What mathematical state does this byte array represent?
How large is the allowed state space?
What happens on overflow?
Can the same block-cipher input ever occur twice?
Which bytes belong to the nonce?
Which bytes belong to the counter?
What is the maximum safe message length?
Those questions expose problems ordinary code review may miss.
Detection Guidance for Defenders
There is no universal network signature for CVE-2025-14813.
It is not a malformed HTTP request or exploit packet.
Detection therefore begins with software inventory.
חפשו:
Bouncy Castle bcprov < fixed version
+
actual G3413CTRBlockCipher usage
Static source searches can include:
G3413CTRBlockCipher
GOST3412_2015Engine
GOST3412
GOST
GCTR
Runtime telemetry can also help.
Applications can log cryptographic metadata without logging keys or plaintext:
provider
algorithm
mode
library version
payload length
operation identifier
A safe audit event might resemble:
{
"crypto_provider": "BC",
"bc_version": "1.83",
"mode": "GCTR",
"payload_bytes": 16384
}
Such records allow defenders to identify historical operations likely to have exceeded the vulnerable counter range without exposing secret material.
Do לא log:
encryption keys
raw plaintext
passwords
private keys
complete sensitive ciphertext unnecessarily
The goal is cryptographic observability, not creating a new data leak.
Software Composition Analysis Is Necessary but Not Sufficient
SCA tools should identify vulnerable Bouncy Castle versions.
But an ideal vulnerability-management workflow should combine:
package vulnerability
+
reachability
+
runtime configuration
+
asset sensitivity
+
ciphertext exposure
Two systems containing Bouncy Castle 1.83 can have dramatically different risk.
System A:
bcprov 1.83 installed
G3413CTRBlockCipher never referenced
System B:
bcprov 1.83
G3413CTRBlockCipher active
large sensitive documents encrypted
ciphertexts externally downloadable
The CVE identifier is the same.
Operational risk is not.
This is particularly important for cryptographic vulnerabilities because package-level scanners often cannot determine which algorithm is selected at runtime.
Dependency Inventory Commands
For Maven:
mvn dependency:tree | grep -i bouncycastle
For Gradle:
./gradlew dependencies | grep -i bouncycastle
For local repositories:
find ~/.m2/repository/org/bouncycastle \
-type f \
-name "*.jar"
For Docker:
docker run --rm IMAGE_NAME \
sh -c 'find / -iname "bcprov*.jar" 2>/dev/null'
For Kubernetes workloads, defenders can inspect application images or SBOM output rather than searching live containers wherever possible.
If a vulnerable release is found, trace the dependency owner before changing it blindly.
Cryptographic libraries can have compatibility implications, particularly when used by:
application servers
identity products
PKI stacks
VPN systems
enterprise middleware
document-signing systems
smart-card integrations
regulated cryptographic protocols
Upgrade testing should therefore include the application functionality that depends on Bouncy Castle.
CVE-2025-14813 and Enterprise Java Products
Bouncy Castle is commonly embedded inside larger software distributions rather than installed directly by end users.
That means an organization may be affected even if developers never added Bouncy Castle themselves.
Current vulnerability databases already associate CVE-2025-14813 remediation activity with downstream enterprise products and Linux distributions. Red Hat advisories, Amazon Linux updates, and other vendor responses began appearing after disclosure. (Explore AWS)
For vendor-managed software, replacing bcprov.jar manually may be unsupported and can create compatibility problems.
The correct workflow is:
identify upstream product
↓
check vendor advisory
↓
install supported product update
↓
verify bundled Bouncy Castle version
Do not assume a manually swapped cryptographic provider is equivalent to a vendor-qualified patch.
Why the Bug Survived from BC 1.59
The vulnerable range begins at Bouncy Castle 1.59, meaning the implementation existed across many releases before the defect was publicly addressed.
Historical Bouncy Castle 1.59 API documentation already contains the G3413CTRBlockCipher class implementing the GOST 3412 2015 CTR mode. (Javadoc)
That longevity is a useful security lesson.
A mature cryptographic library is not equivalent to bug-free cryptographic code.
Cryptographic software combines:
complex specifications
edge-case arithmetic
large state spaces
interoperability constraints
legacy APIs
multiple platforms
performance optimizations
A tiny incorrect assumption about a counter can persist for years because ordinary application workloads may continue encrypting and decrypting apparently successfully.
The encrypted output still looks random.
No crash occurs.
No exception appears.
The cryptographic failure is invisible unless someone specifically tests the security property.
The Difference Between Functional Correctness and Cryptographic Correctness
Suppose an application performs:
cipher.encrypt(data);
cipher.decrypt(ciphertext);
and receives the original data.
A developer might conclude:
Encryption works.
But with CTR keystream repetition, encryption and decryption can remain perfectly symmetrical.
If encryption uses the wrong repeated keystream and decryption uses the same wrong repeated keystream:
decrypt(encrypt(P)) == P
can still be true.
That means ordinary round-trip tests may pass.
Yet confidentiality is broken.
This is one of the central lessons from CVE-2025-14813:
A cipher can decrypt its own ciphertext correctly while still being cryptographically insecure.
Security properties must therefore be tested independently of functional correctness.
Security Checklist for CVE-2025-14813
For teams responding to the vulnerability, the practical workflow is:
| בדוק | Question |
|---|---|
| תלות | Is an affected BC-JAVA release present? |
| Reachability | Is G3413CTRBlockCipher reachable? |
| Runtime | Is GOST CTR actually used? |
| Message size | Can encrypted streams cross the vulnerable counter range? |
| Exposure | Can attackers obtain affected ciphertext? |
| Data sensitivity | What information was protected? |
| Historical data | Was ciphertext previously generated using the vulnerable release? |
| Patch | Has BC been upgraded to a fixed version? |
| אימות | Has counter-repetition regression testing been performed? |
| Recovery | Is historical re-encryption or key rotation appropriate? |
The highest-priority case is not simply “BC 1.83 exists.”
It is:
BC 1.83
+
G3413CTRBlockCipher reachable
+
large encrypted payloads
+
sensitive information
+
attacker-visible ciphertext
That combination converts a package vulnerability into a meaningful confidentiality risk.
Frequently Asked Questions
What is CVE-2025-14813?
CVE-2025-14813 is a vulnerability in Bouncy Castle BC-JAVA’s G3413CTRBlockCipher, which implements GOST R 34.13-2015 CTR mode. Vulnerable versions increment only the final byte of the CTR state, causing counter values and therefore encryption keystream to repeat.
What is the main Bouncy Castle GOST CTR vulnerability?
The effective counter is only eight bits instead of using the intended multi-byte counter area. After that limited state space cycles, previously used keystream is generated again.
Which Bouncy Castle versions are vulnerable?
The current vendor advisory lists BC 1.59 through 1.80.1, BC 1.81.0, and BC 1.82 through 1.83 as vulnerable. Fixed versions include 1.80.2, 1.81.1, and 1.84. (GitHub)
Is Bouncy Castle 1.84 affected?
No. Bouncy Castle explicitly lists 1.84 as a fixed version and included the CVE in the security fixes announced for the release. (Bouncycastle)
Is Bouncy Castle 1.83 vulnerable?
Yes.
Is Bouncy Castle 1.81 vulnerable?
The original 1.81 release is affected, while the security backport 1.81.1 is fixed. (GitHub)
Is CVE-2025-14813 remote code execution?
No. It is a cryptographic confidentiality vulnerability, not an RCE vulnerability.
Does CVE-2025-14813 recover the encryption key?
Not directly. Keystream reuse exposes relationships between plaintexts and can permit plaintext recovery where known or predictable plaintext exists, but that is not equivalent to deriving the underlying master key.
Does an attacker need the same IV across multiple messages?
Not necessarily. The flawed counter can repeat inside one sufficiently long encryption stream.
Why is CTR keystream reuse dangerous?
Because:
C1 = P1 XOR K
C2 = P2 XOR K
therefore:
C1 XOR C2 = P1 XOR P2
The encryption keystream cancels out.
Is CVE-2025-14813 actively exploited?
Current public sources reviewed here do not show confirmed widespread exploitation, CISA enrichment marks exploitation as none, and EPSS remains extremely low. (OpenCVE)
Should I still patch it?
Yes, if an affected Bouncy Castle version is present. Organizations actually using GOST CTR should give it particular priority because the vulnerability invalidates an expected cryptographic confidentiality guarantee.
Final Assessment
CVE-2025-14813 is a useful example of how cryptographic systems fail in practice.
Nothing had to defeat the underlying block cipher.
There was no breakthrough against GOST mathematics.
There was no need to brute-force a 256-bit key.
The failure came from one line of state-management code:
CTR[CTR.length - 1]++;
A multi-byte counter became a one-byte counter.
A one-byte counter eventually repeated.
A repeated counter generated repeated keystream.
And repeated CTR keystream destroys the confidentiality property encryption is supposed to provide.
Bouncy Castle corrected the problem by implementing proper counter carry and preventing the counter from overflowing into the IV region. The official vendor advisory now identifies 1.80.2, 1.81.1, and 1.84 as fixed versions for the affected release branches. (GitHub)
For defenders, the correct response is not panic over the 9.3 CVSS score, nor dismissal because current exploitation signals are low.
It is reachability analysis.
Determine whether a vulnerable Bouncy Castle version exists. Determine whether G3413CTRBlockCipher is actually used. Determine whether sensitive messages crossed the vulnerable counter range. Determine who could obtain the resulting ciphertext. Upgrade to a fixed release, validate the counter behavior, and evaluate whether historical ciphertext requires re-encryption or key-management action.
For library authors and security researchers, the deeper lesson is even broader: cryptographic correctness depends on state transitions as much as cryptographic primitives.
Counter width, carry propagation, nonce uniqueness, overflow handling, maximum message length, and boundary testing are not implementation details.
They are part of the security model itself.
Primary technical references: the Bouncy Castle CVE-2025-14813 advisory, ה primary counter refactoring patch, ה follow-up correctness patch, ה GitHub reviewed advisory GHSA-574f-3g2m-x479, וה Bouncy Castle Java 1.84 release announcement provide the authoritative basis for the affected-version and remediation details above.

