java安全——数字签名+代码签名

【0】README
1)本文文字描述转自 core java volume 2, 旨在学习 java安全——数字签名 的基础知识;
2)本文实践内容以及截图笔记均为原创;

3)如果要给予applet更多的信赖,你必须知道下面两件事:

3.1) applet来自哪里?

3.2) 在传输过程中代码是否被破坏?

-------------------------------------------------------------------------------------------------------

【1】消息摘要

1)消息摘要(message digest): 是数据块的数字指纹。(干货——消息摘要定义)

2)消息摘要具有两个基本属性(properties):

p1) 如果数据的1位或者几位改变了,那么消息摘要也将改变。

p2) 拥有给定消息的伪造者不能创建与原消息具有相同摘要的假消息。

3)看个荔枝:

让我们来看看下面这位亿万富翁留下的遗嘱: "我死了之后,我的财产将由我的孩子平分,但是,我的儿子George应该拿不到一个子。"  这份遗嘱的SHA1指纹为: 2D 8B 35 F3 BF 49 CD B1 94 04 E0 66 21 2B 5E 57 70 49 E1 7E;这位有疑心病的父亲将这份遗嘱交给一位律师保存,而将指纹交给另一位律师保存。现在,假设George能够贿赂那位保存遗嘱的律师,他想修改这份遗嘱,使得Bill一无所得。当然,这需要将原指纹改为下面这样完全不同的位模式: 2A 33 0B 4B B3 FE CC 1C 9D 5C 01 A7 09 51 0B 49 AC 8F 98 92; 那么George能够找到与该指纹相匹配的其他文字吗?如果从地球形成之时,他就很自豪地拥有10亿台计算机,每台计算机每秒钟能处理一百万条信息,他依然无法找到一个能够替换的遗嘱。

4) 计算消息摘要的算法:  其中最著名的两种算法是SHA1和MD5。SHA1是由美国国家标准和技术学会开发的加密散列算法,MD5是由麻省理工学院的Ronald Rivest发明的算法。
5)Attention:许多密码人员建议最好避免使用MD5,而应该使用SHA1算法,直到有更强的加密算法出现。 (干货——许多密码人员建议最好避免使用MD5,而应该使用SHA1算法)

6)Java编程语言已经实现了SHA1和MD5。

6.1)MessageDigest类: 是用于创建封装了指纹算法的对象的"工厂",它的静态方法getInstance返回继承了MessageDigest类的某个类的对象。

6.2) 这意味着MessageDigest类能够承担下面的双重职责(responsibilities):

r1)作为一个工厂类。
r2)作为所有消息摘要算法的超类。

6.2)如何获取消息摘要:

step1)下面是如何获取一个能够计算SHA指纹的对象的方法:

MessageDigest alg = MessageDigest.getInstance("SHA-1");
 or MessageDigest alg = MessageDigest.getInstance("MD5");

step2) 当你已经获取MessageDigest对象之后,通过反复调用update方法,将信息中的所有字节提供给该对象。

 
  
InputStream in = . . .
int ch;
while ((ch = in.read()) != -1)
alg.update((byte) ch);


step2.1)如果这些字节存放在一个数组中,那就可以一次完成整个数组的更新: 

byte[] bytes = . . .;
alg.update(bytes);

step3) 当完成上述操作后,调用digest方法。该方法填充输入信息-指纹算法需要的-并且进行相应的计算,然后以字节数组的形式返回消息摘要。

byte[] hash = alg.digest();

java安全——数字签名+代码签名_第1张图片
for complete source code, please visit    https://github.com/pacosonTang/core-java-volume/blob/master/coreJavaAdvanced/chapter9/digital_signature/Digest.java 


