How do you create and verify a digital signature using Java's JCA Signature class?
answer
- getInstance("SHA256withRSA")
- initSign(private) → update → sign()
- initVerify(public) → update → verify()
- verify returns boolean, not throw
- algorithm must match key type
basics
~10 sGet a Signature object with Signature.getInstance("SHA256withRSA"). To sign: call initSign(privateKey), update(data), then sign() to get the signature bytes. To verify: initVerify(publicKey), update(data), then verify(signatureBytes) returns true if it matches.
solid answer
~40 sJava's signing engine is java.security.Signature, obtained via getInstance with an algorithm string such as "SHA256withRSA" or "SHA256withECDSA". It has two lifecycles. To sign, you initSign(privateKey) to put the object in SIGN state, feed the bytes you want to protect with one or more update(...) calls, then call sign(), which hashes the accumulated data and produces the signature bytes with the private key. To verify, you initVerify(publicKey) to enter VERIFY state, feed the exact same bytes with update(...), then call verify(signatureBytes), which returns a boolean: true if the signature is authentic and the data is unmodified, false otherwise. The keys come from a KeyPair (getPrivate()/getPublic()), and the algorithm in the Signature string must match the key type (RSA keys with ...withRSA, EC keys with ...withECDSA).
code
java · 12 linesKeyPair kp = KeyPairGenerator.getInstance("RSA").generateKeyPair();
byte[] data = "hello".getBytes(StandardCharsets.UTF_8);
Signature signer = Signature.getInstance("SHA256withRSA");
signer.initSign(kp.getPrivate());
signer.update(data);
byte[] signature = signer.sign();
Signature verifier = Signature.getInstance("SHA256withRSA");
verifier.initVerify(kp.getPublic());
verifier.update(data);
boolean valid = verifier.verify(signature); // truego deeper
Can name the class (Signature), get an instance by algorithm string, and recite the initSign/update/sign and initVerify/update/verify sequences.
Knows verify returns a boolean, that keys come from a KeyPair, and that the algorithm string must match the key type; handles the checked exceptions.
Explains the SIGN/VERIFY state machine, that signing hashes then signs, reuse after sign()/verify(), and the difference between false (bad signature) and SignatureException (malformed input).
Frames Signature within JCA's provider model, can reason about algorithm/provider selection, and ties the API to authenticity/integrity guarantees and key-management concerns at a system level.
## What a digital signature is (plain terms) A **digital signature** answers two questions about a piece of data: *did it really come from the holder of a specific private key* (authenticity) and *was it changed since it was signed* (integrity). It works with a **key pair**: a **private key** that only the signer holds, and a **public key** that anyone can have. The signer uses the private key to produce a signature; anyone with the public key can check it. The private key cannot be derived from the public key. ## The Java class: java.security.Signature Java's cryptography is built on the **JCA (Java Cryptography Architecture)** — a provider-based framework where you ask for an engine *by name* and a provider supplies the implementation. The signing engine is `java.security.Signature`. You never call `new Signature()`; you call the factory method: ```java Signature sig = Signature.getInstance("SHA256withRSA"); ``` The string `"SHA256withRSA"` is the **algorithm name**. Read it as "hash the data with SHA-256, then sign that hash with RSA". Other common ones: `"SHA256withECDSA"` (elliptic-curve), `"SHA512withRSA"`, `"SHA256withDSA"`. The hash part and the key-algorithm part both matter and the key-algorithm part **must match the key you use**. ## The two lifecycles A `Signature` object is a small state machine. After `getInstance` it is **uninitialized**; you choose a direction by initializing it: **Signing (you hold the private key):** 1. `sig.initSign(privateKey)` — puts the object into SIGN state. 2. `sig.update(bytes)` — feed the data to be signed. You may call this many times; the data is accumulated. Order matters. 3. `byte[] signature = sig.sign()` — internally hashes all the fed data and produces the signature bytes using the private key. After `sign()` the object resets and can be reused for another signature. **Verifying (you hold the public key):** 1. `sig.initVerify(publicKey)` — puts the object into VERIFY state. 2. `sig.update(bytes)` — feed **exactly the same bytes**, in the same order, that were signed. 3. `boolean ok = sig.verify(signature)` — returns `true` if the signature was produced by the matching private key over exactly this data, `false` otherwise. **It returns a boolean; it does not throw on a bad signature** (it throws `SignatureException` only on malformed input). ## Where the keys come from Keys are produced by a `KeyPairGenerator`: ```java KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA"); kpg.initialize(2048); KeyPair kp = kpg.generateKeyPair(); PrivateKey priv = kp.getPrivate(); PublicKey pub = kp.getPublic(); ``` `getPrivate()` feeds `initSign`; `getPublic()` feeds `initVerify`. The signer keeps the private key secret and publishes the public key. ## A complete round-trip ```java byte[] data = "hello".getBytes(StandardCharsets.UTF_8); Signature signer = Signature.getInstance("SHA256withRSA"); signer.initSign(kp.getPrivate()); signer.update(data); byte[] signature = signer.sign(); Signature verifier = Signature.getInstance("SHA256withRSA"); verifier.initVerify(kp.getPublic()); verifier.update(data); boolean valid = verifier.verify(signature); // true ``` If even one byte of `data` differs between signing and verifying, `verify` returns `false`. If the wrong public key is used, it returns `false`. ## Checked exceptions you must handle `getInstance` throws `NoSuchAlgorithmException`; `initSign`/`initVerify` throw `InvalidKeyException` (e.g., an EC key with an RSA algorithm); `update`/`sign`/`verify` throw `SignatureException` (object in the wrong state or malformed signature input). These are checked exceptions. ## Mental model Think of `Signature` as a one-track tape recorder: you press SIGN or VERIFY (init), play the tape through (update), and then either burn a seal (`sign`) or check the seal (`verify`).
- Why does verify() return a boolean instead of throwing when the signature is invalid?An invalid-but-well-formed signature is a normal, expected outcome (untrusted input), so it is reported as false. SignatureException is reserved for programming/encoding errors like a malformed signature or wrong object state.
- What happens if you sign with an RSA private key but call getInstance("SHA256withECDSA")?initSign throws InvalidKeyException because the key type does not match the algorithm's expected key algorithm.
saying these in an interview costs you the question
- Thinking you encrypt the data with the private key to sign it (you sign a hash; signing is not encryption-with-private-key in any safe API)
- Expecting verify() to throw on a bad signature — it returns false
- Using the public key to sign or the private key to verify
- Calling new Signature() instead of getInstance(...)
- Forgetting that update() data must be identical and in the same order on both sides