提到签名,大家都不陌生,大家知道,重大的文件一般都要领导签名,来确保这个文件的真实有效。而一些比较重要的合同,比如买房的购房合同,都要盖“骑缝章”,这个骑缝章,就是盖在2页纸中间的印章,它也代表了签名,它用来保证你合同的完整性。所以说,签名在日常生活中非常重要,它主要用来保证了信息的完整性。同样,计算机世界也对这个签名过程进行了模拟,数字签名的概念由此而生。
数字签名的过程:
一般数字签名的过程分为2个,一个是签名过程,一个是验证过程。
数字签名的基本过程如下:
(图稍后补上)
实践:
我们java.security包提供了一组API 来表示数字签名的过程,这个核心类就是Signature类,它提供了一组方法来签名和验证,我们这里就给出这个类的一般用法:
我们先建立一个工具类,这个类是个单例,它主要是提供了一些封装方法来封装签名和验证的过程,因为签名和验证都需要公钥-私钥对,所以它包含了这对钥匙的产生逻辑。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
|
package com.charles.signaturestudy; import java.io.IOException; import java.security.InvalidKeyException; import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.KeyStore; import java.security.NoSuchAlgorithmException; import java.security.Signature; import java.security.SignatureException; import java.security.SignedObject; /** * * Description: 这个工具类提供了一组方法来操作Signature类,它主要可以对签名进行一些操作 * * @author charles.wang * @created Oct 28, 2013 11:11:52 AM * */ public class SignatureUtil { private static SignatureUtil instance = null ; //公钥私钥对 private KeyPair keyPair = null ; //数字签名类 private Signature signature = null ; /** * 私有构造器,用指定的算法来初始化Signature类 * @param algorithm */ private SignatureUtil(String algorithm) { try { // 实例化KeyPairGenerator对象,并且指定算法为DSA KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(algorithm); // 初始化 KeyPairGenerator对象 keyPairGenerator.initialize( 1024 ); // 生成Keypair对象 keyPair = keyPairGenerator.generateKeyPair(); // 实例化Signature对象,这个对象提供一组动作方法类操作签名 signature = Signature.getInstance(keyPairGenerator.getAlgorithm()); } catch (NoSuchAlgorithmException ex) { keyPair = null ; signature = null ; } } /** * 单例的工厂方法,用于创建SignatureUtil的实例 * * @return */ public static SignatureUtil getInstance(String algorithm) { if (instance == null ) instance = new SignatureUtil(algorithm); return instance; } /** * 用私钥对指定的原始数据进行签名 * @param data 被签名的数据 * @return */ public byte [] signWithPrivateKey( byte [] data) { try { // 私钥完成签名,所以这里用私钥初始化用于签名的Signature signature.initSign(keyPair.getPrivate()); // 更新要签名的原始数据 signature.update(data); // 返回签名的内容 return signature.sign(); } catch (InvalidKeyException ie) { ie.printStackTrace(); return null ; } catch (SignatureException se) { se.printStackTrace(); return null ; } } /** * 用公钥对指定的原始数据和签名进行验证 * @param data * @param sign * @return */ public boolean verifySignedObjectWithPublicKey( byte [] data, byte [] sign) { try { // 公钥完成验证,所以这里用公钥初始化用于验证的Signature signature.initVerify(keyPair.getPublic()); // 更新要验证的原始数据 signature.update(data); // 验证签名,获得验证结果 return signature.verify(sign); } catch (InvalidKeyException ie) { ie.printStackTrace(); return false ; } catch (SignatureException se) { se.printStackTrace(); return false ; } } } |
然后我们提供了一个演示类,它的过程如下:
先给出原始数据,再用我们的API对其进行数字签名(sign),并且打印出数字签名的内容,然后我们用我们的API来验证(verify)数字签名的有效性。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
|
package com.charles.signaturestudy; /** * * Description: 这个类用来演示数字签名的使用方法 * * @author charles.wang * @created Oct 28, 2013 10:37:32 AM * */ public class SignatureDemo { public static void main(String [] args) throws Exception{ //SignatureUtil是我们开发的一组动作类,它对于Signature类进行了进一步的封装 //它提供了我们可以对数字签名完成的一组动作 SignatureUtil sigUtil = SignatureUtil.getInstance( "DSA" ); //原始数据对象 String content= "被测试的原始数据对象" ; //打印出原始数据 System.out.println( "原始数据为:" +content); //将原始数据对象转为字节数组 byte [] rawData = content.getBytes(); System.out.println( "n开始对原始数据签名..." ); //进行签名,返回签名的内容 byte [] sign = sigUtil.signWithPrivateKey(rawData); System.out.println( "签名内容(16进制)为:" +byte2hex(sign)); System.out.println( "n开始对签名内容验证..." ); //进行验证,对验证结果进行分析 boolean status = sigUtil.verifySignedObjectWithPublicKey(rawData, sign); if (status== true ){ System.out.println( "验证结果,此签名是有效的" ); } else { System.out.println( "验证结果,此签名是无效的" ); } } /** * 将二进制转为字符串的形式 * * @param b * @return */ protected static String byte2hex( byte [] b) // 二行制转字符串 { // 最终要转化的16进制字符串 StringBuilder hexString = new StringBuilder(); // 处理每个转化的当前字符串 String tmpStr = "" ; for ( int n = 0 ; n < b.length; n++) { // 将二进制转为16进制 tmpStr = (Integer.toHexString(b[n] & 0XFF )); // 如果当前转成的字符串只有一位长度的话,则前面补0,然后加上当前转换值 if (tmpStr.length() == 1 ) { hexString.append( "0" ).append(tmpStr); } // 否则,,则直接将当前转换值tmpStr附加在hexString后面 else hexString.append(tmpStr); // 如果没有到byte[]的尾部,则中间用冒号分开,最后一个后面不用加冒号 if (n < b.length - 1 ) hexString.append( ":" ); } return hexString.toString().toUpperCase(); } }
|