【2】消息签名
1)我们介绍了如何计算原始消息的消息摘要和指纹的方法。 如果消息改变了,那么改变后的消息的指纹与原消息的指纹将不匹配。如果消息和它的指纹是分开传送的,那么接收者就可以检查消息是否被篡改过。
2)problem+solution: 
2.1)problem:但是,如果消息和指纹同时被截获了,对消息进行修改,再重新计算指纹,这是一件很容易的事情。毕竟,消息摘要算法是公开的,不需要使用任何密钥。在这种情况下,假消息和新指纹的接收者永远不会知道消息已经被篡改。
2.2)solution: 数字签名解决了这个问题。(干货——引入数字签名的原因)
3)公共密钥加密技术: 是基于公共密钥和私有密钥这个两个基本概念的。
3.1)它的设计思想是: 你可以将公共密钥告诉世界上的任何人,但是,只有自己才拥有私有密钥,重要的是你要保护你的私有密钥,不将它泄漏给其他任何人。这些密钥之间存在一定的数学关系,但是这种关系的具体性质对于实际的编程来说并不重要。
3.2)在现实中,几乎不可能用一个密钥去推算出另一个密钥。也就是说,即使每个人都知道你的公共密钥,不管他们拥有多少计算资源,他们一辈子也无法计算出你的私有密钥。
4)看个荔枝(使用DSA 算法 进行公共密钥签名的交换)

4.1)荔枝背景:假设Alice想要给Bob发送一个消息,Bob想知道该消息是否来自Alice,而不是冒名顶替者。Alice写好了消息,并且用她的私有密钥对该消息摘要签名。Bob得到了她的公共密钥的拷贝,然后Bob用公共密钥对该签名进行校验。

4.2)如果通过了校验,则Bob可以确认以下两个事实(truth):

t1) 原始消息没有被篡改过;

t2) 该消息是由Alice签名的,她是私有密钥的持有者,该私有密钥就是与Bob用于校验的公共密钥相匹配的密钥;

4.3)你可以看到私有密钥的安全性为什么是最重要的。 如果某个人偷了Alice的私有密钥,或者政府要求她交出私有密钥,那么她就麻烦了。小偷或者政府代表就可以假扮她的身份来发送消息和资金转帐指令等等,而其他人则会相信这些消息确实来自于Alice。

java安全——数字签名+代码签名_第2张图片


【3】校验签名
1)JDK配有一个 keytool程序:该程序是一个命令行工具,用于生成和管理一组证书。
2)我们现在要做的是: 使用keytool工具来展示Alice是如何对一个文档进行签名并且将它发送给Bob的,而Bob又是如何校验该文档确实是由Alice签名,而不是冒名顶替的。

3)keytool程序的功能: 负责管理密钥库、证书数据库和私有密钥。密钥库中的每一项都有一个"别名"。

3.1)看个荔枝:下面展示的是Alice如何创建一个密钥库alice.store 并且用别名生成一个密钥对的。

keytool -genkey -keystore alice.store -alias alice

3.2)当生成一个密钥时,系统提示你输入下面这些信息:
java安全——数字签名+代码签名_第3张图片
3.3) 假设Alice想把她的公共密钥提供给Bob,她必须导出一个证书文件: (干货——证书的定义和作用)
java安全——数字签名+代码签名_第4张图片
3.4) 这时,Alice就可以把证书发送给Bob。当Bob收到该证书时,他就可以将证书打印出来:
java安全——数字签名+代码签名_第5张图片
3.5) 如果Bob想检查他是否得到了正确的证书,可以给Alice打电话,让她在电话里读出证书的指纹。
3.6)一旦Bob信任该证书,他就可以将它导入密钥库中。
java安全——数字签名+代码签名_第6张图片

4)现在Alice就可以开始给Bob发送签过名的文档了
4.1)jarsigner工具负责对JAR文件进行签名和校验,Alice只需要将文档添加到要签名的JAR文件中。
jar cvf document.jar document.txt
4.2)然后她使用jarsigner工具将签名添加到文件中,她必须指定要使用的密钥库、JAR文件和密钥的别名。
java安全——数字签名+代码签名_第7张图片

