Skip to content

Latest commit

 

History

History
104 lines (78 loc) · 4.83 KB

README.md

File metadata and controls

104 lines (78 loc) · 4.83 KB

A modern, minimal, fluent JOSE library.

Important

We use this library to explore new additions to the JOSE standard, such as HPKE and post-quantum encryption.

The API is subject to change, and the library is not intended for production use yet.

Implemented Specifications

Usage Examples:

Integrated Encryption with quantum-secure HPKE-9 (MLKEM768-X25519, a.k.a. X-Wing)

HPKE encrypts the payload directly — no separate content encryption algorithm involved:

// encrypt:
var encrypted = JWE.build("payload")
		.encrypt(Alg.hpke9(receiverPublicKey))
		.toCompactSerialization();

// decrypt:
var decrypted = JWE.parse(encrypted).decrypt(Alg.hpke9(receiverPrivateKey));

Key Encryption with HPKE-9-KE

HPKE encrypts a content encryption key, which then encrypts the payload:

// encrypt:
var encrypted = JWE.build("payload")
		.encrypt(Enc.A256GCM, Alg.hpke9Ke(receiverPublicKey))
		.toCompactSerialization();

// decrypt:
var decrypted = JWE.parse(encrypted).decrypt(Alg.hpke9Ke(receiverPrivateKey));

Multiple recipients

Key Encryption allows multiple recipients with different algorithms sharing one content encryption key. Since every recipient unwraps the same key, the JWE is only as strong as its weakest recipient (here: the password).

// encrypt with multiple recipients:
char[] password = ...; // stays owned by the caller: the library never copies or wipes it
try {
    var encrypted = JWE.build("payload")
            .encrypt(Enc.A256GCM, Alg.pbes2(password), Alg.hpke9Ke(receiverPublicKey2))
            .toJsonSerialization();
} finally {
    Arrays.fill(password, '\0');
}

// decrypt as recipient 1 (PBES2):
var decrypted1 = JWE.parse(encrypted).decrypt(Alg.pbes2(password));

// decrypt as recipient 2 (HPKE-9-KE):
var decrypted2 = JWE.parse(encrypted).decrypt(Alg.hpke9Ke(receiverPrivateKey2));

Per-recipient headers, kid-targeted decryption, critical extensions

Each recipient may carry its own header parameters, e.g. a kid and private parameters. The parser enforces crit (RFC 7516 §4.1.13): a JWE listing an extension the caller did not declare is rejected when parsing.

// encrypt:
var encrypted = JWE.build("payload")
        .withProtectedHeader(header) // e.g. {"cty":"json","crit":["org.example.version"],"org.example.version":1}
        .encrypt(Enc.A256GCM,
                Recipient.withAlg(Alg.a256kw(kek)).withClaim("kid", "org.example.password").withClaim("org.example.iterations", 3),
                Recipient.withAlg(Alg.ecdhEs(recoveryPublicKey)).withClaim("kid", "org.example.recovery"))
        .toJsonSerialization();

// inspect, then decrypt the recipient identified by kid:
var parsed = JWE.parse(encrypted, "org.example.version");
var kids = parsed.recipientHeaders().stream().map(h -> h.get("kid").getAsString()).toList();
var decrypted = parsed.decrypt("org.example.recovery", Alg.ecdhEs(recoveryPrivateKey));

JWS with HMAC

// sign:
var token = JWS.build("{\"sub\":\"alice\"}")
        .withProtectedHeader(header) // e.g. {"typ":"JWT","kid":"masterkey"}
        .sign(MacAlg.hs256(key));

// verify:
var verified = JWS.parse(token).verify(MacAlg.hs256(key));
var payload = verified.payload();

verify checks the signature only. This library implements JWS, not JWT: if the payload is a JWT claims set, validating exp, nbf, aud, iss etc. is up to the caller.

License

Distributed under the MIT License.