经签名的Jar包内包含了以下内容:

原Jar包内的class文件和资源文件

签名文件 META-INF/*.SF:这是一个文本文件,包含原Jar包内的class文件和资源文件的Hash

签名block文件 META-INF/*.DSA:这是一个数据文件,包含签名者的 certificate 和数字签名。其中 certificate 包含了签名者的有关信息和 public key;数字签名是对 *.SF 文件内的 Hash 值使用 private key 加密得来使用 keytool 和 jarsigner 工具进行 Jar 包签名和验证

1、使用 keytool 和 jarsigner 工具进行 Jar 包签名和验证

JDK 提供了 keytool 和 jarsigner 两个工具用来进行 Jar 包签名和验证。

keytool 用来生成和管理 keystore。keystore 是一个数据文件,存储了 key pair 有关的2种数据:private key 和 certificate,而 certificate 包含了 public key。整个 keystore 用一个密码进行保护,keystore 里面的每一对 key pair 单独用一个密码进行保护。每对 key pair 用一个 alias 进行指定,alias 不区分大小写。

keytool 支持的算法是:

如果公钥算法为 DSA,则摘要算法使用 SHA-1。这是默认的

如果公钥算法为 RSA,则摘要算法采用 MD5

jarsigner 读取 keystore,为 Jar 包进行数字签名。jarsigner 也可以对签名的 Jar 包进行验证。

下面使用 keytool 和 jarsigner 对它进行签名和验证

第1步:用 keytool 生成 keystore

打开CMD窗口,键入如下命令生成keystore文件,其中jamesKeyStore 为公钥秘钥数据文件,james 是alias 的 key pair,keypass 的值123456是秘钥指令,storepass 的值123456是秘钥库指令

keytool -genkey -alias james -keypass 123456  -validity 3650 -keystore jamesKeyStore -storepass 123456

具体生成过程见下图:

第2步:用 jarsigner 对 Jar 包进行签名

使用如下命令可以在CMD窗口中验证签名JAR包

jarsigner -verify cd-vsb-protect-control-1.0-1.jar

2、JAVA验证JAR包签名

(1)、JDK对JAR包数字签名验证逻辑

JDK加载包文件提供了两个类JarFile和JarInputStream,两个类由如下构造方法,参数 boolean verify的作用是限制是否要生成JarVerifier对象,JarVerifier类的功能是提供验证JAR包签名的方法。

/*** Creates a new JarFile to read from the specified

* File object.

*@paramfile the jar file to be opened for reading

*@paramverify whether or not to verify the jar file if

* it is signed.

*@throwsIOException if an I/O error has occurred

*@throwsSecurityException if access to the file is denied

* by the SecurityManager.*/

public JarFile(File file, boolean verify) throwsIOException {this(file, verify, ZipFile.OPEN_READ);

}/*** Creates a new JarFile to read from the specified

* File object in the specified mode. The mode argument

* must be either OPEN_READ or OPEN_READ | OPEN_DELETE.

*

*@paramfile the jar file to be opened for reading

*@paramverify whether or not to verify the jar file if

* it is signed.

*@parammode the mode in which the file is to be opened

*@throwsIOException if an I/O error has occurred

*@throwsIllegalArgumentException

* if the mode argument is invalid

*@throwsSecurityException if access to the file is denied

* by the SecurityManager

*@since1.3*/

public JarFile(File file, boolean verify, int mode) throwsIOException {super(file, mode);this.verify =verify;

}

/*** Creates a new JarInputStream and reads the optional

* manifest. If a manifest is present and verify is true, also attempts

* to verify the signatures if the JarInputStream is signed.

*

*@paramin the actual input stream

*@paramverify whether or not to verify the JarInputStream if

* it is signed.

*@exceptionIOException if an I/O error has occurred*/

public JarInputStream(InputStream in, boolean verify) throwsIOException {super(in);this.doVerify =verify;//This implementation assumes the META-INF/MANIFEST.MF entry//should be either the first or the second entry (when preceded//by the dir META-INF/). It skips the META-INF/ and then//"consumes" the MANIFEST.MF to initialize the Manifest object.

JarEntry e = (JarEntry)super.getNextEntry();if (e != null && e.getName().equalsIgnoreCase("META-INF/"))

e= (JarEntry)super.getNextEntry();

first=checkManifest(e);

}privateJarEntry checkManifest(JarEntry e)throwsIOException