4.3)当Bob收到JAR文件时,他可以使用jarsigner程序的-verify选项,对文件进行校验。
jarsigner -verify -keystore bob.store document.jar

4.4)Bob不需要设定密钥别名。该jarsigner程序会在数字签名中找到密钥所有者的X.500名字,并在密钥库中搜寻匹配的证书。

4.5)如果JAR文件没有受到破坏而且签名匹配,那么jarsigner程序将打印:  jar verified. 否则,程序将显示一个出错消息。

java安全——数字签名+代码签名_第8张图片


【4】认证问题

1)看个荔枝:假设你从一个声称代表某著名软件公司的陌生人那里获得了一个消息,他要求你运行消息附带的程序。这个陌生人甚至将他的公共密钥的拷贝发送给你,以便让你校验他是否是该消息的作者。你检查后会发现该签名是有效的,这就证明该消息是用匹配的私有密钥签名的,并且没有遭到破坏。这时候你要小心:你仍然不清楚谁写的这条消息。

2)认证问题定义:任何人都可以生成一对公共密钥和私有密钥,再用私有密钥对消息进行签名,然后把签名好的消息和公共密钥发送给你。这种确定发送者身份的问题称为"认证问题"。(干货——认证问题定义)

3)认证问题的solution: 解决这个认证问题的通常做法是比较简单的。假设陌生人和你有一个你们俩都值得信赖的共同熟人。假设陌生人亲自约见了该熟人,将包含公共密钥的磁盘交给了他。后来,你的熟人与你见面,向你担保他与该陌生人见了面,并且该陌生人确实在那家著名的软件公司工作,然后将磁盘交给你 。如此这样,你的熟人就证明了陌生人身份的真实性。

java安全——数字签名+代码签名_第9张图片

4)你不需要与熟人见面的: 取而代之的是,他可以将他的私有签名应用于陌生人的公共密钥文件之上即可。
java安全——数字签名+代码签名_第10张图片
5)信任模型: 然而,你们之间可能没有共同的熟人。有些信任模型假设你们之间总是存在一个"信任链"-即一个共同熟人的链路-这样你就可以信任该链中的每个成员。
6)你常常会遇到由负责担保他人身份的一个或多个实体签署的数字签名。你必须评估一下究竟能够在多大程度上信任这些身份认证人。你可能非常信赖VeriSign公司,因为也许你在许多网页中都看到过他们公司的标志,或者你曾经听说过,每当有新的万能密钥产生时,他们就会要求在一个非常保密的会议室中聚集众多揣着黑色公文包的人进行磋商。

【5】证书签名
1)problem+solution:
1.1)problem: 假设Alice想要给同事 Cindy 发送一条经过签名的消息,但是Cindy并不希望因为要校验许多签名指纹而受到困扰。
1.2)solution:  假设有一个Cindy信任的实体来校验这些签名。在这个例子中,Cindy信任ACME软件公司的信息资源部。
2)证书授权(CA-certificate authorization)的过程:
step1) 首先 需要创建一个密钥库acmesoft.store,生成一个密钥对并导出公共密钥;
java安全——数字签名+代码签名_第11张图片
step2) 其中的公共密钥被导入到了一个自签名的证书中,然后将其添加到每个雇员的密钥库中:
java安全——数字签名+代码签名_第12张图片
step3)如果Alice要发送消息给Cindy以及ACME软件公司的其他任何人,她需要将她自己的证书签名, 并提交给信息资源部。
step4) 现在,Cindy将签名的证书导入到她的密钥库中:
keytool -importcert -keystore cindy.certs -alias alice -file alice_signedby_acmeroot.cer
step5) 密钥库要进行校验,以确定该密钥是由密钥库中已有的受信赖的根密钥签过名的,而且Cindy不必对证书的指纹进行校验。  一旦Cindy添加了根证书和经常给她发送文档的人的证书后,她就再也不用担心密钥库了。


