Skip to content

feat(sdk)!: remove bouncycastle and ayza libraries#367

Merged
mkleene merged 56 commits into
mainfrom
remove-ayza-and-bouncycastle
May 21, 2026
Merged

feat(sdk)!: remove bouncycastle and ayza libraries#367
mkleene merged 56 commits into
mainfrom
remove-ayza-and-bouncycastle

Conversation

@mkleene

@mkleene mkleene commented May 18, 2026

Copy link
Copy Markdown
Contributor

This pull makes this library provider-agnostic. Users can control how cryptography is provided by configuring
their java.security configuration, as we do in the FIPS-profile tests. We add two new maven profiles fips and
non-fips. These profiles are used solely for testing; the library itself is agnostic to which providers are used
for cryptography.

In the non-fips profile we use the normal providers configured on the JVM. The BouncyCastle provider is included
just for some test verification.

In the fips profile we include the BouncyCastle FIPS providers we need at runtime to verify that our library works with a FIPS-compliant setup.

Ayza removal

I believe it's necessary to remove ayza since it loads a BouncyCastleProvider explicitly. Since the FIPS provider
occupies the same namespace loading both providers can't work.

BouncyCastle moved to test-only

Removing BouncyCastle as a compile dependency isn't strictly necessary but it results in much less rework and
eliminates a transitive dependency for consumers.

Remaining FIPS work

We still need to:

  • verify that the JWK libraries we use are FIPS compliant with the correct providers to claim that this
    library can be made FIPS compliant
  • verify that we generate random numbers in a FIPS-compliant way. Because BouncyCastle does not provide a
    FIPS-compliant source of entropy we need to include the SUN provider to get at /dev/rand. This is OK, since
    • we should pick up this provider and use it for all cryptographic operations (but we may want to provide ways to guarantee that a particular provider is used)
    • the FIPS boundary for random numbers starts at the provider. We don't have tools to verify this now, though

The breaking API changes are as follows:

Area Change Related to
SDKBuilder.sslFactory(SSLFactory) Removed. Replaced by sslFactory(SSLSocketFactory) and sslFactory(SSLSocketFactory, X509TrustManager) Ayza removal
SDKBuilder.sslFactoryFromTrustManager(X509TrustManager) New method Ayza removal
ECKeyPair(ECCurve, ECAlgorithm) constructor Removed. Replaced by ECKeyPair(ECCurve) BouncyCastle removal
ECKeyPair.publicKeyFromPem Removed method from public interface since it should not have been exposed and since the types were changing anyway BouncyCastle removal
ECKeyPair.privateKeyFromPem, getPEMPublicKeyFromX509Cert, publicKeyFromECPoint, static compressECPublickey(String) Removed from public API (moved to test-only PemTestUtils) BouncyCastle removal
Dependencies Drops io.github.hakky54:ayza* and the runtime BC dep; adds bc-fips, bcpkix-fips, bctls-fips (via new fips/non-fips Maven profiles) Both

Summary by CodeRabbit

  • New Features

    • FIPS-profile cryptography support and improved TLS trust-material builder (directory/keystore/trust-manager sources).
  • Improvements

    • Migrated crypto to standard Java security APIs for broader compatibility.
    • Added AES-GCM key-generation helper and more robust TLS/trust-manager integration across the SDK.
  • Tests

    • Updated and added tests and test utilities for PEM/EC key handling and TLS/trust behaviors.
  • Chores

    • Updated build/test config and pinned Bouncy Castle FIPS dependencies.

Review Change Stack

mkleene and others added 30 commits May 11, 2026 15:42
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Remove the three io.github.hakky54:ayza* dependencies and replace their
TLS trust-material role with an SDK-owned TrustProvider built on
provider-agnostic JCA APIs (CertificateFactory, KeyStore,
TrustManagerFactory, SSLContext). This works under any registered crypto
provider, including BC-FIPS, and avoids hardcoded provider names.

- Add TrustProvider and package-private CompositeX509ExtendedTrustManager
  for combining JVM default + custom trust material.
- SDKBuilder: replace SSLFactory field with SSLSocketFactory +
  X509TrustManager. sslFactory(SSLFactory) becomes
  sslFactory(SSLSocketFactory); add sslFactory(SSLSocketFactory,
  X509TrustManager) for callers that have a matching trust manager.
  sslFactoryFromDirectory / sslFactoryFromKeyStore signatures and
  semantics are preserved, now backed by TrustProvider internally.
- TokenSource takes SSLSocketFactory directly.
- Command.java --insecure path uses TrustProvider.insecure().
- SDKBuilderTest reworked to drop nl.altindag imports and use
  TrustProvider + standard JCA.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
@github-actions

Copy link
Copy Markdown
Contributor

@github-actions

github-actions Bot commented May 18, 2026

Copy link
Copy Markdown
Contributor