{if (e != null &&JarFile.MANIFEST_NAME.equalsIgnoreCase(e.getName())) {

man= newManifest();byte bytes[] = getBytes(new BufferedInputStream(this));

man.read(newByteArrayInputStream(bytes));

closeEntry();if(doVerify) {

jv= newJarVerifier(bytes);

mev= newManifestEntryVerifier(man);

}return (JarEntry)super.getNextEntry();

}returne;

}

如下代码所示在JarInputStream类对象调用getNextEntry方法获取JarEntry对象时,如果jv对象不为空时,要调用JarVerifier类的beginEntry方法,而此方法最总调用了ManifestEntryVerifier类的mev.setEntry(null, je)方法,

ManifestEntryVerifier类用来做JAR安全证书验证。

public ZipEntry getNextEntry() throwsIOException {

JarEntry e;if (first == null) {

e= (JarEntry)super.getNextEntry();if(tryManifest) {

e=checkManifest(e);

tryManifest= false;

}

}else{

e=first;if(first.getName().equalsIgnoreCase(JarIndex.INDEX_NAME))

tryManifest= true;

first= null;

}if (jv != null && e != null) {//At this point, we might have parsed all the meta-inf//entries and have nothing to verify. If we have//nothing to verify, get rid of the JarVerifier object.

if (jv.nothingToVerify() == true) {

jv= null;

mev= null;

}else{

jv.beginEntry(e, mev);

}

}returne;

}

/*** This method scans to see which entry we're parsing and

* keeps various state information depending on what type of

* file is being parsed.*/

public voidbeginEntry(JarEntry je, ManifestEntryVerifier mev)throwsIOException

{if (je == null)return;if (debug != null) {

debug.println("beginEntry "+je.getName());

}

String name=je.getName();/** Assumptions:

* 1. The manifest should be the first entry in the META-INF directory.

* 2. The .SF/.DSA/.EC files follow the manifest, before any normal entries

* 3. Any of the following will throw a SecurityException:

* a. digest mismatch between a manifest section and

* the SF section.

* b. digest mismatch between the actual jar entry and the manifest*/

if(parsingMeta) {

String uname=name.toUpperCase(Locale.ENGLISH);if ((uname.startsWith("META-INF/") ||uname.startsWith("/META-INF/"))) {if(je.isDirectory()) {

mev.setEntry(null, je);return;

}if (uname.equals(JarFile.MANIFEST_NAME) ||uname.equals(JarIndex.INDEX_NAME)) {return;

}if(SignatureFileVerifier.isBlockOrSF(uname)) {/*We parse only DSA, RSA or EC PKCS7 blocks.*/parsingBlockOrSF= true;

baos.reset();

mev.setEntry(null, je);return;

}//If a META-INF entry is not MF or block or SF, they should//be normal entries. According to 2 above, no more block or//SF will appear. Let's doneWithMeta.

}

}if(parsingMeta) {

doneWithMeta();

}if(je.isDirectory()) {

mev.setEntry(null, je);return;

}//be liberal in what you accept. If the name starts with ./, remove//it as we internally canonicalize it with out the ./.

if (name.startsWith("./"))

name= name.substring(2);//be liberal in what you accept. If the name starts with /, remove//it as we internally canonicalize it with out the /.

if (name.startsWith("/"))

name= name.substring(1);//only set the jev object for entries that have a signature//(either verified or not)

if (!name.equals(JarFile.MANIFEST_NAME)) {if (sigFileSigners.get(name) != null ||verifiedSigners.get(name)!= null) {

mev.setEntry(name, je);return;

}

}//don't compute the digest for this entry

mev.setEntry(null, je);return;

}

(2)、使用java验证JAR包签名

看了上面JDK提供的JAR相关的工具类,我们可以使用JarInputStream类的逻辑来验证,思想是通过空读取JarEntry对象验证包文件中的每个文件数字签名是否被篡改,在获取JarInputStream类对象时设置verify参数值为true,当声明需要做签名验证时在使用jarIn.getNextJarEntry()获取JarEntry对象如果文件被篡改会跑出异常java.lang.SecurityException: SHA-256 digest error for 文件名,这个时候表明JAR签名验证不通过。

,代码实现如下:

public static void verify(String path) throwsIOException{

File file= newFile(path);

InputStream in= newFileInputStream(file);

JarInputStream jarIn= new JarInputStream(in,true);while(jarIn.getNextJarEntry() != null){continue;

}

}