【6】证书请求 
1)problem+solution 
1.1)problem: 在前一节中,我们用密钥库和CertificateSigner工具模拟了一个CA。但是,大多数CA都运行着更加复杂的软件来管理证书,并且使用的证书格式也略有不同。本节将展示与这些软件包进行交互时需要增加的处理步骤。
1.2)solution:我们将用OpenSSL软件包作为实例。 (干货——OpenSSL 可以生成 CA(certificate authentication-证书授权))
2)solution的具体步骤:
step1) 为了创建一个CA,需要运行CA脚本 , 如 
/usr/lib/ssl/misc/CA.pl -newca
这个脚本会在当前目录中创建一个demoCA子目录,这个目录包含了一个根密钥对和有关证书与证书撤销列表的存储。
java安全——数字签名+代码签名_第13张图片
(openssl req -new -out my.pem
step2) 你希望将这个公共密钥导入到所有雇员的Java密钥库中,但是它的格式是隐私增强型邮件(PEM-Private Enhanced Mail)格式,而不是密钥库更容易接受的DER格式。将文件demoCA/cacert.pem 复制成文件acmeroot.pem,然后在文本编辑器中打开这个文件。移除下面这行之前的所有内容:
-----BEGIN CERTIFICATE-----

以及下面这行之后的所有内容:

-----END CERTIFICATE-----
java安全——数字签名+代码签名_第14张图片

step3)现在可以按照通常的方式将acmeroot.pem导入到每个密钥库中了:

keytool -importcert -keystore cindy.certs -alias alice -file acmeroot.pem

这看起来有点不可思议,keytool竟然不能自己去执行这种编辑操作。

step4)要对Alice的公共密钥签名,需要生成一个证书请求,它包含这个PEM格式的证书:

keytool -certreq -keystore alice.store -alias alice -file alice.pem

step5)要签名这个证书,需要运行:

openssl ca -in alice.pem -out alice_signedby_acmeroot.pem

step6)与前面一样,在alice_signedby_acmeroot.pem中切除BEGIN CERTIFICATE/END CERTIFICATE标记之外的所有内容。然后,将其导入到密钥库中:

keytool -importcert -keystore cindy.certs -alias alice -file 
alice_signedby_acmeroot.pem

你可以使用相同的步骤,使一个证书得到诸如VeriSign这样的公共证书权威机构的签名。

-------------------------------------------------------------------------------------------------------------------------------

【7】代码签名

1)认证技术最重要的一个应用是对可执行程序进行签名。
2)应用背景: 如果从网上下载一个程序,自然会关心该程序可能带来的危害,例如,该程序可能已经感染了病毒。如果知道代码从何而来,并且它从离开源头后就没有被篡改过,那么放心程度会比不清楚这些信息时要高得多。事实上,如果该程序是用Java语言编写的,那么就可以利用这些信息来理性地决定应该让该程序拥有什么样的优先权。

【7.1】 JAR 文件签名
1)下面是两种cases:
c1) 在企业内联网上传递:  系统管理员在本地机器上安装证书和策略文件。每当Java插件工具加载经过签名的代码时,它就会查询密钥库的签名和策略文件中的权限。安装证书和策略非常简单,每个桌面只需要安装一次。

c2) 在公众因特网上传递: 软件供应商获取由证书发放权威(如VeriSign公司)签名的证书。当最终用户访问一个包含已签名applet的Web站点时,就会弹出一个对话框,显示软件供应商的相关信息,并给最终用户两个选择,一个是给予applet全部权限,另一个是继续在沙盒中运行。

2)在本节的剩余部分: 我们将要介绍如何建立策略文件,来为已知来源的代码赋予特定的权限。创建和部署这些策略文件不是普通最终用户要做的,然而,系统管理员在准备部署企业内联网程序时需要做这些工作。 (干货——我们将要介绍如何建立策略文件,来为已知来源的代码赋予特定的权限。)
3)看个荔枝:ACME对含有程序代码的JAR文件进行签名的steps:
Attention)  .certs 后缀表示密钥库,而.cer后缀表示证书;
step1)ACME生成根证书:  keytool -genkeypair -keystore acmesoft.certs -alias acmeroot 

