Jar 包签名

 

1,加密、摘要和数字签名

(1)公钥加密算法

关于公钥加密算法,参考维基百科词条 Public-key cryptography

公钥加密算法又称为非对称密钥加密算法,因为它包含一个公钥-私钥对,称为key pair。即 key pair = private key + public key。

从功能上说,两个key作用相同,用一个key加密的消息,只能用另一个key解密,反之亦然。两个 key 的区别只在于谁拥有/知道它:private key 只有 key pair 的生成者知道,public key 则公开。key pair 的另一个特性是无法从一个 key 推算出另一个 key。

常用公钥加密算法是RSA和DSA。(TODO: 区别?)

(2)数字签名

关于数字签名,参考维基百科词条 Digital signature

数字签名算法是基于公钥加密算法的,其过程是:

  • 生成 key pair
  • 签名:对消息进行摘要获得其Hash值;用 private key 加密消息Hash获得数字签名(Signature)
  • 验证:对消息进行摘要获得其Hash值;用 public key 解开数字签名获得消息Hash;对两个Hash进行比对

由于 private key 无法伪造或从 public key 推算出,因此,消息发送者必为 private key 拥有者,由此确保了消息来源的真实性(Authentication)和不可否认性(Non-repudiation)。如果消息在发送过程中损坏或被篡改,进行摘要后Hash值必定不一致,数字签名验证无法通过,由此确保了消息内容的完备性(Integrity)。

(3)消息摘要

关于消息摘要,参考维基百科词条 Cryptographic hash function

消息摘要算法是使用一个Hash函数对任意长度的输入数据进行处理,输出固定长度的数据。输出数据称为消息摘要。无法从消息摘要倒推出消息内容。常用的消息摘要算法是 MD5 和 SHA-1。(TODO: 区别?)

2,Jar 包签名和验证

对Jar包的数字签名和验证过程和前面描述的数字签名原理和过程一致。Jar包是待发送的消息,经过签名后,Jar包内置入了数字签名和public key,验证者可以使用这两项数据进行验证。