Dependency Review

The following issues were found:
  • ❌ 1 vulnerable package(s)
  • ✅ 0 package(s) with incompatible licenses
  • ✅ 0 package(s) with invalid SPDX license definitions
  • ⚠️ 3 package(s) with unknown licenses.
See the Details below.

Vulnerabilities

pom.xml

NameVersionVulnerabilitySeverity
org.bouncycastle:bc-fips2.1.2Bouncy Castle has a vulnerability in program files gcm128w, gcm512wmoderate

License Issues

sdk/pom.xml

PackageVersionLicenseIssue Type
org.apache.maven.plugins:maven-surefire-pluginNullUnknown License
org.bouncycastle:bc-fipsNullUnknown License
org.bouncycastle:bctls-fipsNullUnknown License
Denied Licenses: GPL-2.0, AGPL-1.0, AGPL-1.0-or-later, AGPL-1.0-only, AGPL-3.0, AGPL-3.0-only, AGPL-3.0-or-later, GPL-1.0, GPL-1.0+, GPL-1.0-only, GPL-1.0-or-later, CNRI-Python-GPL-Compatible, GPL-2.0+, GPL-2.0-only, GPL-2.0-or-later, GPL-2.0-with-GCC-exception, GPL-2.0-with-autoconf-exception, GPL-2.0-with-bison-exception, GPL-2.0-with-classpath-exception, GPL-2.0-with-font-exception, GPL-3.0, GPL-3.0+, GPL-3.0-only, GPL-3.0-or-later, GPL-3.0-with-GCC-exception, GPL-3.0-with-autoconf-exception, LGPL-2.0, LGPL-2.0+, LGPL-2.0-only, LGPL-2.0-or-later, LGPL-2.1, LGPL-2.1+, LGPL-2.1-only, LGPL-2.1-or-later, LGPL-3.0, LGPL-3.0+, LGPL-3.0-only, LGPL-3.0-or-later, LGPLLR, NGPL

OpenSSF Scorecard

PackageVersionScoreDetails
maven/org.bouncycastle:bc-fips 2.1.2 UnknownUnknown
maven/org.bouncycastle:bcpkix-fips 2.1.11 UnknownUnknown
maven/org.bouncycastle:bctls-fips 2.1.23 UnknownUnknown
maven/org.bouncycastle:bctls-jdk18on 1.82 🟢 6.4
Details
CheckScoreReason
Maintained🟢 1030 commit(s) and 2 issue activity found in the last 90 days -- score normalized to 10
Packaging⚠️ -1packaging workflow not detected
Code-Review⚠️ 0Found 0/30 approved changesets -- score normalized to 0
Token-Permissions🟢 9detected GitHub workflow tokens with excessive permissions
Security-Policy🟢 10security policy file detected
CII-Best-Practices⚠️ 0no effort to earn an OpenSSF best practices badge detected
Dangerous-Workflow🟢 10no dangerous workflow patterns detected
License🟢 10license file detected
SAST🟢 10SAST tool detected: CodeQL
Binary-Artifacts⚠️ 0binaries present in source code
Signed-Releases⚠️ -1no releases found
Pinned-Dependencies⚠️ 0dependency not pinned by hash detected -- score normalized to 0
Fuzzing🟢 10project is fuzzed
Branch-Protection⚠️ -1internal error: error during branchesHandler.setup: internal error: some github tokens can't read classic branch protection rules: https://github.com/ossf/scorecard-action/blob/main/docs/authentication/fine-grained-auth-token.md
maven/org.apache.maven.plugins:maven-surefire-plugin UnknownUnknown
maven/org.bouncycastle:bc-fips UnknownUnknown
maven/org.bouncycastle:bcpkix-fips UnknownUnknown
maven/org.bouncycastle:bcpkix-jdk18on UnknownUnknown
maven/org.bouncycastle:bctls-fips UnknownUnknown

Scanned Files

  • pom.xml
  • sdk/pom.xml

@github-actions

Copy link
Copy Markdown
Contributor

X-Test Failure Report

Comment thread sdk/src/main/java/io/opentdf/platform/sdk/TrustProvider.java Dismissed
@github-actions

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
sdk/src/main/java/io/opentdf/platform/sdk/TrustProvider.java (1)

95-99: ⚡ Quick win

Specify minimum TLS version instead of generic "TLS" string to prevent negotiation of deprecated protocols.

