Is there a way to unsign a java jar file? I have some signed jars I'm trying to use in my development environment, but I get security exceptions, so I want to unsign those jars and I can sign them later when I'm ready to deploy.
7 Answers
To remove the signature from a jar file, remove the META-INF
directory from it. A jar file is a zip file so on Linux you can do this:
zip -d file.jar 'META-INF/*.SF' 'META-INF/*.RSA'
If you have many jar files to unsign, the following command performs this on every jar file in the current directory and below:
find . -name '*.jar' -exec zip -d '{}' 'META-INF/*.SF' 'META-INF/*.RSA' ';'
I see an answer is already accepted, but i think this might be usefull anyway:
i've cooked up something (partly from other posts) to automate the task.
Comes with No warranty whatsoever, but it works for me :)
Copies the Jar file while removing the signature information.
Note, the MANIFEST is only left with the MAIN section!
Use javac JarUnsigner.java
to create the .class file
Use java -cp <class dir> JarUnsigner <inJar> <outJar>
import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Enumeration;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;
public class JarUnsigner {
private static final String MANIFEST = "META-INF/MANIFEST.MF";
public static void main(String[] args){
if (args.length!=2){
System.out.println("Arguments: <infile.jar> <outfile.jar>");
System.exit(1);
}
String infile = args[0];
String outfile = args[1];
if ((new File(outfile)).exists()){
System.out.println("Output file already exists:" + outfile);
System.exit(1);
}
try{
ZipFile zipFile = new ZipFile(infile);
final ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(outfile));
for (Enumeration e = zipFile.entries(); e.hasMoreElements();) {
ZipEntry entryIn = (ZipEntry) e.nextElement();
if (! exclude_file( entryIn.getName() ) ) {
/* copy the entry as-is */
zos.putNextEntry( new ZipEntry( entryIn.getName() ));
InputStream is = zipFile.getInputStream(entryIn);
byte[] buf = new byte[1024];
int len;
while ((len = (is.read(buf))) > 0) {
zos.write(buf, 0, len);
}
zos.closeEntry();
} else {
if (MANIFEST.equals(entryIn.getName())){
/* if MANIFEST, adjust the entry */
zos.putNextEntry(new ZipEntry(MANIFEST));
// manifest entries until first empty line. i.e. the 'MainAttributes' section
// (this method is used so to keep the formatting exactly the same)
InputStream mIS = zipFile.getInputStream(entryIn);
BufferedReader in = new BufferedReader(new InputStreamReader(mIS));
String line = in.readLine();
byte[] mNL = "\n".getBytes("UTF-8");
while( line != null && !line.trim().isEmpty() ) {
zos.write( line.getBytes("UTF-8"));
zos.write( mNL );
line = in.readLine();
}
zos.write( mNL );
zos.closeEntry();
}else{
/* else: Leave out the Signature files */
}
}
}
zos.close();
System.out.println("Successfully unsigned " + outfile);
}catch(IOException ex){
System.err.println("Error for file: " + infile);
ex.printStackTrace();
System.exit(1);
}
}
/**
* Exclude .SF signature file
* Exclude .RSA and DSA (signed version of .SF file)
* Exclude SIG- files (unknown sign types for signed .SF file)
* Exclude Manifest file
* @param filename
* @return
*/
public static boolean exclude_file(String filename){
return filename.equals("META-INF/MANIFEST.MF") ||
filename.startsWith("META-INF/SIG-") ||
filename.startsWith("META-INF/") && ( filename.endsWith(".SF") || filename.endsWith(".RSA") || filename.endsWith(".DSA") );
}
}
Use in ANT to unsign a bunch of jars as follows:
<apply executable="java" dest="${output-dir}">
<arg value="-cp" />
<arg value="${dev-dir}" />
<arg value="JarUnsigner" />
<srcfile/>
<targetfile/>
<fileset dir="${input-dir}" includes="*.jar"/>
<mapper type="glob" from="*.jar" to="*.jar"/> <!-- uses "dest"-->
</apply>
I successfully verified DwB's answer with small modification: As Is there a quick way to delete a file from a Jar / war without having to extract the jar and recreate it? states, deleting just from jar tool is not possible. I needed to make only small change in proprietary build script and I didn't want to rejar whole jar.
I realized unsign was possible when I only made important .RSA file zero-sized. This can be accomplished only with jar u
command:
cd %JAR_DIR%
jar xvf myapp.jar META-INF/MYAPP.RSA
type nul > META-INF/MYAPP.RSA
jar uvf myapp.jar META-INF/MYAPP.RSA
rmdir /S/Q META-INF
If you look at the jarsigner tool and what it does, it generates 3 things: 1) the .SF file (signature file) 2) a signature block file based on the algorithm used (e.g. .RSA, .DSA, etc.) 3) modification or creation of a MANIFEST.MF file
Summary: To "unsign a jar" just delete the first 2 files (.sf and .dsa/rsa FILE). Either delete the MANIFEST.MF file, or open it up and delete all of the Hashes listed for each .class and other file listed there).
So, if you delete EVERYTHING in the META-INF directory you run the risk of deleting other resources of the jar that might be needed (i.e. property files, etc.). This "shotgun approach" of deleting everything that "looks signature related" is harmful and doesn't follow the principle of: 1st do no harm (to your .jar).
see here: https://docs.oracle.com/javase/8/docs/technotes/tools/windows/jarsigner.html
https://docs.oracle.com/javase/tutorial/deployment/jar/intro.html
Understanding Signing and Verification
"...When you sign a JAR file your public key is placed inside the archive along with an associated certificate so that it's easily available for use by anyone wanting to verify your signature.... Digests and the Signature File
When you sign a JAR file, each file in the archive is given a digest entry in the archive's manifest. Here's an example of what such an entry might look like:
Name: test/classes/ClassOne.class SHA1-Digest: TD1GZt8G11dXY2p4olSZPc5Rj64=
When a JAR file is signed, a signature file is automatically generated and placed in the JAR file's META-INF directory, the same directory that contains the archive's manifest. Signature files have filenames with an .SF extension.
The Signature Block File
In addition to the signature file, a signature block file is automatically placed in the META-INF directory when a JAR file is signed. Unlike the manifest file or the signature file, signature block files are not human-readable.
The signature block file contains two elements essential for verification:
The digital signature for the JAR file that was generated with the signer's private key The certificate containing the signer's public key, to be used by anyone wanting to verify the signed JAR file Signature block filenames typically will have a .DSA extension indicating that they were created by the default Digital Signature Algorithm. Other filename extensions are possible if keys associated with some other standard algorithm are used for signing.
Another variant, that edits MANIFEST.MF too, ie deletes everything from the first empty line (edit the 'jarlocation' and 'signer' parts to your case):
Tmpdir=tmp.$$
mkdir -p "$Tmpdir"
cd "$Tmpdir"
for i in <jarlocation>/*.jar; do \
printf 'Doing %s\n' "$i"
unzip "$i" 'META-INF/*'
sed -i '/^[[:space:]]*$/,$d' META-INF/MANIFEST.MF
zip -r "$i" META-INF/MANIFEST.MF
test -f META-INF/<signer>.SF && zip -d "$i" 'META-INF/<signer>.*'
rm -rf META-INF
done
cd ..
rm -rf "$Tmpdir"
printf 'Unsigning finished\n'