实际上,经签名的Jar包内包含了以下内容:

  • 原Jar包内的class文件和资源文件
  • 签名文件 META-INF/*.SF:这是一个文本文件,包含原Jar包内的class文件和资源文件的Hash
  • 签名block文件 META-INF/*.DSA:这是一个数据文件,包含签名者的 certificate 和数字签名。其中 certificate 包含了签名者的有关信息和 public key;数字签名是对 *.SF 文件内的 Hash 值使用 private key 加密得来

(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 包进行验证。

下面以 JDK 中的 tools.jar 包为例,使用 keytool 和 jarsigner 对它进行签名和验证。

第1步:用 keytool 生成 keystore

执行以下命令,生成文件名为 test.ks 的 keystore,并生成 alias 为 testkey 的 key pair

keytool -keystore test.ks -genkey -alias testkey

根据屏幕提示输入各项信息

输入keystore密码:
再次输入新密码:
您的名字与姓氏是什么?
  [Unknown]:
您的组织单位名称是什么?
  [Unknown]:
您的组织名称是什么?
  [Unknown]:
您所在的城市或区域名称是什么?
  [Unknown]:
您所在的州或省份名称是什么?
  [Unknown]:
该单位的两字母国家代码是什么
  [Unknown]:
CN=Unknown, OU=Unknown, O=Unknown, L=Unknown, ST=Unknown, C=Unknown 正确吗?
  [否]:  y

输入<testkey>的主密码
        (如果和 keystore 密码相同,按回车):
再次输入新密码:

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

执行命令并按提示输入 keystore 和 testkey 的密码后,即可对 tools.jar 进行签名并输出为 tools_signed.jar

jarsigner -keystore test.ks -signedjar tools_signed.jar tools.jar testkey
输入密钥库的口令短语:
输入 testkey 的密钥口令:

警告:
签名者证书将在六个月内过期。

 第3步:用 jarsigner 对 Jar 包进行验证

执行以下命令,验证Jar 包签名是否有效

jarsigner -verify tools_signed.jar

输出

 
 
jar 已验证。

警告:
此 jar 包含签名者证书将在六个月内过期的条目。

要了解详细信息,请使用 -verbose 和 -certs 选项重新运行。
 
 

注意,以上命令只是使用 Jar 包内的签名文件,验证 public key 与生成签名的 private key 是否是有效 key pair,以及 Jar 包内容是否完整,并没有和 keystore 进行比对。如果需要验证 Jar 包是否是使用某一 keystore 内的密钥进行的签名,可以指定如下的命令和选项:

jarsigner -verify -verbose -keystore test.ks tools_signed.j
ar

输出如下,注意对每一个class或资源文件,前面状态标记中包含k,表明在 keystore 中找到了匹配的 certificate,也就是找到了匹配的 public key。如果需要打印出每一个 class文件或资源文件的 certificate 详细信息,可以增加 -certs 选项

 ...
smk     3384 Tue Jul 19 02:02:54 CST 2011 sun/tools/attach/HotSpotAttachProvider.class
smk     4597 Tue Jul 19 01:52:50 CST 2011 sun/tools/attach/HotSpotVirtualMachine.class
smk     3487 Tue Jul 19 02:02:54 CST 2011 sun/tools/attach/WindowsAttachProvider.class
smk     1001 Tue Jul 19 02:02:54 CST 2011 sun/tools/attach/WindowsVirtualMachine$PipedInputStream.class
smk     2796 Tue Jul 19 02:02:54 CST 2011 sun/tools/attach/WindowsVirtualMachine.class
           0 Tue Jul 19 01:52:50 CST 2011 sun/tools/jstack/
smk     4113 Tue Jul 19 01:52:50 CST 2011 sun/tools/jstack/JStack.class
           0 Tue Jul 19 01:53:02 CST 2011 sun/tools/jinfo/
smk     4325 Tue Jul 19 01:53:02 CST 2011 sun/tools/jinfo/JInfo.class
           0 Tue Jul 19 01:52:56 CST 2011 sun/tools/jmap/
smk     8177 Tue Jul 19 01:52:56 CST 2011 sun/tools/jmap/JMap.class

  s = 已验证签名
  m = 在清单中列出条目
  k = 在密钥库中至少找到了一个证书
  i = 在身份作用域内至少找到了一个证书

jar 已验证。

警告:
此 jar 包含签名者证书将在六个月内过期的条目。

要了解详细信息,请使用 -verbose 和 -certs 选项重新运行。

(2)编程进行 Jar 包签名的验证

 可以使用以下API在运行时对 Jar 包进行签名验证:

  • java.util.jar.JarFile
  • java.util.jar.JarEntry
  • java.security.KeyStore
  • java.security.cert.Certificate

读取 keystore 内的 certificate:

final String ksPath = ...
final String ksPass = ...
final HashMap<String, Certificate> certMap = new HashMap<String, Certificate>();
InputStream in
= new FileInputStream(ksPath); KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType()); ks.load(in, ksPass.toCharArray()); Enumeration<String> aliases = ks.aliases(); while (aliases.hasMoreElements()) { String alias = aliases.nextElement(); Certificate cert = s.getCertificate(alias);
certMap.put(alias, cert);
}
// while

验证 Jar 包:

final String jarPath = "G:\\tmp\\jar_sign_test\\tools_signed.jar";

JarFile jar = new JarFile(jarPath, true);
Enumeration<JarEntry> entries = jar.entries();
while (entries.hasMoreElements()) {
    JarEntry entry = entries.nextElement();

    // Verify the entry
    InputStream in = jar.getInputStream(entry);
    try {
        drain(in);
    } finally {
        try {
            in.close();
        } catch (Exception e) {
        }
    }

    Certificate[] certs = entry.getCertificates();
    if (null != certs && certs.length > 0) {
         for (Certificate cert : certs) {

               String alias = verify(cert, certMap);
               if (null == alias) {
                   ...
               } else {
                   ...
               }

           }// for
      }
} // while

根据 JarEntry.getCertificates() 方法 Java doc,在调用之前,必须首先将此 JarEntry 数据完全读取,因此上面的代码段中调用了一个 drain() 方法:

    private static void drain(InputStream in) throws IOException {
        byte[] buf = new byte[512];
        while (-1 != in.read(buf))
            ;
    }

verify() 方法用来检查 certificate 是否与 keystore 内的某个 certificate 匹配,主要用到了 Certificate.verify(PublicKey ) 方法:

    private static String verify(Certificate cert, HashMap<String, Certificate> map) {
        Iterator<String> it = map.keySet().iterator();
        while (it.hasNext()) {
            String alias = it.next();
            try {
                cert.verify(map.get(alias).getPublicKey());
                return alias;
            } catch (Exception e) {
                continue;
            }
        }// while

        return null;
    }

 

 

 

 

 

 

 

转载于:https://www.cnblogs.com/jackofhearts/p/jar_signing.html

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值