java jar 签名_JAR包数字签名与验证相关推荐

  1. java jar 和 war 包的区别

    一. jar 包 JAR(Java Archive,Java 归档文件)是与平台无关的文件格式,它允许将许多文件组合成一个压缩文件.JavaSE程序可以打包成Jar包(J其实可以理解为Java了). ...

  2. java修复工具_jar包修复工具箱

    jar包修复工具箱是一款针对java编程语言中的jar文件包而开发设计的一款非常好用的jar文件修复工具!在jar的传输和重新打包编译过程中,有可能出现文件丢失的情况,所以,为了减少不必要的麻烦,下载 ...

  3. Java jar war ear 包区别

    一.JAR包 A.简介 JAR包通常是开发时要引用的通用类,打成包便于存放管理.简单来说,JAR包就是别人写好的一些类,然后对这些类进行打包.将这些JAR包引入项目lib中,可以直接使用这些JAR包中 ...

  4. java jar包签名

    JAR文件可以用 jarsigner工具或者直接通过 java.securityAPI 签名.签名后的JAR 本身的文件 文件与原来JAR本身的 文件完全相同,只是更新了它的 manifest文件,并 ...

  5. java jnlp 签名_JAVA JNLP组件数字签名制作步骤

    为JAR签名需要两个工具: 1.用keytool来创建一个密匙(同时指定时效,多久会过期,默认只给 6个月) 2.用JARSigner用此密匙为JAR签名. 可以用同一个密匙来为多个JAR签名. 注意 ...

  6. java jar包收集

    activation~与javaMail有关的jar包,使用javaMail时应与mail.jar (mail.jar和activation.jar)一起加入到lib中去,具体负责mail的数据源和类 ...

  7. java jar下载_java jar包资源下载

    jar包有:commons-httpclient-3.1,commons-io-2.4,commons-io-2.6,commons-lang-2.5,commons-logging-1.2,curv ...

  8. java jar包 资源_java jar包资源下载

    jar包有:commons-httpclient-3.1,commons-io-2.4,commons-io-2.6,commons-lang-2.5,commons-logging-1.2,curv ...

  9. linux 测试环境启用jar_Linux下用java -jar运行可执行jar包的方法教程

    问题来源 一般来说,一个稍微大些的项目都会有一些依赖的Jar包,而在将项目部署到服务器的过程中,如果没有持续集成环境的话,也就是说服务器不支持在线编译及打包,那么需要自己上传依赖的Jar包,然而可能服 ...

最新文章

  1. ASP.NET MVC3 读书笔记三(Html辅助方法下)
  2. python买什么书好-python看什么书好
  3. 荣耀20搭载鸿蒙,荣耀20系列刚发布,搭载鸿蒙系统新机来袭,余承东已准备好!...
  4. 判断字符串_python实现--判断回文字符串、回文链表、回文数
  5. struts的国际化
  6. mysql调度触发器,MySQL触发器:达到某个datetime时更新
  7. jenkins java反序列化_Jenkins “Java 反序列化”过程远程命令执行漏洞
  8. 新零售时代招商的新鲜玩法——用全网联动 促销活动来招商
  9. The beautiful values of the palace(2019南京网络赛)
  10. nodeJS之域名DNS
  11. 拼团功能实现 php_PHP实现微信退款功能
  12. 语音识别技术突飞猛进 终有一天将超过人
  13. win7 计算机定时关机脚本,win7定时关机命令是什么 如何设置定时关机【图解】...
  14. 综合项目之闪讯破解(二)之 如何用C++建立PPPOE连接
  15. c 开发android原生程序,Android原生开发极简教程
  16. Springboot 406错误
  17. A*算法和dijkstra算法
  18. 惠普服务器故障代码_HP服务器常见代码
  19. 电气器件系列二十二:调速电机
  20. Get the information of all heroes in the League of Legends through the crawler.

热门文章

  1. BMS电池管理测试解决方案
  2. 推荐几个炫酷的Python开源项目
  3. IT咨询公司薪酬比较
  4. 缓解就业焦虑的利器,证书真的越多越有保障吗?
  5. Linux内核配置和编译
  6. 请问各位如何用Delphi控制U盘的安全拔出????已经有源代码,但是不知道如何控制指定的U盘...
  7. ::v-deep和/deep/区分
  8. 无贡献、无创新、无思路,ML领域准博士求助:论文到底要怎么创新? | Reddit热议...
  9. 小乖文章在线伪原创软件【长期更新】
  10. IP地址与Mac地址的关系与区别 - 学习/实践