Mirko Caserta has released Bruce 2.0, a library that wraps JCA with a clean API. As such, it attempts to address the same gap for cryptography that commons email covers for JavaMail: the underlying library is technically correct - the best kind of correct - but a bear to use, and that difficulty creates a barrier that prevents people who really should use the specification from being willing to lean into it.
JCA works, of course. It's just that using it requires knowledge that casual practitioners aren't likely to have, and this means that we are forced into either learning the ins and outs of cryptography to have decent data security, or we cargo-cult security concerns. It'd be like asking someone to master physics when all they want to do is fly a remote control plane; airflow is important to those designing planes, but requiring a degree for such things is a little much, when even young children are able to come up with paper airplanes that fly well.
Bruce takes the agony of all the nuts and bolts of security and, well, fixes it. From this, stolen shamelessly from "Digital signatures in Java: the hard way vs. the Bruce way":
// Load the keystore
KeyStore keystore = KeyStore.getInstance("PKCS12");
try (InputStream is = new FileInputStream("keystore.p12")) {
keystore.load(is, "changeit".toCharArray());
}
// Extract keys
PrivateKey privateKey = (PrivateKey)
keystore.getKey("alice", "changeit".toCharArray());
PublicKey publicKey =
keystore.getCertificate("alice").getPublicKey();
byte[] message = "Transfer $500 to Alice".getBytes("UTF-8");
// Sign
Signature signer = Signature.getInstance("SHA256withRSA");
signer.initSign(privateKey);
signer.update(message);
byte[] signatureBytes = signer.sign();
// Encode for transport
String encoded = Base64.getEncoder().encodeToString(signatureBytes);
// Decode and verify
byte[] decoded = Base64.getDecoder().decode(encoded);
Signature verifier = Signature.getInstance("SHA256withRSA");
verifier.initVerify(publicKey);
verifier.update(message);
boolean isValid = verifier.verify(decoded);
... to this:
var keystore = keystore(
"classpath:keystore.p12",
"changeit".toCharArray(),
"PKCS12");
var privateKey = privateKey(keystore, "alice", "changeit".toCharArray());
var publicKey = publicKey(keystore, "alice");
var signer = signerBuilder()
.key(privateKey)
.algorithm("SHA256withRSA")
.build();
var verifier = verifierBuilder()
.key(publicKey)
.algorithm("SHA256withRSA")
.build();
var message = Bytes.from("Transfer $500 to Alice");
var signature = signer.sign(message);
var encoded = signature.encode(BASE64); // ready for transport
var isValid = verifier.verify(message, Bytes.from(encoded, BASE64));
I'll be clear: I'm not a cryptographer, and the technical correctness of Bruce isn't something I can vouch for personally. Security libraries earn trust slowly and should - this is not the domain where "looks good to me" is sufficient due diligence. Read the documentation, examine the source, and run your tests. Run all your tests. Write new ones. The exposure surface for cryptographic code is meaningfully different from, say, a misbehaving email client.
But the API design is immediately legible, the intent is clear, and the problem it solves is real. If it holds up under scrutiny - and that scrutiny is your job for your projects, not mine - Bruce looks like exactly the kind of thing the Java ecosystem needed someone to build.