D:\tmp>mkdir jar_signatureD:\tmp>cd jar_signature
D:\tmp\jar_signature>keytool -genkeypair -keystore acmesoft.certs -alias acmeroot
输入密钥库口令:
再次输入新口令:
您的名字与姓氏是什么?
  [Unknown]:  tang
您的组织单位名称是什么?
  [Unknown]:  swjtu
您的组织名称是什么?
  [Unknown]:  swjtu
您所在的城市或区域名称是什么?
  [Unknown]:  chengdu
您所在的省/市/自治区名称是什么?
  [Unknown]:  sichuan
该单位的双字母国家/地区代码是什么?
  [Unknown]:  CN
CN=tang, OU=swjtu, O=swjtu, L=chengdu, ST=sichuan, C=CN是否正确?
  [否]:  y
输入  的密钥口令
        (如果和密钥库口令相同, 按回车):
再次输入新口令:
D:\tmp\jar_signature>dir
 驱动器 D 中的卷是 软件
 卷的序列号是 0006-7799
 D:\tmp\jar_signature 的目录
2016/02/21  22:05              .
2016/02/21  22:05              ..
2016/02/21  22:05             1,274 acmesoft.certs
               1 个文件          1,274 字节
               2 个目录 21,075,292,160 可用字节

step2)我们为公共证书建立第二个密钥库client.certs,并将公共的acmeroot证书添加进去: 
keytool -exportcert -keystore acmesoft.certs -alias acmeroot -file acmeroot.cer
keytool -importcert -keystore client.certs -alias acmeroot -file acmeroot.cer
 
     
D:\tmp\jar_signature>keytool -exportcert -keystore acmesoft.certs -alias acmeroot -file acmeroot.cer
输入密钥库口令:
存储在文件  中的证书
D:\tmp\jar_signature>keytool -importcert -keystore client.certs -alias acmeroot -file acmeroot.cer
输入密钥库口令:
再次输入新口令:
所有者: CN=tang, OU=swjtu, O=swjtu, L=chengdu, ST=sichuan, C=CN
发布者: CN=tang, OU=swjtu, O=swjtu, L=chengdu, ST=sichuan, C=CN
序列号: 717a234
有效期开始日期: Sun Feb 21 22:14:20 CST 2016, 截止日期: Sat May 21 22:14:20 CST 2016
证书指纹:
         MD5: 4E:6A:EA:66:B1:67:69:44:61:5F:E1:D0:51:12:18:EF
         SHA1: BF:E0:B8:76:E5:FA:A6:F6:E5:33:31:0B:B7:68:45:81:97:07:9B:FF
         SHA256: 1D:26:85:E1:62:C9:43:E3:28:C5:3F:AD:E7:D3:C8:C7:6A:24:92:14:18:2F:71:EF:28:7D:16:9B
:6D:AD:1A:28
         签名算法名称: SHA1withDSA
         版本: 3
扩展:
#1: ObjectId: 2.5.29.14 Criticality=false
SubjectKeyIdentifier [
KeyIdentifier [
0000: A8 83 5A D8 8C 45 4C 0D   C2 9A 4B 1D D5 8F 8B 3E  ..Z..EL...K....>
0010: 8C 3F 47 14                                        .?G.
]
]
是否信任此证书? [否]:  y
证书已添加到密钥库中

step3)为了创建一个经过签名的JAR文件,首先将各个类文件添加到JAR文件中,例如,
javac HelloWorld.java
jar cvf HelloWorld.jar *.class
 
      
D:\tmp\jar_signature>javac HelloWorld.java
D:\tmp\jar_signature>jar cvf HelloWorld.jar *.class
已添加清单
正在添加: HelloWorld.class(输入 = 428) (输出 = 292)(压缩了 31%)
step4) 然后ACME中某个信任的人运行jarsigner工具,指定JAR文件和私有密钥的别名: jarsigner -keystore acmesoft.certs HelloWorld.jar acmeroot
被签名的applet现在就已经准备好在Web服务器中部署了。
 
      

