Summary
The OpenSSH test suites embed 36 well-formed private keys as inline PEM literals. Because they are syntactically valid keys shipped inside the published -sources.jar artifacts, automated secret scanners (GitGuardian, gitleaks, GitHub secret scanning, Trufflehog) flag them as leaked credentials in any organisation that ingests or vendors BouncyCastle sources.
These keys secure nothing — they are known-answer fixtures for encoder/decoder round-trips, intentionally public, and the passphrase for the encrypted variants (JcaTest!9) is a literal in the same file. The ask is narrow: make that intent machine-evident, so downstream teams can dismiss the finding in seconds instead of opening an incident.
Affected files
| File |
Private-key PEM blocks |
core/src/test/java/org/bouncycastle/crypto/test/OpenSSHKeyParsingTests.java |
28 |
prov/src/test/java/org/bouncycastle/jce/provider/test/OpenSSHSpecTests.java |
8 |
Covers ssh-rsa (PKCS#1 and openssh-key-v1), ssh-dss, ecdsa-sha2-nistp256 and ssh-ed25519, plus aes256-ctr/bcrypt encrypted variants.
Observed on 1.86-SNAPSHOT. The vectors are long-standing: the 1024-bit RSA key is present in tags r1rv61 and r1rv73, so it has shipped in published source artifacts since 1.61.
Impact
- False-positive incident load. Each vendored copy or fork re-triggers the alert. Triage currently requires reading the test to infer that the key is a fixture.
- No suppression path for consumers. Scanner allowlists are keyed on path or fingerprint; a consumer vendoring
bcprov sources into a monorepo has neither a stable path nor upstream-provided fingerprints.
- Personal identifiers in key comments. Two
openssh-key-v1 vectors carry the originating workstation comment — mark@barnacles and meganwoods@tyche-3265.local (OpenSSHSpecTests.java). These are incidental to the test and needlessly associate named individuals with material that reads as a credential leak.
Non-goals
Spelled out because each is a plausible reflexive response that would cost real effort for no benefit:
- Not a vulnerability report. No key here protects any system, so there is no exposure to assess and no advisory to publish. Please do not route this through the security process.
- Not a request to rotate anything. Rotation presupposes a system the key guards. There is none.
- Not a request to rewrite git history. The material is immutable in already-published Maven Central artifacts, so a
filter-repo would break every clone and fork while retracting nothing.
- Not a request to remove the vectors. They are load-bearing known-answer fixtures; deleting them would reduce coverage. Any change should preserve the assertions.
Proposed remediation
Ordered by cost. Any one materially reduces downstream triage; (1) and (2) are complementary.
-
Declare intent in-file. Add a class-level Javadoc block to both test classes stating that the embedded keys are deliberately published, disposable test vectors that protect no system, with a stable marker string (e.g. NOT-A-SECRET: openssh test vector) that consumers can allowlist by content rather than path.
-
Ship a scanner allowlist. Commit a .gitleaks.toml (and/or .github/secret_scanning.yml) covering the two paths, so contributors and forks inherit the suppression rather than each re-deriving it.
-
Regenerate the two vectors carrying personal identifiers, substituting a neutral comment such as bc-test-vector. This is a mechanical, behaviour-preserving change: it requires recomputing the pinned expectations in OpenSSHSpecTests — the ED25519 base/tail hex either side of the random check-int, and the PKCS#8 encodings asserted for the encrypted keys.
Happy to raise a PR for any combination of the above if the maintainers indicate a preferred direction.
Reproduction
The underlying material is visible without any tooling:
$ git grep -c -E -- '-----BEGIN (RSA|DSA|EC|OPENSSH) PRIVATE KEY-----' -- \
'core/src/test/java/org/bouncycastle/crypto/test/OpenSSHKeyParsingTests.java' \
'prov/src/test/java/org/bouncycastle/jce/provider/test/OpenSSHSpecTests.java'
core/src/test/java/org/bouncycastle/crypto/test/OpenSSHKeyParsingTests.java:28
prov/src/test/java/org/bouncycastle/jce/provider/test/OpenSSHSpecTests.java:8
Each of these is a complete, parseable private key in PEM armour. Scanners that ship a generic private-key rule keyed on the -----BEGIN … PRIVATE KEY----- header — which is the default in gitleaks, Trufflehog and GitHub secret scanning — will match on them; the exact finding count and grouping varies by tool and by whether history is scanned.
Summary
The OpenSSH test suites embed 36 well-formed private keys as inline PEM literals. Because they are syntactically valid keys shipped inside the published
-sources.jarartifacts, automated secret scanners (GitGuardian, gitleaks, GitHub secret scanning, Trufflehog) flag them as leaked credentials in any organisation that ingests or vendors BouncyCastle sources.These keys secure nothing — they are known-answer fixtures for encoder/decoder round-trips, intentionally public, and the passphrase for the encrypted variants (
JcaTest!9) is a literal in the same file. The ask is narrow: make that intent machine-evident, so downstream teams can dismiss the finding in seconds instead of opening an incident.Affected files
core/src/test/java/org/bouncycastle/crypto/test/OpenSSHKeyParsingTests.javaprov/src/test/java/org/bouncycastle/jce/provider/test/OpenSSHSpecTests.javaCovers
ssh-rsa(PKCS#1 andopenssh-key-v1),ssh-dss,ecdsa-sha2-nistp256andssh-ed25519, plusaes256-ctr/bcryptencrypted variants.Observed on
1.86-SNAPSHOT. The vectors are long-standing: the 1024-bit RSA key is present in tagsr1rv61andr1rv73, so it has shipped in published source artifacts since 1.61.Impact
bcprovsources into a monorepo has neither a stable path nor upstream-provided fingerprints.openssh-key-v1vectors carry the originating workstation comment —mark@barnaclesandmeganwoods@tyche-3265.local(OpenSSHSpecTests.java). These are incidental to the test and needlessly associate named individuals with material that reads as a credential leak.Non-goals
Spelled out because each is a plausible reflexive response that would cost real effort for no benefit:
filter-repowould break every clone and fork while retracting nothing.Proposed remediation
Ordered by cost. Any one materially reduces downstream triage; (1) and (2) are complementary.
Declare intent in-file. Add a class-level Javadoc block to both test classes stating that the embedded keys are deliberately published, disposable test vectors that protect no system, with a stable marker string (e.g.
NOT-A-SECRET: openssh test vector) that consumers can allowlist by content rather than path.Ship a scanner allowlist. Commit a
.gitleaks.toml(and/or.github/secret_scanning.yml) covering the two paths, so contributors and forks inherit the suppression rather than each re-deriving it.Regenerate the two vectors carrying personal identifiers, substituting a neutral comment such as
bc-test-vector. This is a mechanical, behaviour-preserving change: it requires recomputing the pinned expectations inOpenSSHSpecTests— the ED25519 base/tail hex either side of the random check-int, and the PKCS#8 encodings asserted for the encrypted keys.Happy to raise a PR for any combination of the above if the maintainers indicate a preferred direction.
Reproduction
The underlying material is visible without any tooling:
Each of these is a complete, parseable private key in PEM armour. Scanners that ship a generic private-key rule keyed on the
-----BEGIN … PRIVATE KEY-----header — which is the default in gitleaks, Trufflehog and GitHub secret scanning — will match on them; the exact finding count and grouping varies by tool and by whether history is scanned.