How does the update() method work in the Signature lifecycle, and what are the rules when feeding data in multiple chunks?
answer
- update accumulates bytes in order
- many small updates == one big update
- stream files: read buf, update(buf,0,n)
- sign()/verify() finalize then reset
- verifier must feed identical bytes/encoding
basics
~20 supdate() feeds the bytes to be signed or verified into the Signature object. You can call it many times to stream large data in chunks; the bytes are accumulated in order. The verifier must feed exactly the same bytes in the same order, then call sign() or verify().
solid answer
~50 sBetween initializing and finalizing, you supply the data with one or more update(...) calls. update accepts a single byte, a byte[], a byte[] with offset+length, or a ByteBuffer, and it accumulates everything you feed in order — concatenating ten 1 KB chunks is identical to one 10 KB chunk. This lets you sign or verify streams/large files without loading them fully into memory: read a buffer, update, repeat. Only at sign() or verify() does the engine finalize: it completes the digest over all accumulated bytes and performs the asymmetric operation. Calling update before init, or after a previous sign/verify without re-initializing, throws SignatureException, since the object resets to needing data after each finalize. The critical correctness rule: the verifier must feed byte-for-byte the same content in the same order as the signer — any difference in bytes, order, or canonicalization makes verify() return false. So both sides must agree on serialization/encoding before hashing.
code
java · 10 linesSignature sig = Signature.getInstance("SHA256withRSA");
sig.initSign(priv);
try (InputStream in = Files.newInputStream(path)) {
byte[] buf = new byte[8192];
int n;
while ((n = in.read(buf)) != -1) {
sig.update(buf, 0, n); // feed only the bytes read
}
}
byte[] signature = sig.sign();go deeper
Knows update() feeds the data and can be called multiple times before sign()/verify().
Streams large data with update(buf,0,n), knows chunk boundaries are irrelevant but order/content are not, and that the verifier must feed identical bytes.
Explains the finalize-and-reset behavior, the encoding/canonicalization pitfalls that cause spurious verify failures, and designs a deterministic serialization for signed payloads.
Establishes canonical-byte / signing-envelope conventions across services, accounts for streaming memory limits, and defines interop contracts so independent implementations verify each other's signatures.
## Where update() sits The `Signature` object moves through phases: **getInstance → init (SIGN or VERIFY) → update (feed data) → finalize (sign or verify)**. `update()` is the middle phase: it is how you tell the object *which bytes* the signature covers. ## What update() accepts There are several overloads: ```java sig.update(byte b); // one byte sig.update(byte[] data); // whole array sig.update(byte[] data, int off, int len); // a slice sig.update(ByteBuffer buffer); // NIO buffer ``` Each call **appends** to the running data. The engine maintains a running digest internally; it does not buffer the whole message in memory (that's the point of hashing). So: ```java sig.update(chunk1); sig.update(chunk2); sig.update(chunk3); ``` produces exactly the same result as `sig.update(chunk1 ++ chunk2 ++ chunk3)`. **Order matters; boundaries do not.** ## Streaming large data Because update is incremental and the engine only keeps a small digest state, you can sign or verify a file far larger than RAM: ```java Signature sig = Signature.getInstance("SHA256withRSA"); sig.initSign(priv); try (InputStream in = Files.newInputStream(path)) { byte[] buf = new byte[8192]; int n; while ((n = in.read(buf)) != -1) { sig.update(buf, 0, n); // feed only the bytes read } } byte[] signature = sig.sign(); ``` Note `update(buf, 0, n)` — you must feed only the `n` valid bytes, not the whole `buf`, or you'd include stale buffer tail bytes and corrupt the signature. ## Finalize and reset `sign()` and `verify()` are the **finalize** operations: they complete the digest over everything fed so far and run the asymmetric math. **After finalize, the object resets**: it returns to the initialized state ready to accept new `update()` calls for another signature with the same key (you do not need to call init again to sign a second message with the same key — though re-initializing is harmless and explicit). Calling `update()` on an **uninitialized** object, or `sign()/verify()` with no data fed, throws `SignatureException`. ## The cardinal correctness rule: identical bytes A signature covers *bytes*, not *meaning*. The verifier must reconstruct **byte-for-byte exactly** what the signer fed, in the same order. Common failures: - **Encoding drift**: signer used UTF-8, verifier used UTF-16 (or platform default). Always pin the charset. - **Serialization non-determinism**: signing a JSON/object whose field order or whitespace differs between sides. Use a canonical form, or sign the exact serialized bytes you transmit. - **Line endings / trailing newlines** differing between platforms. - **Feeding the whole buffer** instead of the `n` bytes actually read. If any of these differ, the digest differs, and `verify()` returns `false` — even though the keys and algorithm are correct. ## Mental model `update()` is pouring water into a funnel: you can pour in any number of splashes, but the funnel measures the *total in the order poured*. The signer and verifier must pour the identical water.
- Can you reuse a Signature object to sign a second message?Yes. After sign()/verify() the object resets to its initialized state, so you can feed new data and sign again with the same key. Re-calling initSign/initVerify is also fine and makes intent explicit.
- Two systems use the same key/algorithm but verify() always fails. What's the likely cause?The bytes fed differ — usually a charset mismatch, non-canonical serialization (field order/whitespace), differing line endings, or feeding the whole buffer instead of the bytes actually read.
saying these in an interview costs you the question
- Passing update(buf) when only n bytes were read instead of update(buf, 0, n)
- Assuming chunk boundaries change the signature (they don't; only order/content do)
- Letting charset/serialization differ between signer and verifier
- Calling sign()/verify() without any update() and expecting a valid result
- Thinking the engine buffers the entire message in memory