ph security - phax/ph-commons GitHub Wiki

A Java library with shared security aspects: authentication, certificates, keystores, CRL/OCSP, message digests, MACs and password hashing.

This library is independent of BouncyCastle - see ph-bc for BouncyCastle specifics.

Highlights:

  • KeyStoreHelper / KeyStoreDescriptorBuilder / TrustStoreDescriptorBuilder - keystore and truststore handling
  • CertificateHelper / CertificateDecodeHelper - certificate utilities
  • TrustedCAChecker - CA certificate validation
  • CRLHelper / CRLCache / CRLDownloader - CRL handling and caching
  • RevocationCheckBuilder - fluent builder for OCSP/CRL revocation checks
  • BCrypt / PasswordHashCreatorBCrypt / PasswordHashCreatorPBKDF2_SHA256_1000_48 - password hashing
  • VerySecureRandom - cryptographically secure random number generation
  • AuthToken / AuthTokenRegistry / AuthIdentificationManager - token-based authentication
  • EMessageDigestAlgorithm / EMacAlgorithm - algorithm enums

Maven usage

Add the following to your pom.xml to use this artifact:

<dependency>
  <groupId>com.helger.commons</groupId>
  <artifactId>ph-security</artifactId>
  <version>x.y.z</version>
</dependency>

Architecture decisions

ADR-1: ph-security must not depend on BouncyCastle

Status: accepted (2026-08-25, effective since v12.4.0)

Context

Everything ph-security offers - keystores, certificates, CRL/OCSP revocation checking, message digests, MACs and password hashing - is reachable through the JCA/JCE APIs of the JDK. Up to and including v12.3.5 there were nevertheless exactly two links against BouncyCastle: CRLHelper decoded the "CRL Distribution Points" certificate extension via org.bouncycastle.asn1.x509.CRLDistPoint, and EOCSPResponseStatus imported six int constants from org.bouncycastle.asn1.ocsp.OCSPResponseStatus. The ph-bc dependency was declared optional, so BouncyCastle never propagated transitively, but CRLHelper.getAllDistributionPoints (X509Certificate) failed with a NoClassDefFoundError without it on the classpath. Downstream projects doing revocation checks - e.g. peppol-commons and phase4 - therefore had to declare ph-bc themselves and shipped roughly 12 MB of bcprov + bcpkix + bcutil for those two linkages.

Decision

ph-security uses only the JCA/JCE APIs of the JDK plus other ph-* modules. BouncyCastle is permitted in test scope only and must never appear on the compile or runtime classpath. Functionality that genuinely requires BouncyCastle belongs into ph-bc or into a module that depends on ph-bc.

Consequences

  • Applications decide for themselves whether they need BouncyCastle - e.g. for the BKS keystore type or for algorithms the JDK providers don't offer - and ph-bc stays the single place that registers the provider.
  • The JDK offers no public API for the "CRL Distribution Points" extension. X509Certificate only decodes basicConstraints, keyUsage, extendedKeyUsage, subjectAltName, issuerAltName and the unique IDs, and sun.security.x509 is not exported by the java.base module, so a library cannot rely on it (strong encapsulation is enforced by default since Java 16). Therefore the small, deliberately strict DER parser CRLDistributionPointParser for exactly that one extension (RFC 5280, section 4.2.1.13) is maintained inside ph-security. It is covered by a differential test that compares its result against the previous BouncyCastle based implementation for every available test certificate.
  • Rejected alternatives: moving CRLHelper into ph-bc (breaks the public API and would silently degrade the revocation check to "status unknown" whenever ph-bc is absent), using the internal sun.security.x509 classes (not exported, so a library cannot rely on them), and adding a small third-party ASN.1 library (ph-security has no third-party runtime dependency besides SLF4J and the JSpecify annotations, and the only really small candidate - com.hierynomus:asn-one - had its last release in 2021).
  • New code in ph-security must not add an org.bouncycastle.* import. Because ph-bc is a test scope dependency in ph-security/pom.xml, the compiler enforces this - no additional build rule is required.
⚠️ **GitHub.com Fallback** ⚠️