D:\tmp\jar_signature>jarsigner -keystore acmesoft.certs HelloWorld.jar acmeroot
输入密钥库的密码短语:
jar 已签名。
警告:
签名者证书将在六个月内过期。
未提供 -tsa 或 -tsacert, 此 jar 没有时间戳。如果没有时间戳, 则在签名者证书的到期日期 (2016-05-21) 或
以后的任何撤销日期之后, 用户可能无法验证此 jar。


step5)  接着,让我们转而配置客户机。必须将一个策略文件发布到每一台客户机上。 为了引用密钥库,策略文件将以下面这行开头:
keystore "keystoreURL", "keystoreType";
step5.1)  其中,URL可以是绝对的或相对的,其中相对URL 是相对于策略文件的位置而言的。如果密钥库是由keytool工具生成的,则它的类型是JKS。例如: 
keystore "file:certs.store", "JKS";
""grant子句可以有signedBy "alias"后缀,例如:
grant signedBy "acmeroot"
{
. . .
};
所有可以用与别名相关的公共密钥进行校验的签名代码现在都已经在grant语句中被授予了权限。
step6) 创建一个包含如下内容的策略文件HelloWorld.policy:
keystore "client.certs" "JKS"
grant signedBy "acmeroot"
{
	permission java.lang.RuntimePermission "usePolicy";
	permission java.io.FilePermission "D:\tmp\jar_signature\", "read";
};
step7) 最后,告诉applet浏览器使用该策略文件:
appletviewer -J-Djava.security.policy=HelloWorld.policy FileReadApplet.htm

Attention) 当然,我们这里没有用 applet class 作为实例文件,而是用 的 普通的一个读取   D:\tmp\jar_signature\ 目录下的文件的一个java 文件;

-------------------------------------------------------------------------------------------------------------------------------

【7.2】软件开发者证书
1)应用背景:   假设当你在因特网上冲浪时,遇到了一个Web站点,倘若你通过弹出的对话框为它授予了需要的权限,它就会运行一个来自不明提供商的applet或者Web启动应用。 这样的程序是用由证书权威机构发放的"软件开发者"证书进行签名的。弹出的对话框用于确定软件开发者和证书发放者的身份。

2)现在你有两个选择(select):

s1)用全部权限运行程序,或者

s2)将程序限制在沙盒中运行。(对话框中的Cancel按钮是一种误导。如果点击这个按钮,applet不会被取消,而是运行在沙盒中。)

3)那么什么样的因素可能会影响你的决定呢?下面是已经了解的情况:

3.1) Thawte公司将一个证书卖给了软件开发人员。

3.2) 程序确实是用该证书签名的,并且在传输过程中没有被篡改过。

3.3) 该证书确实是由Thawteit签名的,它是用本地cacerts文件中的公共密钥校验的。

4) 是否就意味着该代码可以安全运行了?

4.1)如果你只知道供应商的名字,以及Thawte公司卖给他们一个软件开发者证书这个事实,那么你会信赖该供应商吗?

4.2)如果想要担保ChemAxon Kft.不是个彻头彻尾的破解者,恐怕连Thawte公司自己也会陷入麻烦之中。

4.3)然而,没有一个证书发放者会对软件供应商的诚信度和资格能力进行广泛的审查。

Conclusion) 对于用软件开发者证书,我们没有太大的热情。  如果在公共互联网上的applet和网络启动的应用程序更努力地保持在它们各自的沙盒中运行,而这些沙盒又能够得到改进,那情况就会好得多。


你可能感兴趣的:(java安全——数字签名+代码签名)