The generic SSLContext.getInstance("TLS") (lines 96 and 211) permits negotiation of deprecated TLS 1.0/1.1. While modern JDKs disable these by default via jdk.tls.disabledAlgorithms, relying on JVM configuration is fragile. Since BC-FIPS supports "TLSv1.2" and "TLSv1.3" protocol strings, use one of these instead for stronger guarantees.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@sdk/src/main/java/io/opentdf/platform/sdk/TrustProvider.java` around lines 95
- 99, Replace the generic SSLContext.getInstance("TLS") usages with a specific,
minimum protocol like "TLSv1.2" (or "TLSv1.3" if you require/guarantee support)
to avoid negotiating deprecated TLS 1.0/1.1; update the call in
TrustProvider.fromTrustManager (and the other SSLContext.getInstance("TLS")
occurrence referenced in this review) to use SSLContext.getInstance("TLSv1.2")
and ensure the rest of TrustProvider construction (sslContext.init(...),
returned TrustProvider) remains unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@sdk/src/main/java/io/opentdf/platform/sdk/TrustProvider.java`:
- Around line 95-99: Replace the generic SSLContext.getInstance("TLS") usages
with a specific, minimum protocol like "TLSv1.2" (or "TLSv1.3" if you
require/guarantee support) to avoid negotiating deprecated TLS 1.0/1.1; update
the call in TrustProvider.fromTrustManager (and the other
SSLContext.getInstance("TLS") occurrence referenced in this review) to use
SSLContext.getInstance("TLSv1.2") and ensure the rest of TrustProvider
construction (sslContext.init(...), returned TrustProvider) remains unchanged.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 348f7f1d-b626-4140-961a-d2cc299dfa3f

📥 Commits

Reviewing files that changed from the base of the PR and between fafe689 and 5fdaea0.

📒 Files selected for processing (5)
  • pom.xml
  • sdk/src/main/java/io/opentdf/platform/sdk/AesGcm.java
  • sdk/src/main/java/io/opentdf/platform/sdk/SDKBuilder.java
  • sdk/src/main/java/io/opentdf/platform/sdk/TrustProvider.java
  • sdk/src/test/java/io/opentdf/platform/sdk/ECKeyPairTest.java
🚧 Files skipped from review as they are similar to previous changes (3)
  • pom.xml
  • sdk/src/test/java/io/opentdf/platform/sdk/ECKeyPairTest.java
  • sdk/src/main/java/io/opentdf/platform/sdk/SDKBuilder.java

@github-actions

Copy link
Copy Markdown
Contributor

@marythought marythought left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall — this is a real DX improvement, even with the breaking changes. From the consumer's seat:

  • Dropping ayza is a long-term win. Removes a niche third-party type from the public surface; SSLSocketFactory / X509TrustManager are types every Java dev already knows.
  • The new SDKBuilder method set is well-shaped. sslFactoryFromTrustManager / sslFactoryFromDirectory / sslFactoryFromKeyStore cover the common cases; the bare/paired sslFactory(SSLSocketFactory[, X509TrustManager]) overloads handle the escape hatches; naming is consistent and the javadoc explicitly tells callers to prefer the paired form. Good guidance built into the API.
  • Sensible defaults — fromDirectory / fromKeyStore include JVM default cacerts automatically. The common "my certs plus system trust" case works without customers having to think about composition.
  • The breaking-change table in the PR description is exactly the format consumers need. Old → new in one place.
  • ECKeyPair public-surface tightening is principled. The moved-to-test-only helpers were marked as "should not have been exposed" and would have broken under FIPS anyway. Better to fix now.

Three specific notes as inline comments below.

Comment thread cmdline/src/main/java/io/opentdf/platform/Command.java Outdated
Comment thread sdk/src/main/java/io/opentdf/platform/sdk/ECKeyPair.java
Comment thread sdk/src/main/java/io/opentdf/platform/sdk/TrustProvider.java
@mkleene
mkleene requested a review from marythought May 19, 2026 19:32
@github-actions

Copy link
Copy Markdown
Contributor

X-Test Failure Report

@sonarqubecloud

sonarqubecloud Bot commented May 19, 2026

Copy link
Copy Markdown

@github-actions

Copy link
Copy Markdown
Contributor

@marythought marythought left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-reviewed after the latest commits. Both items from my first review are addressed:

  1. insecureSslFactory() convenience method added to SDKBuilder — clean one-liner for local-dev consumers.
  2. ADR documenting the BouncyCastle/Ayza removal rationale.

CI is green on all test/integration/xtest jobs. The SonarCloud and dependency-review failures look infrastructure-related.

LGTM — nice work on the provider-agnostic JCA migration.

@jentfoo jentfoo left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@mkleene
mkleene merged commit 63b49af into main May 21, 2026
20 of 21 checks passed
@mkleene
mkleene deleted the remove-ayza-and-bouncycastle branch May 21, 2026 21:33
mkleene added a commit that referenced this pull request Jun 22, 2026
🤖 I have created a release *beep* *boop*
---


<details><summary>0.16.0</summary>

##
[0.16.0](v0.15.0...v0.16.0)
(2026-06-22)


### ⚠ BREAKING CHANGES

