jarsigner signs and verifies Java Archive (JAR) files using a private key and certificate from a keystore. Oracle ships it beside keytool in every JDK: you create the signing keypair with keytool -genkeypair, then jarsigner writes signature metadata under META-INF/ and anyone with your public certificate can check that the archive was not tampered with.
This guide builds a tiny runnable JAR on Ubuntu, signs it with a PKCS12 keystore, verifies the signature, and walks through the chain warnings jarsigner prints for a self-signed certificate — the same PKIX messages you see when a corporate code-signing CA is missing from trust. For keystore basics, see Java keystore vs truststore. For CA-signed signing keys, see Create a CSR and import a CA-signed certificate.
Tested on: Ubuntu 26.04 LTS; OpenJDK 25.0.3; kernel 7.0.0-27-generic.
Quick answer: keytool plus jarsigner
| Tool | Role |
|---|---|
keytool |
Create PKCS12 keystore, generate RSA keypair (PrivateKeyEntry), list aliases |
jarsigner |
Sign JAR (jar signed.) or verify signature (jar verified.) using keystore entry |
keytool -printcert -jarfile |
Read signer certificate from a signed JAR without opening the keystore |
jarsigner never generates keys — it only consumes what keytool stored.
Prerequisites
- OpenJDK 11+ with
keytool,jarsigner,jar, andjavac(Install keytool on Ubuntu) — see keytool command. - A writable working directory for the lab files.
- Password
changeitbelow matches other guides in this course.
Step 1 — Build a sample JAR
Create a minimal application and package it with an explicit Main-Class:
mkdir -p ~/jar-sign-lab/src/com/example ~/jar-sign-lab/build
cd ~/jar-sign-lab
cat > src/com/example/Hello.java << 'EOF'
package com.example;
public class Hello {
public static void main(String[] args) {
System.out.println("Hello from signed JAR lab");
}
}
EOF
javac -d build src/com/example/Hello.java
printf 'Manifest-Version: 1.0\nMain-Class: com.example.Hello\n\n' > manifest.txt
jar --create --file demo.jar -m manifest.txt -C build .Confirm the unsigned archive runs:
java -jar demo.jarHello from signed JAR labSigning happens after packaging. You can also sign an existing JAR from a build tool — jarsigner modifies the file in place unless you pass -signedjar.
Step 2 — Create a signing keypair with keytool
Generate a self-signed code-signing key inside PKCS12. For production you would use a CA-issued code-signing certificate; a self-signed key is enough to learn the jarsigner workflow and reproduce chain warnings:
STOREPASS=changeit
keytool -genkeypair -alias codesigner -keyalg RSA -keysize 2048 -validity 365 \
-keystore signer.p12 -storetype PKCS12 -storepass "$STOREPASS" \
-dname "CN=Demo Code Signer, OU=Lab, O=GoLinuxCloud, C=US" \
-ext "KU=digitalSignature" \
-ext "EKU=codeSigning"Confirm the certificate is marked for code signing — Oracle treats missing codeSigning extended key usage as a severe badExtendedKeyUsage warning during verify:
keytool -list -v -alias codesigner -keystore signer.p12 -storepass "$STOREPASS" \
| grep -A4 -E 'KeyUsage|ExtendedKeyUsage'ExtendedKeyUsages [
codeSigning
]
#2: ObjectId: 2.5.29.15 Criticality=false
KeyUsage [
DigitalSignature
]List the keystore — you need a PrivateKeyEntry alias for jarsigner:
keytool -list -keystore signer.p12 -storepass "$STOREPASS"Keystore type: PKCS12
Your keystore contains 1 entry
codesigner, Jul 3, 2026, PrivateKeyEntry,
Certificate fingerprint (SHA-256): 64:B3:CC:57:74:29:7C:DF:8D:9C:12:2A:59:B1:28:55:15:92:FE:F8:73:B1:81:1E:96:48:DC:2E:92:74:8F:81The alias name (codesigner) becomes the base name for META-INF/CODESIGN.SF and META-INF/CODESIGN.RSA (uppercased, max eight characters).
Step 3 — Sign the JAR with jarsigner
-storepass on the command line for repeatability. In production, avoid exposing keystore passwords in shell history, CI logs, or process listings. Use protected CI secrets, environment injection, prompt-based entry, or a secured file-based secret mechanism.
Point jarsigner at the keystore, store password, JAR file, and alias:
jarsigner -keystore signer.p12 -storetype PKCS12 -storepass "$STOREPASS" \
demo.jar codesignerjar signed.
Warning:
The signer's certificate is self-signed.jar signed. means signature files were written. The self-signed warning at sign time is informational — verification performs the full chain check.
To keep the original unsigned file untouched, package a fresh copy and write a separate signed output:
jar --create --file demo-unsigned.jar -m manifest.txt -C build .
jarsigner -keystore signer.p12 -storetype PKCS12 -storepass "$STOREPASS" \
-signedjar demo-signed.jar demo-unsigned.jar codesignerRun the signed output file:
java -jar demo-signed.jarHello from signed JAR labThe in-place demo.jar from the first sign command also runs with java -jar demo.jar — both archives contain the same signed classes.
Step 4 — Verify the signature
Basic verification checks that signatures match file digests:
jarsigner -verify demo.jarjar verified.
Warning:
This jar contains entries whose certificate chain is invalid. Reason: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
This jar contains entries whose signer certificate is self-signed.
This jar contains signed entries that are not signed by alias in this keystore.
This jar contains signatures that do not include a timestamp. Without a timestamp, users may not be able to validate this jar after any of the signer certificates expire (as early as 2027-07-03).
Re-run with the -verbose and -certs options for more details.jar verified. means the archive integrity check passed — no file listed in the signature was modified after signing. The warnings underneath are about trust in the signer certificate, not about tampering.
An unsigned copy fails immediately:
jar --create --file unsigned.jar -m manifest.txt -C build .
jarsigner -verify unsigned.jarjar is unsigned.What happens if the JAR changes after signing?
Modify a signed entry and verification fails. Add a new file after signing and jarsigner may still verify the original signed entries but warn about unsigned content — Oracle documents this in the jarsigner man page:
cp demo.jar demo-tampered.jar
echo "new file after signing" > extra.txt
jar --update --file demo-tampered.jar extra.txt
jarsigner -verify -verbose demo-tampered.jar? = unsigned entry
jar verified.
Warning:
This jar contains unsigned entries which have not been integrity-checked.The ? marker flags extra.txt as unsigned. Re-sign the JAR after all contents are final. The original demo.jar is unchanged and is used in the next steps.
Step 5 — Read chain warnings with -verbose -certs
Add -verbose -certs to see per-entry signature status and the PKIX chain result Oracle documents in the jarsigner man page:
jarsigner -verify -verbose -certs demo.jarTrimmed output from the lab:
s 203 Fri Jul 03 16:11:34 IST 2026 META-INF/MANIFEST.MF
>>> Signer
X.509, CN=Demo Code Signer, OU=Lab, O=GoLinuxCloud, C=US
Signature algorithm: SHA384withRSA, 2048-bit RSA key
[certificate is valid from 7/3/26, 4:11 PM to 7/3/27, 4:11 PM]
[Invalid certificate chain: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target]
sm 441 Fri Jul 03 16:11:26 IST 2026 com/example/Hello.class
>>> Signer
X.509, CN=Demo Code Signer, OU=Lab, O=GoLinuxCloud, C=US
Signature algorithm: SHA384withRSA, 2048-bit RSA key
[Invalid certificate chain: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target]
s = signature was verified
m = entry is listed in manifest
k = at least one certificate was found in keystore
jar verified.The sm prefix on Hello.class means the entry is listed in the manifest and its signature verified. Invalid certificate chain appears because the signer cert is self-signed and not anchored in a trusted certificate available to jarsigner during verification. That is expected in this lab.
When you verify with a truststore that contains the issuing CA — for example $JAVA_HOME/lib/security/cacerts for public code-signing CAs or a corporate code-signing CA truststore — chain validation can succeed:
jarsigner -verify -verbose -certs -keystore /path/to/truststore.p12 -storetype PKCS12 \
-storepass "$STOREPASS" demo.jarFor this self-signed lab keystore, passing -keystore signer.p12 may help jarsigner match the alias, but it does not turn a self-signed lab certificate into a publicly trusted signing certificate. With a CA-imported chain in the keystore, the >>> Signer block shows the alias and omits Invalid certificate chain. Timestamp warnings may still appear if you did not pass -tsa.
Step 6 — Use -strict for severe warnings
Oracle treats invalid chains and self-signed signers as severe warnings. Without -strict, jarsigner exits 0 even when those warnings print. With -strict, severe warnings become errors:
jarsigner -verify -strict demo.jarjar verified, with signer errors.
Error:
This jar contains entries whose certificate chain is invalid. Reason: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
This jar contains entries whose signer certificate is self-signed.
Warning:
This jar contains signed entries that are not signed by alias in this keystore.
This jar contains signatures that do not include a timestamp. Without a timestamp, users may not be able to validate this jar after any of the signer certificates expire (as early as 2027-07-03).Use -strict in CI pipelines when you want a non-zero exit code for untrusted signers, even if the JAR bytes were not tampered with.
Step 7 — Inspect the signer certificate
Read the embedded certificate without the keystore password:
keytool -printcert -jarfile demo.jarSample output:
Signer #1:
Signature:
Owner: CN=Demo Code Signer, OU=Lab, O=GoLinuxCloud, C=US
Issuer: CN=Demo Code Signer, OU=Lab, O=GoLinuxCloud, C=US
Serial number: ...
Valid from: Fri Jul 03 16:11:34 IST 2026 until: Sat Jul 03 16:11:34 IST 2027
Certificate fingerprints:
SHA256: 64:B3:CC:57:74:29:7C:DF:8D:9C:12:2A:59:B1:28:55:15:92:FE:F8:73:B1:81:1E:96:48:DC:2E:92:74:8F:81
Signature algorithm name: SHA384withRSA
Subject Public Key Algorithm: 2048-bit RSA key
Version: 3Owner equals Issuer here because the certificate is self-signed.
Fix chain warnings for real deployments
| Lab symptom | Production fix |
|---|---|
certificate chain is invalid / PKIX |
Ensure the keystore contains the full chain (trustedCertEntry for CA, PrivateKeyEntry for signer); distribute the CA to verifiers or use -keystore during verify |
signatures that do not include a timestamp |
Add -tsa http://timestamp.digicert.com (or your TSA URL) when signing |
signed entries not signed by alias in this keystore |
Pass -keystore and the expected alias during verify, or ignore if you only need integrity |
jar verified confirms digests match the signature. It does not mean every verifier will trust the signer. Treat chain warnings as a trust problem, not a signing failure.
Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
jarsigner: unable to sign jar / alias not found |
Wrong alias or empty keystore | keytool -list -keystore signer.p12 and match alias |
Keystore was tampered with, or password was incorrect |
Wrong -storepass |
Use the password from keytool -genkeypair |
jar is unsigned |
Never signed or signature stripped | Re-run jarsigner without -verify |
| Integrity OK but chain invalid | Self-signed or private CA not in trust | Import CA chain; see Fix PKIX path building failed |
unsigned entries which have not been integrity-checked |
Files added after signing | Re-sign the JAR after all contents are final |
badKeyUsage / badExtendedKeyUsage |
Cert not marked for code signing | Add -ext KU=digitalSignature -ext EKU=codeSigning to keytool -genkeypair or reissue with extendedKeyUsage = codeSigning |
References
- jarsigner man page — sign, verify,
-strict, errors and warnings - keytool man page —
-genkeypair,-printcert -jarfile - Signing JAR Files (Oracle Java tutorial) — deployment overview
Summary
Create a signing PrivateKeyEntry with keytool -genkeypair in PKCS12 (include EKU=codeSigning), then run jarsigner -keystore signer.p12 demo.jar codesigner to embed a signature. jarsigner -verify demo.jar prints jar verified when digests match. Add -verbose -certs to see Invalid certificate chain for a self-signed lab key — the signer is not anchored in a trusted certificate available during verification. Use -strict when CI should fail on untrusted chains, pass -keystore with your CA truststore for explicit verification, and add -tsa for timestamped signatures that survive certificate expiry.

