In short
To sign a JAR, run jarsigner -keystore keystore.p12 -tsa http://time.certum.pl MyApp.jar myalias. jarsigner hashes every entry in the archive, signs those hashes with your certificate's private key, and stores the result inside META-INF/. Always pass -tsa — without a timestamp the signature dies with your certificate. If the certificate is publicly trusted, the key lives on hardware, so you point jarsigner at a PKCS#11 config file instead of a keystore file.
jarsigner has shipped with every JDK since the nineties, and the core command hasn't changed much. What has changed is everything around it: keys for publicly trusted certificates now live on hardware tokens or in cloud services and are reached through PKCS#11, modern JDKs silently treat SHA-1-signed archives as unsigned, and a signature applied without a timestamp quietly expires with the certificate. This guide walks the whole path — what signing writes into the JAR, which keystore setup fits your situation, the exact commands, and the warnings jarsigner prints when something is off.
Signing your own software? Compare OV and EV code signing certificates and current prices.
What signing a JAR actually does
Signing a JAR writes proof of origin into the archive itself: jarsigner computes a SHA-256 digest for every entry, records those digests in the manifest, then signs them with your certificate's private key and stores the signature — with your certificate chain and an optional timestamp — as new files under META-INF/. Anyone holding the JAR can later confirm both who signed it and that not a single byte has changed since.
Three files carry the whole mechanism. The manifest (MANIFEST.MF) gains a digest line for every entry in the archive. The signature file (MYALIAS.SF, named after your keystore alias) contains digests of the manifest's sections, so the manifest can't be quietly rewritten. And the signature block (MYALIAS.RSA, or .EC for elliptic-curve keys) holds the actual cryptographic signature plus the certificates needed to check it. Your classes and resources are untouched — which is also why a signed and unsigned JAR behave identically at runtime.
One detail that surprises people coming from Windows code signing: coverage is per entry, not per file-blob. If any tool adds an entry to the archive after signing, the JAR doesn't become invalid — it becomes partially signed, and verification flags it as containing unsigned entries. That's the root cause behind most mysterious verification failures in build pipelines.
When do you still need to sign a JAR?
You need to sign a JAR when something downstream verifies it: a plugin framework, an enterprise deployment policy, middleware that refuses unsigned archives, or customers who check vendor artifacts before letting them near production. The applet era that made JAR signing famous is over — browsers dropped the Java plugin years ago and Java Web Start left the JDK with version 11 — but the verification habit moved into infrastructure rather than disappearing.
- Plugin ecosystems. Eclipse p2 and other OSGi-based installers surface warnings — or refuse installation outright, depending on policy — for unsigned bundles. If you distribute Eclipse or IDE plugins, signing is part of shipping.
- Enterprise and middleware requirements. Plenty of corporate Java estates verify signatures on the JARs they deploy, and products like Oracle E-Business Suite require customizations to be signed with a trusted certificate.
- Supply-chain assurance. A signed, timestamped JAR gives customers a one-command check (
jarsigner -verify) that the artifact they downloaded is byte-for-byte what you released — independent of the download channel.
Worth being clear about what JAR signing is not: Maven Central verifies PGP signatures, not jarsigner signatures, and a desktop app packaged with jpackage gets signed at the installer level with OS-native tools — SignTool on Windows. jarsigner is specifically for the archives the Java world itself verifies.
What you need before you sign
Two things: a JDK (jarsigner and keytool live in its bin directory, no separate download) and a certificate with the code signing purpose whose private key jarsigner can reach. Where that certificate comes from depends on who must trust the result — the same three-tier logic as every other kind of code signing.
- Testing and internal tools:
keytool -genkeypair -alias myalias -keyalg RSA -keysize 3072 -validity 365 -keystore keystore.p12creates a self-signed certificate in a PKCS#12 keystore (the default format since JDK 9). Signatures verify only where you've imported that certificate into the truststore by hand. - Inside one organization: a code signing certificate from your internal CA, imported into a keystore, verifies wherever machines already trust that CA's root.
- Software that leaves your organization: you need a certificate from a publicly trusted CA, issued to a validated identity. My-SSL carries Certum standard and EV code signing certificates that jarsigner uses the same way SignTool does — Certum's signing manual documents both tools side by side. The validation paperwork is covered in the required documents guide.
No .p12 file for publicly trusted keys
Under CA/Browser Forum rules in force since June 1, 2023, publicly trusted code signing keys must be generated and kept on certified hardware — a USB token, smart card, HSM, or a cloud signing service. You will not receive a keystore file to pass to -keystore. Instead, jarsigner talks to the hardware through PKCS#11, which is what the next section sets up. The self-signed keystore path above remains available for anything that doesn't need public trust.
Sign the JAR with jarsigner
With a keystore file, one command does everything: name the keystore, name a timestamp authority with -tsa, then give the JAR and the alias of the key to sign with. jarsigner prompts for the keystore password, rewrites the archive in place (or writes a copy if you pass -signedjar), and prints jar signed. on success.
# Check which alias your keystore actually contains
keytool -list -keystore keystore.p12
# Sign in place, with a timestamp
jarsigner -keystore keystore.p12 \
-tsa http://time.certum.pl \
MyApp.jar myalias
# Or keep the original and write a signed copy
jarsigner -keystore keystore.p12 \
-tsa http://time.certum.pl \
-signedjar MyApp-signed.jar MyApp.jar myalias
# Expected output:
# jar signed.Unlike SignTool, you don't need to force the hash algorithm: a current JDK defaults to SHA-256 digests and, for RSA keys, a SHA256withRSA signature — sensible defaults you'd only override with -digestalg and -sigalg in unusual compatibility situations. The flag you should treat as mandatory is -tsa. Without it, jarsigner still signs but warns that the JAR may not validate after the certificate expires — and it means exactly that: every copy you ever shipped stops verifying on that date. Our timestamping guide covers why in detail; the short version is that the timestamp proves signing happened while the certificate was valid, which lets the signature outlive it.
The trailing myalias argument trips up more first attempts than any flag: it's the name of the key entry inside the keystore, not a file name or your company name. When in doubt, keytool -list shows every alias the keystore holds.
Signing from a USB token or the cloud (PKCS#11)
When the private key lives on a hardware token or in a cloud signing service, jarsigner reaches it through PKCS#11: you write a two-line config file pointing at the vendor's PKCS#11 library, then tell jarsigner to use that instead of a keystore file. The signing command itself barely changes, and the token PIN takes the place of the keystore password.
# provider.cfg — points Java at the token's PKCS#11 library
# (path shown for a Certum card on Windows; the SimplySign
# desktop app installs its own library)
name = Certum
library = C:\Windows\System32\crypto3PKCS.dll
# List the aliases the token exposes
keytool -list -keystore NONE -storetype PKCS11 \
-providerClass sun.security.pkcs11.SunPKCS11 \
-providerArg provider.cfg
# Sign — jarsigner prompts for the token PIN
jarsigner -keystore NONE -storetype PKCS11 \
-providerClass sun.security.pkcs11.SunPKCS11 \
-providerArg provider.cfg \
-tsa http://time.certum.pl \
MyApp.jar "certificate alias"The alias on a token is usually derived from the certificate's Common Name — your validated company or personal name, often with spaces, so quote it. If the keytool -list step shows no aliases, the token middleware isn't installed or the card isn't connected; sort that out before blaming jarsigner. The same PKCS#11 setup works whether the key sits on a physical Certum card or in Certum's SimplySign cloud service, where a desktop app stands in for the card reader — handy when the person (or CI job) signing isn't near a USB port. The trade-offs between those two setups get a full article in cloud signing vs USB tokens.
Verify the signature
Verification is one command: jarsigner -verify -verbose -certs MyApp.jar. It re-digests every entry, checks the signature and the certificate chain, and ends with jar verified. when everything holds. The -verbose -certs pair is what makes the output useful: per-entry flags, the full signer chain, and a Timestamped by line confirming your -tsa flag did its job.
jarsigner -verify -verbose -certs MyApp.jar
# Healthy output ends with:
# - Signed by "CN=Your Company, O=Your Company, C=..."
# - Timestamped by "CN=Certum Timestamping 2024 CA" on Mon Jul 06 ...
# jar verified.
# In CI, add -strict so problems become a non-zero exit code
jarsigner -verify -strict MyApp.jarRead the per-entry flags in the verbose listing: s means the entry's signature verified, m means it's listed in the manifest. An entry with neither is unsigned — the partially-signed trap from earlier. And note that plain -verify is forgiving by design; it can print jar verified. followed by warnings you'd rather treat as failures. -strict turns those warnings into exit codes, which is the behavior you want in a pipeline.
Common warnings and what they mean
jarsigner rarely fails outright — its habit is to sign or verify anyway and attach a warning, which makes the warnings worth reading carefully. These are the ones that actually stop people, roughly in the order they tend to appear.
"Certificate chain not found for: myalias"
The alias doesn't exist in the keystore you named, or it exists but has no private key. Run keytool -list against the same keystore and copy the alias exactly — it's case-insensitive but must otherwise match. On tokens, remember the alias comes from the certificate's Common Name, not anything you chose.
"keystore load: … password was incorrect" or "keystore was tampered with"
Wrong keystore password — the "tampered with" phrasing is historical JKS wording for the same thing. Against a PKCS#11 token, the "password" is the token PIN; too many wrong attempts can lock the card, so check with the vendor's card manager app rather than retrying blindly.
"no -tsa or -tsacert is provided … may not be able to validate this jar after the signer certificate's expiration"
You signed without a timestamp. The signature works today and breaks the day the certificate expires. Re-run the same command with -tsa http://time.certum.pl (any RFC 3161 timestamp authority works) — re-signing simply replaces the old signature.
"This jar contains unsigned entries" or "entries whose signer certificate … " after a build
Something modified the archive after signing — shade/assembly plugins, resource filtering, even a well-meaning re-zip. Coverage is per entry, so the added files are simply outside the signature. Make jarsigner the final build step and verify with -strict right after.
"jar treated as unsigned, because it is signed with a weak algorithm that is now disabled"
The SHA-1 policy. Since the October 2022 JDK updates, archives whose digest, signature, or timestamp uses SHA-1 are treated as unsigned unless they carry a timestamp from before January 1, 2019. You'll see this on JARs signed years ago with old defaults, or by pipelines that still pass -digestalg SHA1. The fix is to re-sign with a current JDK and a timestamp — the defaults are already right.
"The signer's certificate is self-signed"
Expected with a keytool-generated test certificate — it's a statement of fact, not an error. It disappears when you sign with a CA-issued certificate whose chain ends in a trusted root. If you're seeing it in production distribution, that's the signal you've outgrown the test setup.
Frequently Asked Questions
Answers to common questions about certificates and our services.
Can I sign a JAR without buying a certificate?
Yes, for anything you control. keytool -genkeypair creates a key pair with a self-signed certificate in one command, and jarsigner signs with it happily — verification just carries the warning that the certificate is self-signed. Machines that should trust it need the certificate imported into their truststore by hand. That trade is fine for internal tools, test builds, and learning the workflow; it stops working the moment the JAR goes to people who have never heard of you. For those, you need a certificate from a publicly trusted CA, issued to a validated identity.
Do I need to re-sign a JAR after rebuilding it?
Yes, every time. The signature covers a digest of each entry in the archive, so a rebuild — even one that produces byte-identical classes but a different manifest timestamp — can invalidate it. Adding files to an already-signed JAR is worse: the new entries simply aren't covered, and verification reports the archive as containing unsigned entries. The reliable pattern is to make signing the last step of the build, after every other tool has finished touching the archive, and to verify immediately afterwards.
Why does a modern JDK treat my signed JAR as unsigned?
Almost always the SHA-1 policy. Since the October 2022 JDK updates (17.0.5 and the parallel 8/11 updates), JARs whose digest, signature, or timestamp uses SHA-1 are disabled by default and treated as unsigned — unless they were timestamped before January 1, 2019. Run jarsigner -verify -verbose -certs and look for 'SHA1' next to the word 'disabled' in the output. The fix is to re-sign with a current JDK, whose defaults (SHA-256 digests, SHA256withRSA) are fine, and timestamp the new signature.
Can I use the same certificate for JARs and Windows EXEs?
Yes. A code signing certificate isn't tied to one file format — the same Certum certificate works in jarsigner for JARs and in SignTool or Set-AuthenticodeSignature for EXEs, MSIs, and PowerShell scripts. Certum's own signing manual walks through both tools against the same card. The practical difference is ceremony, not cryptography: jarsigner reaches the key through a PKCS#11 configuration file, while the Windows tools find it in the certificate store via the token's CSP driver.
What is the difference between jarsigner and SignTool?
They implement different trust systems. SignTool produces Authenticode signatures that Windows itself checks — SmartScreen, UAC dialogs, and kernel policy all read them. jarsigner produces JAR signatures that Windows knows nothing about; they're checked by Java: the jarsigner -verify command, frameworks like OSGi and Eclipse p2, and applications that validate their own plugins. Signing an EXE with jarsigner or a JAR with SignTool doesn't work. If you ship both kinds of artifact, you sign twice, usually with the same certificate.
Does Java refuse to run unsigned JARs?
No. Plain java -jar and classpath loading never demand a signature — an unsigned JAR runs exactly like a signed one. What checks signatures is whatever sits above the JVM: plugin frameworks that only install signed bundles, enterprise deployment policies, middleware such as Oracle E-Business Suite that requires signed applet-era JARs, and build pipelines that verify vendor artifacts. Signing is how you pass those checks and how anyone can confirm the archive is exactly what you published — not a switch that gates execution everywhere.
The bottom line
Find your alias with keytool -list, sign with jarsigner -keystore … -tsa http://time.certum.pl MyApp.jar myalias, and confirm with jarsigner -verify -strict. Two habits carry all the long-term weight: never sign without the timestamp, and always sign as the last build step. When your JARs need to verify on machines that have never heard of you, a Certum code signing certificate plugs into this exact workflow through PKCS#11 — and the same certificate signs your Windows binaries too.