* **sdk:** remove bouncycastle and ayza libraries
([#367](#367))

### Features

* **sdk:** remove bouncycastle and ayza libraries
([#367](#367))
([63b49af](63b49af))
* verify that BC works with `approved_only` mode set
([#375](#375))
([36fd6cf](36fd6cf))


### Bug Fixes

* **main:** Update bc module sdk version.
([#377](#377))
([9b38f82](9b38f82))
</details>

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).

Co-authored-by: opentdf-automation[bot] <149537512+opentdf-automation[bot]@users.noreply.github.com>
Co-authored-by: Morgan Kleene <mkleene@virtru.com>
sujankota added a commit that referenced this pull request Jun 22, 2026
…Wing, ECDH+ML-KEM) (#368)

Add hybrid post-quantum key wrapping to the Java SDK so TDFs can be
protected against "harvest now, decrypt later" attacks while preserving
classical security guarantees during the PQC transition.
  
  Introduces three new `KeyType` values backed by hybrid KEMs:

  - `HybridXWingKey` (`hpqt:xwing`) — X-Wing (X25519 + ML-KEM-768)
  - `HybridSecp256r1MLKEM768Key` (`hpqt:secp256r1-mlkem768`)
  - `HybridSecp384r1MLKEM1024Key` (`hpqt:secp384r1-mlkem1024`)

When a KAS advertises one of these
algorithms,`TDF.upsertAndGetNewKeyAccess` routes through
`HybridCrypto.wrapDEK`, which performs both a classical ECDH/X25519 key
agreement and an ML-KEM encapsulation, combines the two shared secrets
(HKDF-SHA256 with the standard TDF salt), and uses the result to wrap
the DEK with AES-256-GCM. A new `hybrid-wrapped` key-access type is
emitted; the ephemeral classical public key and ML-KEM ciphertext are
packaged together inside an ASN.1 envelope stored in `wrappedKey`
(rather than the separate `ephemeralPublicKey` field used for EC-wrapped
keys).

New supporting classes: `HybridCrypto`, `HybridNISTKeyPair`,
`XWingKeyPair`, plus unit tests and a full-manifest TDF round-trip test.

  ## Provider-agnostic implementation 

In line with #367's removal of BouncyCastle as a compile dependency,
this PR limits BC usage to the only primitives JDK 11 stdlib cannot
supply — ML-KEM keygen/encap/decap and X-Wing keygen/encap/decap (the
JCA `KEM` API is JDK21+; ML-KEM in stdlib is 24+).

  Everything else is stdlib JCA or an existing SDK helper:

- **ASN.1 envelope** — minimal hand-rolled DER codec for `SEQUENCE { [0]
IMPLICIT OCTET STRING, [1] IMPLICIT OCTET STRING }` with multi-byte
length support. No `org.bouncycastle.asn1.*` imports. - **HKDF** —
delegates to the existing `ECKeyPair.calculateHKDF(salt, secret)` (RFC
5869, empty info, L=32 — what all three algorithms need).
  - **EC keygen / ECDH / curve parameters** — stdlib
    `KeyPairGenerator.getInstance("EC")`,
    `KeyAgreement.getInstance("ECDH")`,
`AlgorithmParameters.getInstance("EC")` with `ECGenParameterSpec`. No
    `BouncyCastleProvider` registration; consumers control providers via
    `java.security` as the ADR intends.

  `bcprov-jdk18on` is declared at compile/runtime scope in the default
  `non-fips` Maven profile, version pinned via the existing parent
  dep-management entry.

  ## Out of scope (follow-ups)

- **`fips` profile support for hybrid PQC** — needs verification of
which
    `bc-fips` version ships ML-KEM and X-Wing and how it registers them.

<!-- review_stack_entry_start -->

[![Review Change
Stack](https://storage.googleapis.com/coderabbit_public_assets/review-stack-in-coderabbit-ui.svg)](https://app.coderabbit.ai/change-stack/opentdf/java-sdk/pull/368?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack)

<!-- review_stack_entry_end -->
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

## Release Notes

* **New Features**
* Added support for hybrid post-quantum cryptography with three new key
types: X-Wing, secp256r1+ML-KEM-768, and secp384r1+ML-KEM-1024.
* Introduced hybrid key wrapping mechanism for TDF encryption, enabling
classical and post-quantum algorithm combinations.

* **Tests**
* Added end-to-end testing suite for hybrid post-quantum key wrapping
workflows.

* **Documentation**
* Updated test documentation with hybrid PQC integration workflow and
troubleshooting guidance.

<!-- review_stack_entry_start -->

[![Review Change
Stack](https://storage.googleapis.com/coderabbit_public_assets/review-stack-in-coderabbit-ui.svg)](https://app.coderabbit.ai/change-stack/opentdf/java-sdk/pull/368?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack)

<!-- review_stack_entry_end -->

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
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.

4 participants