package demo.security.jarsign;
import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.jar.Attributes;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.jar.Manifest;
public class JarSignatureVerifier {
private String cause=null;
public static void main(String[] args) throws IOException {
JarSignatureVerifier verifier=new JarSignatureVerifier();
boolean verified=verifier.verify(new File(args[0]));
if(verified)
System.out.println(args[0]+" is verified OK.");
else {
String cause=verifier.getCause();
System.out.println(args[0]+" is not verified.");
System.out.println("The reason is "+cause);
}
}
public String getCause() {
if(cause==null)
return "";
else
return cause;
}
private boolean verify(File jarFile) throws IOException {
JarFile jar=new JarFile(jarFile);
//find all signed resources from MANIFEST.mf
InputStream is = jar.getInputStream(jar.getEntry("META-INF/MANIFEST.MF"));
Manifest man = new Manifest(is);
is.close();
Set<String> signed = new HashSet<String>();
for(Map.Entry<String, Attributes> entry: man.getEntries().entrySet()) {
for(Object attrkey: entry.getValue().keySet()) {
if (attrkey instanceof Attributes.Name &&
((Attributes.Name)attrkey).toString().indexOf("-Digest") != -1)
signed.add(entry.getKey());
}
}
//find all entries in jar
// contains all entries in the Manifest that are not signed.
// Ususally, the following will be excluded:
// * MANIFEST.MF itself
// * *.SF files containing the signature of MANIFEST.MF
// * *.DSA files containing public keys of the signer
Set<String> entries = new HashSet<String>();
for(Enumeration<JarEntry> entry = jar.entries(); entry.hasMoreElements(); ) {
JarEntry je = entry.nextElement();
if (!je.isDirectory()
&& !je.getName().equals("META-INF/MANIFEST.MF")
&& !(je.getName().startsWith("META-INF/") && je.getName().endsWith(".SF"))
&& !(je.getName().startsWith("META-INF/") && je.getName().endsWith(".DSA"))
)
entries.add(je.getName());
}
Set<String> unsigned = new HashSet<String>(entries);
unsigned.removeAll(signed);
// contains all the entries with a signature that are not present in the JAR
Set<String> missing = new HashSet<String>(signed);
missing.removeAll(entries);
if(unsigned.size()>0) {
cause="Some resources has not been signed.\n"+unsigned;
return false;
}
if(missing.size()>0) {
cause="Some resources is missing from jar.\n"+missing;
return false;
}
String cmd="jarsigner -verify "+jarFile.getAbsolutePath();
Process proc=Runtime.getRuntime().exec(cmd);
String cmdResult = new ProcessOutputReader().read(proc);
int exitVal = proc.exitValue();
if(exitVal==0)
return true;
else {
cause = cmdResult;
return false;
}
}
}
class ProcessOutputReader {
private int lineCnt=0;
/**
* 返回输出到控制台的行数
*
* @return
*/
public int getOutputLineCnt() {
return lineCnt;
}
/**
* 读出控制台的输出
*
* @param proc
* @return
*/
public String read(final Process proc){
final StringBuffer sb = new StringBuffer();
final String lineSep=System.getProperty("os.name").toLowerCase().startsWith("windows")?"\n\r":"\n";
Thread outputThread=new Thread(){
public void run(){
String line = "";
BufferedReader reader=null;
InputStream is=proc.getInputStream();
InputStreamReader bis=null;
try{
bis = new InputStreamReader(new BufferedInputStream(is),System.getProperty("file.encoding"));
reader=new BufferedReader(bis);
while((line=reader.readLine())!=null){
sb.append(line);
sb.append(lineSep);
lineCnt++;
}
}catch(IOException e){
}finally {
try {
reader.close();
} catch (IOException e) {
}
try {
bis.close();
} catch (IOException e) {
}
try {
is.close();
} catch (IOException e) {
}
}
}
};
Thread errorputThread=new Thread(){
public void run(){
String line = "";
BufferedReader reader=null;
InputStream is=proc.getErrorStream();
InputStreamReader bis=null;
try{
bis = new InputStreamReader(new BufferedInputStream(is),System.getProperty("file.encoding"));
reader=new BufferedReader(bis);
while((line=reader.readLine())!=null){
sb.append(line);
sb.append(lineSep);
}
}catch(IOException e){
}finally {
try {
reader.close();
} catch (IOException e) {
}
try {
bis.close();
} catch (IOException e) {
}
try {
is.close();
} catch (IOException e) {
}
}
}
};
outputThread.start();
errorputThread.start();
try{ //等待进程执行结束
proc.waitFor();
}catch(InterruptedException e){
}
try { //在进程结束后,确保proc的输出流都被读入到sb中
outputThread.join();
errorputThread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
return new String(sb);
}
}