Jar 包

    添加时间:2013-7-17 点击量:

    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();
    ifnull != certs && certs.length > 0) {
    for (Certificate cert : certs) {

    String alias
    = verify(cert, certMap);
    ifnull == 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;
    }








    我们永远不要期待别人的拯救,只有自己才能升华自己。自己已准备好了多少容量,方能吸引对等的人与我们相遇,否则再美好的人出现、再动人的事情降临身边,我们也没有能量去理解与珍惜,终将擦肩而过。—— 姚谦《品味》
    分享到: