Package zeroecho.core.alg.common.sig


package zeroecho.core.alg.common.sig
Streaming signature contexts and helpers that adapt JCA Signature to a pull-based pipeline.

This package provides a generic signature context that wraps an InputStream, updates a JCA engine as bytes flow, and either appends a fixed-length trailer (sign) or verifies against a caller-supplied tag (verify). A small internal passthrough stream performs byte forwarding and end-of-stream finalization.

Design goals

  • Single-use pipeline integration: create a context for SIGN or VERIFY and call wrap(InputStream) once.
  • Known tag length up front: produced/expected signature length is resolved at construction so downstream components can append or strip trailers without buffering the whole stream.
  • Provider encapsulation: JCA provider details are hidden behind factories that create initialized engines.

Components

  • GenericJcaSignatureContext - streaming context that uses a configured Signature, resolves a fixed tag length (via resolvers), and exposes a one-shot wrap(InputStream) API. Verification behavior is controlled by a pluggable comparison approach.
  • SignatureStream - internal passthrough input stream that feeds chunks to the signature engine, emits the trailer in SIGN mode, and performs final verification in VERIFY mode.

Length resolution

Tag length is determined by resolvers supplied at construction time:

  • SignLengthResolver - returns the produced length (fixed or by probing a provider with an empty-message sign).
  • VerifyLengthResolver - returns the expected length (fixed or derived from key parameters).

Verification approach

Verification is delegated to a VerificationBiPredicate strategy. The default core delegates to Signature.verify(byte[]), and callers may decorate it to throw on mismatch or to record results externally.

Usage sketch

Sign


GenericJcaSignatureContext ctx = new GenericJcaSignatureContext(
    algorithm,
    privateKey,
    GenericJcaSignatureContext.jcaFactory("SHA256withRSA", null),
    GenericJcaSignatureContext.SignLengthResolver.probeWith("SHA256withRSA", null));

try (InputStream in = ctx.wrap(upstream)) {
    in.transferTo(out); // body, then signature trailer of ctx.tagLength() bytes
}

Verify


GenericJcaSignatureContext vctx = new GenericJcaSignatureContext(
    algorithm,
    publicKey,
    GenericJcaSignatureContext.jcaFactory("SHA256withRSA", null),
    GenericJcaSignatureContext.VerifyLengthResolver.fixed(256));

vctx.setVerificationApproach(vctx.getVerificationCore().getThrowOnMismatch());
vctx.setExpectedTag(signatureBytes);

try (InputStream verified = vctx.wrap(bodyWithoutTrailer)) {
    verified.transferTo(java.io.OutputStream.nullOutputStream()); // throws on mismatch at EOF
}

Thread-safety

  • Contexts and streams are stateful, not thread-safe, and intended for single use.
  • Create a new context per wrapped stream.
Since:
1.0