用Java数字签名提供 安全
众所周知, 在产品和项目开发中起着非常重要的作用。通过 文档可以获取很多信息,还可以使用 文件进行CRUD(增加、查询、更新和 删除)操作。然而值得注意的是,我们如何确保 中的数据是来自经过认证的可信和可靠的来源。关于 文件数据的可靠性和真实性存在很多问题。通常的 情况是,开发者直接处理 文件而不去考虑数据的可靠性。有一些情况提出了上面的所有问题。现实生活中,每当我们从邮局收到一封信件时我们如何确定这封 信是来自我们的朋友?依据可能是他/她的习惯用语、用词或者邮件详细地址。也可能是他/她的个性签名。如今,我们收到的信件可能被某人进行了篡改,添加了 其他内容。基于上述原因,通常我们会验证朋友的手写签名。当然这些是关于来自邮局的普通邮件。电子消息又该如何?我们如何验证电子消息的真实性?这种情况 我们会采用数字签名。本文会对保证数据完整性的 数字签名技术进行简要介绍,并且展示如何为 文件附加电子签名及其验证过程。
使用的技术
过去几年里, 数字签名取得了快速发展,在金融领域尤其如此。在开始讨论之前,让我们考虑一个典型场景:想象一下,某个组织将所有雇员的薪资内 容用 文件发送给所得税部门。那么现在的问题是:所得税部门如何验证这份 文件?这就是说,IT部门需要验证该组织的敏感信息。IT部门需要确保 文件的来源可信,并且在IT部门收到之前没有经过篡改——也就是说文档的内容没有在传递中被修改。首先,我们需要理解数字签名的概念。数字签名是一 种用来验证文档发自可信方的电子签名。它确保了文档的原始内容在传输中没有受到修改。数字签名可以用于任何加密和非加密消息,因此接收方可以识别发送者的 身份,并确认消息没有被其他人修改。根据维基百科的定义:“数字签名是一种验证数字信息或文档的数学方法”。一个有效的数字签名可以让接收者确认收到的消 息来自已知发送方,发送者不能否认自己发送了此消息(提供认证和不可否认性)并且此消息在传输中未经修改(提供完整性)。数字签名通常被用在软件发布、金 融事务和其他需要检测伪造或篡改的重要场合。
下面让我们来看完整的一个带有数字签名的 文件:
<? version="1.0" encoding="UTF-8" standalone="no"?><SalaryDeposit>
<Organisation>
<Name>DDLab Inc</Name>
<AccountNo>SBC-12345789</AccountNo>
</Organisation>
<Employees>
<Emp>
<Name>John Abraham</Name>
<AccountNo>SB-001</AccountNo>
<Amount>1234</Amount>
</Emp>
<Emp>
<Name>Bipasha Basu</Name>
<AccountNo>SB-002</AccountNo>
<Amount>2334</Amount>
</Emp>
<Emp>
<Name>Vidya Balan</Name>
<AccountNo>SB-003</AccountNo>
<Amount>3465</Amount>
</Emp>
<Emp>
<Name>Debadatta Mishra</Name>
<AccountNo>SB-007</AccountNo>
<Amount>5789</Amount>
</Emp>
<Emp>
<Name>Priti Zinta</Name>
<AccountNo>SB-009</AccountNo>
<Amount>1234</Amount>
</Emp>
</Employees>
<Signature ns="http://www.w3.org/2000/09/ dsig#">
<SignedInfo>
<CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC- -c14n-20010315"/>
<SignatureMethod Algorithm="http://www.w3.org/2000/09/ dsig#rsa-sha1"/>
<Reference URI="">
<Transforms>
<Transform Algorithm="http://www.w3.org/2000/09/ dsig#enveloped-signature"/>
</Transforms>
<DigestMethod Algorithm="http://www.w3.org/2000/09/ dsig#sha1"/>
<DigestValue>bHS+6uf8KbJV4AGzoHNHLfnXvKM=</DigestValue>
</Reference>
</SignedInfo>
<SignatureValue>
aUEMrCT5dzeOfSNaznzoT0If8WZ8KQcMNXDqtoeseonVk3NqOk9ctcxrf3QVX3wP6810DDRPdI6l
e8ccG64Ge0HjkO+aYC5+c2L/qKBzwtSbl/olJEuFU2DVxBQO+K29TTUJfxpVzC9Zf2pvT+1NRj0f
2/ofHujYZ01D6+YqI8c=
</SignatureValue>
<KeyInfo>
<KeyValue>
<RSAKeyValue>
<Modulus>
jfAd5uV38L36+lDZJrqfH9oLN86VJezXYfAeU+lrFoHlKAXVJLAi9hKvBHQRer4tPfdez6iSBKsl
6IHkPnVRAKt0xU99uxi5QpymsWAX3qnBqHlw9Z70PwyZ+Xysfw4Q2tK2HtSgUOhMuaUcIf9sbHvf
gbvcRPgxDZZqfIzDmDU=</Modulus>
<Exponent>AQAB</Exponent>
</RSAKeyValue>
</KeyValue>
</KeyInfo>
</Signature>
</SalaryDeposit>
上面是一个带有签名的 文件,该文件可以随时进行验证。文件中包了含雇员名称、帐号和薪资信息。然而,实际的数字签名通 过<Signature></Signature>标记进行附加。<Signature> 标记中的信息提供了文档的真实性。正如你看到的那样,虽然你可以随意修改其中的数据,但是这种修改会在随后的签名验证中被查到。
基本上数字签名有三种类型:
- 封内签名
- 封外签名
- 分离签名
封内签名
这种签名是将签名作为 对象的子信息,也就是说 <Signature>是邮件中 文件的子标签。封内数字签名的结构如下:
<RootElement> <Signature> …… </Signature> </ RootElement>
本文会介绍如何创建 封内数字签名。
封外签名
这种签名将 文档包含到Signature对象,也就是说<Signature>标签是签名 文件的根元素。封外签名结构如下:
<Signature > < My Document > …… </ My Document > </Signature>
分离签名
这种情况下,签名是独立生成的不作为 的一部分。也就是说你会拥有两个 文件:一个待签名的 文件,另一个是 签名。下面是分离签名的 结构:
<Signature> …… </Signature>
数字签名文件结构如下:
<Signature ns="">
<SignedInfo>
<CanonicalizationMethod Algorithm="" />
<SignatureMethod Algorithm="" />
<Reference URI="">
<Transforms>
<Transform Algorithm="" />
</Transforms>
<DigestMethod Algorithm="" />
<DigestValue></DigestValue>
</Reference>
</SignedInfo>
<SignatureValue></SignatureValue>
<KeyInfo>
<KeyValue>
<RSAKeyValue>
<Modulus></Modulus>
<Exponent></Exponent>
</RSAKeyValue>
</KeyValue>
</KeyInfo>
</Signature>
中<Signature>有3个子标签,结构如下:
<Signature> <SignedInfo></SignedInfo> <SignatureValue></SignatureValue> <KeyInfo></KeyInfo> </Signature>
这里<Signature>是 数字签名的根元素,这一点由W3C建议并且必须遵守。<SignedInfo>元素是你的签名信息;<SignatureValue>包含了实际的签名以及使用 64加密的内容;最后<KeyInfo>表示公钥。让我们再看一下<SignedInfo>标签,结构如下:
<SignedInfo>
<CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC- -c14n-20010315"/>
<SignatureMethod Algorithm="http://www.w3.org/2000/09/ dsig#rsa-sha1"/>
<Reference URI="">
<Transforms>
<Transform Algorithm="http://www.w3.org/2000/09/ dsig#enveloped-signature"/>
</Transforms>
<DigestMethod Algorithm="http://www.w3.org/2000/09/ dsig#sha1"/>
<DigestValue>bHS+6uf8KbJV4AGzoHNHLfnXvKM=</DigestValue>
</Reference>
</SignedInfo>
当使用Java创建 数字签名时,SignedInfo对象被用来在数字签名的Signature标签内创建元素。这也是W3C建议的 签名标准中的一部分。
标签<KeyInfo>的结构如下:
<KeyInfo>
<KeyValue>
<RSAKeyValue>
<Modulus></Modulus>
<Exponent></Exponent>
</RSAKeyValue>
</KeyValue>
</KeyInfo>
KeyInfo>标记包含了需要数学计算的相关信息,主要有公钥的系数和指数。
要创建 数字签名可以遵循下列步骤:
- 生成一组私钥和公钥。
- 获得原始 文件。
- 通过Java API使用私钥和公钥为原始的 文件签名,生成带有 签名的文档。
让我们看看使用Java生成 签名的相关代码:
public void generate DigitalSignature(String original FilePath,
String destnSigned FilePath, String privateKeyFilePath, String publicKeyFilePath) {
// 获取 文档对象
Document doc = get Document(original FilePath);
// 创建 签名工厂
SignatureFactory SigFactory = SignatureFactory.getInstance("DOM");
PrivateKey privateKey = new KryptoUtil().getStoredPrivateKey(privateKeyFilePath);
DOMSignContext domSignCtx = new DOMSignContext(privateKey, doc.getDocumentElement());
Reference ref = null;
SignedInfo signedInfo = null;
try {
ref = SigFactory.newReference("", SigFactory.newDigestMethod(DigestMethod.SHA1, null),
Collections.singletonList( SigFactory.newTransform(Transform.ENVELOPED,
(TransformParameterSpec) null)), null, null);
signedInfo = SigFactory.newSignedInfo(
SigFactory.newCanonicalizationMethod(CanonicalizationMethod.INCLUSIVE,
(C14NMethodParameterSpec) null),
SigFactory.newSignatureMethod(SignatureMethod.RSA_SHA1, null),
Collections.singletonList(ref));
} catch (NoSuchAlgorithmException ex) {
ex.printStackTrace();
} catch (InvalidAlgorithmParameterException ex) {
ex.printStackTrace();
}
// 传入公钥路径
KeyInfo keyInfo = getKeyInfo( SigFactory, publicKeyFilePath);
// 创建新的 签名
Signature Signature = SigFactory.new Signature(signedInfo, keyInfo);
try {
// 对文档签名
Signature.sign(domSignCtx);
} catch (MarshalException ex) {
ex.printStackTrace();
} catch ( SignatureException ex) {
ex.printStackTrace();
}
// 存储签名过的文档
storeSignedDoc(doc, destnSigned FilePath);
}
签名验证
数字签名的验证包含以下操作:
验证数字签名
- 计算<SignedInfo>元素摘要。
- 使用公钥解密<SignatureValue>元素。
- 比较上面两个值。
- 计算引用摘要
- 重新计算<SignedInfo>元素引用摘要。
- 将它们与<DigestValue>中的摘要比较。
为了验证 签名文档,需要完成下列步骤
- 得到 文档和公钥。
- 验证<SignedInfo> 元素的数字签名。
- 计算<SignedInfo> 元素的摘要并对值进行比较。
让我们看看下面这段 数字签名示例代码:
public static boolean is DigitalSignatureValid(String signed FilePath, String pubicKeyFilePath) throws Exception {
boolean validFlag = false;
Document doc = get Document(signed FilePath);
NodeList nl = doc.getElementsByTagNameNS( Signature. NS, "Signature");
if (nl.getLength() == 0) {
throw new Exception("No Digital Signature Found, document is discarded");
}
PublicKey publicKey = new KryptoUtil().getStoredPublicKey(pubicKeyFilePath);
DOMValidateContext valContext = new DOMValidateContext(publicKey, nl.item(0));
SignatureFactory fac = SignatureFactory.getInstance("DOM");
Signature signature = fac.unmarshal Signature(valContext);
validFlag = signature.validate(valContext);
return validFlag;
}
如上面示例代码所示, 签名可以通过重新计算<SignedInfo>的摘要值进行验证,验证算法由 <SignatureMethod>元素指定;使用公钥可以验证<SignedInfo>摘要中 的<SignatureValue>值是否正确。 引用摘要会在<SignedInfo>元素中重新计算,并与<Reference> 元素中对应的<DigestValue> 进行比对。接下来,让我们熟悉一下 数字签名相关的Java组件。
SignatureFactory
SignatureFactory是生成 文档数字签名的工厂对象。对象的创建如下列代码所示:
SignatureFactory factory = SignatureFactory.getInstance("DOM");
DOMSignContext
DOMSignContext对象用来生成DOM树。在创建数字签名的过程中,DOM树会被附上 数字签名。DOMSignContext对象要求输入私钥和 文档的根元素。
Reference
Reference对象用来在Signature 标记的SignedInfo内部创建 数字签名。对象创建的遵循“W3C 签名文法和处理”规则。Reference的基本结构如下:
<Reference URI=""> <Transforms> <Transform Algorithm="http://www.w3.org/2000/09/ dsig#enveloped-signature"/> </Transforms> <DigestMethod Algorithm="http://www.w3.org/2000/09/ dsig#sha1"/> <DigestValue>bHS+6uf8KbJV4AGzoHNHLfnXvKM=</DigestValue> </Reference>
SignedInfo
类似的,SignedInfo对象可以在数字签名的Signature标记内部创建元素。创建的规则同样遵循“W3C 数字签名协议”。SignedInfo的基本结构如下:
<SignedInfo>
<CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC- -c14n-20010315"/>
<SignatureMethod Algorithm="http://www.w3.org/2000/09/ dsig#rsa-sha1"/>
<Reference URI="">
<Transforms>
<Transform Algorithm="http://www.w3.org/2000/09/ dsig#enveloped-signature"/>
</Transforms>
<DigestMethod Algorithm="http://www.w3.org/2000/09/ dsig#sha1"/>
<DigestValue>bHS+6uf8KbJV4AGzoHNHLfnXvKM=</DigestValue>
</Reference>
</SignedInfo>
Signature
最后, Signature对象用来创建 文档的封面签名。按照W3C的建议,签名对象应该作为 数字签名的根元素。
完整的结构如下:
<Signature ns="http://www.w3.org/2000/09/ dsig#">
<SignedInfo>
<CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC- -c14n-20010315"/>
<SignatureMethod Algorithm="http://www.w3.org/2000/09/ dsig#rsa-sha1"/>
<Reference URI="">
<Transforms>
<Transform Algorithm="http://www.w3.org/2000/09/ dsig#enveloped-signature"/>
</Transforms>
<DigestMethod Algorithm="http://www.w3.org/2000/09/ dsig#sha1"/>
<DigestValue>bHS+6uf8KbJV4AGzoHNHLfnXvKM=</DigestValue>
</Reference>
</SignedInfo>
<SignatureValue>aUEMrCT5dzeOfSNaznzoT0If8WZ8KQcMNXDqtoeseonVk3NqOk9ctcxrf3QVX3wP6810DDRPdI6l
e8ccG64Ge0HjkO+aYC5+c2L/qKBzwtSbl/olJEuFU2DVxBQO+K29TTUJfxpVzC9Zf2pvT+1NRj0f
2/ofHujYZ01D6+YqI8c=</SignatureValue>
<KeyInfo>
<KeyValue>
<RSAKeyValue>
<Modulus>jfAd5uV38L36+lDZJrqfH9oLN86VJezXYfAeU+lrFoHlKAXVJLAi9hKvBHQRer4tPfdez6iSBKsl
6IHkPnVRAKt0xU99uxi5QpymsWAX3qnBqHlw9Z70PwyZ+Xysfw4Q2tK2HtSgUOhMuaUcIf9sbHvf
gbvcRPgxDZZqfIzDmDU=</Modulus>
<Exponent>AQAB</Exponent>
</RSAKeyValue>
</KeyValue>
</KeyInfo>
</Signature>
为了有一个完成的理解,可以从这里下载完整的Netbeans项目代码。
可以用你最喜欢的Java IDE对项目进行配置;也可以在source文件夹下运行程序。这个项目已经包含了公钥和私钥。如果想要自己生成,可以运行 “TestGenerateKeys”类生成一对公钥和私钥。通过指定自己的XMI文件,还可以查看 签名的生成过程。
以上就是本次我们给大家整理的内容的全部,感谢大家对脚本之家的支持,如果大家还有不明白的可以在下方留言区讨论。
继续阅读与本文标签相同的文章
vue2.0 下拉框默认标题设置方法
-
给大家推荐一本深入学习 Kotlin 的书
2026-05-18栏目: 教程
-
终于把Apollo存储加密这件事搞定了
2026-05-18栏目: 教程
-
Excel导入导出
2026-05-18栏目: 教程
-
18段代码带你玩转18个机器学习必备交互工具
2026-05-18栏目: 教程
-
Istio的架构设计
2026-05-18栏目: 教程
