Java加密技术(一)
文章分类:Java编程
加密解密,曾经是我一个毕业设计的重要组件。在工作了多年以后回想当时那个加密、解密算法,实在是太单纯了。
言归正传,这里我们主要描述Java已经实现的一些加密解密算法,最后介绍数字证书。
如基本的单向加密算法:
• BASE64 严格地说,属于编码格式,而非加密算法
• MD5(Message Digest algorithm 5,信息摘要算法)
• SHA(Secure Hash Algorithm,安全散列算法)
• HMAC(Hash Message Authentication Code,散列消息鉴别码)
复杂的对称加密(DES、PBE)、非对称加密算法:
• DES(Data Encryption Standard,数据加密算法)
• PBE(Password-based encryption,基于密码验证)
• RSA(算法的名字以发明者的名字命名:Ron Rivest, AdiShamir 和Leonard Adleman)
• DH(Diffie-Hellman算法,密钥一致协议)
• DSA(Digital Signature Algorithm,数字签名)
• ECC(Elliptic Curves Cryptography,椭圆曲线密码编码学)
本篇内容简要介绍BASE64、MD5、SHA、HMAC几种方法。
MD5、SHA、HMAC这三种加密算法,可谓是非可逆加密,就是不可解密的加密方法。我们通常只把他们作为加密的基础。单纯的以上三种的加密并不可靠。
BASE64
按照RFC2045的定义,Base64被定义为:Base64内容传送编码被设计用来把任意序列的8位字节描述为一种不易被人直接识别的形式。(The Base64 Content-Transfer-Encoding is designed to represent arbitrary sequences of octets in a form that need not be humanly readable.)
常见于邮件、http加密,截取http信息,你就会发现登录操作的用户名、密码字段通过BASE64加密的。
通过java代码实现如下:
Java代码
1. /**
2. * BASE64解密
3. *
4. * @param key
5. * @return
6. * @throws Exception
7. */
8. public static byte[] decryptBASE64(String key) throws Exception {
9. return (new BASE64Decoder()).decodeBuffer(key);
10. }
11.
12. /**
13. * BASE64加密
14. *
15. * @param key
16. * @return
17. * @throws Exception
18. */
19. public static String encryptBASE64(byte[] key) throws Exception {
20. return (new BASE64Encoder()).encodeBuffer(key);
21. }
主要就是BASE64Encoder、BASE64Decoder两个类,我们只需要知道使用对应的方法即可。另,BASE加密后产生的字节位数是8的倍数,如果不够位数以=符号填充。
MD5
MD5 -- message-digest algorithm 5 (信息-摘要算法)缩写,广泛用于加密和解密技术,常用于文件校验。校验?不管文件多大,经过MD5后都能生成唯一的MD5值。好比现在的ISO校验,都是MD5校验。怎么用?当然是把ISO经过MD5后产生MD5的值。一般下载linux-ISO的朋友都见过下载链接旁边放着MD5的串。就是用来验证文件是否一致的。
通过java代码实现如下:
Java代码
1. /**
2. * MD5加密
3. *
4. * @param data
5. * @return
6. * @throws Exception
7. */
8. public static byte[] encryptMD5(byte[] data) throws Exception {
9.
10. MessageDigest md5 = MessageDigest.getInstance(KEY_MD5);
11. md5.update(data);
12.
13. return md5.digest();
14.
15. }
通常我们不直接使用上述MD5加密。通常将MD5产生的字节数组交给BASE64再加密一把,得到相应的字符串。
SHA
SHA(Secure Hash Algorithm,安全散列算法),数字签名等密码学应用中重要的工具,被广泛地应用于电子商务等信息安全领域。虽然,SHA与MD5通过碰撞法都被破解了, 但是SHA仍然是公认的安全加密算法,较之MD5更为安全。
通过java代码实现如下:
Java代码
1. /**
2. * SHA加密
3. *
4. * @param data
5. * @return
6. * @throws Exception
7. */
8. public static byte[] encryptSHA(byte[] data) throws Exception {
9.
10. MessageDigest sha = MessageDigest.getInstance(KEY_SHA);
11. sha.update(data);
12.
13. return sha.digest();
14.
15. }
16. }
HMAC
HMAC(Hash Message Authentication Code,散列消息鉴别码,基于密钥的Hash算法的认证协议。消息鉴别码实现鉴别的原理是,用公开函数和密钥产生一个固定长度的值作为认证标识,用这个标识鉴别消息的完整性。使用一个密钥生成一个固定大小的小数据块,即MAC,并将其加入到消息中,然后传输。接收方利用与发送方共享的密钥进行鉴别认证等。
通过java代码实现如下:
Java代码
1. /**
2. * 初始化HMAC密钥
3. *
4. * @return
5. * @throws Exception
6. */
7. public static String initMacKey() throws Exception {
8. KeyGenerator keyGenerator = KeyGenerator.getInstance(KEY_MAC);
9.
10. SecretKey secretKey = keyGenerator.generateKey();
11. return encryptBASE64(secretKey.getEncoded());
12. }
13.
14. /**
15. * HMAC加密
16. *
17. * @param data
18. * @param key
19. * @return
20. * @throws Exception
21. */
22. public static byte[] encryptHMAC(byte[] data, String key) throws Exception {
23.
24. SecretKey secretKey = new SecretKeySpec(decryptBASE64(key), KEY_MAC);
25. Mac mac = Mac.getInstance(secretKey.getAlgorithm());
26. mac.init(secretKey);
27.
28. return mac.doFinal(data);
29.
30. }
给出一个完整类,如下:
Java代码
1. import java.security.MessageDigest;
2.
3. import javax.crypto.KeyGenerator;
4. import javax.crypto.Mac;
5. import javax.crypto.SecretKey;
6.
7. import sun.misc.BASE64Decoder;
8. import sun.misc.BASE64Encoder;
9.
10. /**
11. * 基础加密组件
12. *
13. * @author 梁栋
14. * @version 1.0
15. * @since 1.0
16. */
17. public abstract class Coder {
18. public static final String KEY_SHA = "SHA";
19. public static final String KEY_MD5 = "MD5";
20.
21. /**
22. * MAC算法可选以下多种算法
23. *
24. *
25. * HmacMD5
26. * HmacSHA1
27. * HmacSHA256
28. * HmacSHA384
29. * HmacSHA512
30. *
31. */
32. public static final String KEY_MAC = "HmacMD5";
33.
34. /**
35. * BASE64解密
36. *
37. * @param key
38. * @return
39. * @throws Exception
40. */
41. public static byte[] decryptBASE64(String key) throws Exception {
42. return (new BASE64Decoder()).decodeBuffer(key);
43. }
44.
45. /**
46. * BASE64加密
47. *
48. * @param key
49. * @return
50. * @throws Exception
51. */
52. public static String encryptBASE64(byte[] key) throws Exception {
53. return (new BASE64Encoder()).encodeBuffer(key);
54. }
55.
56. /**
57. * MD5加密
58. *
59. * @param data
60. * @return
61. * @throws Exception
62. */
63. public static byte[] encryptMD5(byte[] data) throws Exception {
64.
65. MessageDigest md5 = MessageDigest.getInstance(KEY_MD5);
66. md5.update(data);
67.
68. return md5.digest();
69.
70. }
71.
72. /**
73. * SHA加密
74. *
75. * @param data
76. * @return
77. * @throws Exception
78. */
79. public static byte[] encryptSHA(byte[] data) throws Exception {
80.
81. MessageDigest sha = MessageDigest.getInstance(KEY_SHA);
82. sha.update(data);
83.
84. return sha.digest();
85.
86. }
87.
88. /**
89. * 初始化HMAC密钥
90. *
91. * @return
92. * @throws Exception
93. */
94. public static String initMacKey() throws Exception {
95. KeyGenerator keyGenerator = KeyGenerator.getInstance(KEY_MAC);
96.
97. SecretKey secretKey = keyGenerator.generateKey();
98. return encryptBASE64(secretKey.getEncoded());
99. }
100.
101. /**
102. * HMAC加密
103. *
104. * @param data
105. * @param key
106. * @return
107. * @throws Exception
108. */
109. public static byte[] encryptHMAC(byte[] data, String key) throws Exception {
110.
111. SecretKey secretKey = new SecretKeySpec(decryptBASE64(key), KEY_MAC);
112. Mac mac = Mac.getInstance(secretKey.getAlgorithm());
113. mac.init(secretKey);
114.
115. return mac.doFinal(data);
116.
117. }
118. }
再给出一个测试类:
Java代码
1. import static org.junit.Assert.*;
2.
3. import org.junit.Test;
4.
5. /**
6. *
7. * @author 梁栋
8. * @version 1.0
9. * @since 1.0
10. */
11. public class CoderTest {
12.
13. @Test
14. public void test() throws Exception {
15. String inputStr = "简单加密";
16. System.err.println("原文:\n" + inputStr);
17.
18. byte[] inputData = inputStr.getBytes();
19. String code = Coder.encryptBASE64(inputData);
20.
21. System.err.println("BASE64加密后:\n" + code);
22.
23. byte[] output = Coder.decryptBASE64(code);
24.
25. String outputStr = new String(output);
26.
27. System.err.println("BASE64解密后:\n" + outputStr);
28.
29. // 验证BASE64加密解密一致性
30. assertEquals(inputStr, outputStr);
31.
32. // 验证MD5对于同一内容加密是否一致
33. assertArrayEquals(Coder.encryptMD5(inputData), Coder
34. .encryptMD5(inputData));
35.
36. // 验证SHA对于同一内容加密是否一致
37. assertArrayEquals(Coder.encryptSHA(inputData), Coder
38. .encryptSHA(inputData));
39.
40. String key = Coder.initMacKey();
41. System.err.println("Mac密钥:\n" + key);
42.
43. // 验证HMAC对于同一内容,同一密钥加密是否一致
44. assertArrayEquals(Coder.encryptHMAC(inputData, key), Coder.encryptHMAC(
45. inputData, key));
46.
47. BigInteger md5 = new BigInteger(Coder.encryptMD5(inputData));
48. System.err.println("MD5:\n" + md5.toString(16));
49.
50. BigInteger sha = new BigInteger(Coder.encryptSHA(inputData));
51. System.err.println("SHA:\n" + sha.toString(32));
52.
53. BigInteger mac = new BigInteger(Coder.encryptHMAC(inputData, inputStr));
54. System.err.println("HMAC:\n" + mac.toString(16));
55. }
56. }
控制台输出:
Console代码
1. 原文:
2. 简单加密
3. BASE64加密后:
4. 566A5Y2V5Yqg5a+G
5.
6. BASE64解密后:
7. 简单加密
8. Mac密钥:
9. uGxdHC+6ylRDaik++leFtGwiMbuYUJ6mqHWyhSgF4trVkVBBSQvY/a22xU8XT1RUemdCWW155Bke
10. pBIpkd7QHg==
11.
12. MD5:
13. -550b4d90349ad4629462113e7934de56
14. SHA:
15. 91k9vo7p400cjkgfhjh0ia9qthsjagfn
16. HMAC:
17. 2287d192387e95694bdbba2fa941009a
注意
编译时,可能会看到如下提示:
引用
警告:sun.misc.BASE64Decoder 是 Sun 的专用 API,可能会在未来版本中删除
import sun.misc.BASE64Decoder;
^
警告:sun.misc.BASE64Encoder 是 Sun 的专用 API,可能会在未来版本中删除
import sun.misc.BASE64Encoder;
^
BASE64Encoder和BASE64Decoder是非官方JDK实现类。虽然可以在JDK里能找到并使用,但是在API里查不到。JRE 中 sun 和 com.sun 开头包的类都是未被文档化的,他们属于 java, javax 类库的基础,其中的实现大多数与底层平台有关,一般来说是不推荐使用的。
BASE64的加密解密是双向的,可以求反解。
MD5、SHA以及HMAC是单向加密,任何数据加密后只会产生唯一的一个加密串,通常用来校验数据在传输过程中是否被修改。其中HMAC算法有一个密钥,增强了数据传输过程中的安全性,强化了算法外的不可控因素。
单向加密的用途主要是为了校验数据在传输过程中是否被修改。
Java加密技术(二)
文章分类:Java编程
接下来我们介绍对称加密算法,最常用的莫过于DES数据加密算法。
DES
DES-Data Encryption Standard,即数据加密算法。是IBM公司于1975年研究成功并公开发表的。DES算法的入口参数有三个:Key、Data、Mode。其中Key为8个字节共64位,是DES算法的工作密钥;Data也为8个字节64位,是要被加密或被解密的数据;Mode为DES的工作方式,有两种:加密或解密。
DES算法把64位的明文输入块变为64位的密文输出块,它所使用的密钥也是64位。
通过java代码实现如下:Coder类见 Java加密技术(一)
Java代码
1. import java.security.Key;
2. import java.security.SecureRandom;
3.
4. import javax.crypto.Cipher;
5. import javax.crypto.KeyGenerator;
6. import javax.crypto.SecretKey;
7. import javax.crypto.SecretKeyFactory;
8. import javax.crypto.spec.DESKeySpec;
9.
10.
11. /**
12. * DES安全编码组件
13. *
14. *
15. * 支持 DES、DESede(TripleDES,就是3DES)、AES、Blowfish、RC2、RC4(ARCFOUR)
16. * DES key size must be equal to 56
17. * DESede(TripleDES) key size must be equal to 112 or 168
18. * AES key size must be equal to 128, 192 or 256,but 192 and 256 bits may not be available
19. * Blowfish key size must be multiple of 8, and can only range from 32 to 448 (inclusive)
20. * RC2 key size must be between 40 and 1024 bits
21. * RC4(ARCFOUR) key size must be between 40 and 1024 bits
22. * 具体内容 需要关注 JDK Document http://.../docs/technotes/guides/security/SunProviders.html
23. *
24. *
25. * @author 梁栋
26. * @version 1.0
27. * @since 1.0
28. */
29. public abstract class DESCoder extends Coder {
30. /**
31. * ALGORITHM 算法
32. * 可替换为以下任意一种算法,同时key值的size相应改变。
33. *
34. *
35. * DES key size must be equal to 56
36. * DESede(TripleDES) key size must be equal to 112 or 168
37. * AES key size must be equal to 128, 192 or 256,but 192 and 256 bits may not be available
38. * Blowfish key size must be multiple of 8, and can only range from 32 to 448 (inclusive)
39. * RC2 key size must be between 40 and 1024 bits
40. * RC4(ARCFOUR) key size must be between 40 and 1024 bits
41. *
42. *
43. * 在Key toKey(byte[] key)方法中使用下述代码
44. * SecretKey secretKey = new SecretKeySpec(key, ALGORITHM);
替换
45. *
46. * DESKeySpec dks = new DESKeySpec(key);
47. * SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(ALGORITHM);
48. * SecretKey secretKey = keyFactory.generateSecret(dks);
49. *
50. */
51. public static final String ALGORITHM = "DES";
52.
53. /**
54. * 转换密钥
55. *
56. * @param key
57. * @return
58. * @throws Exception
59. */
60. private static Key toKey(byte[] key) throws Exception {
61. DESKeySpec dks = new DESKeySpec(key);
62. SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(ALGORITHM);
63. SecretKey secretKey = keyFactory.generateSecret(dks);
64.
65. // 当使用其他对称加密算法时,如AES、Blowfish等算法时,用下述代码替换上述三行代码
66. // SecretKey secretKey = new SecretKeySpec(key, ALGORITHM);
67.
68. return secretKey;
69. }
70.
71. /**
72. * 解密
73. *
74. * @param data
75. * @param key
76. * @return
77. * @throws Exception
78. */
79. public static byte[] decrypt(byte[] data, String key) throws Exception {
80. Key k = toKey(decryptBASE64(key));
81.
82. Cipher cipher = Cipher.getInstance(ALGORITHM);
83. cipher.init(Cipher.DECRYPT_MODE, k);
84.
85. return cipher.doFinal(data);
86. }
87.
88. /**
89. * 加密
90. *
91. * @param data
92. * @param key
93. * @return
94. * @throws Exception
95. */
96. public static byte[] encrypt(byte[] data, String key) throws Exception {
97. Key k = toKey(decryptBASE64(key));
98. Cipher cipher = Cipher.getInstance(ALGORITHM);
99. cipher.init(Cipher.ENCRYPT_MODE, k);
100.
101. return cipher.doFinal(data);
102. }
103.
104. /**
105. * 生成密钥
106. *
107. * @return
108. * @throws Exception
109. */
110. public static String initKey() throws Exception {
111. return initKey(null);
112. }
113.
114. /**
115. * 生成密钥
116. *
117. * @param seed
118. * @return
119. * @throws Exception
120. */
121. public static String initKey(String seed) throws Exception {
122. SecureRandom secureRandom = null;
123.
124. if (seed != null) {
125. secureRandom = new SecureRandom(decryptBASE64(seed));
126. } else {
127. secureRandom = new SecureRandom();
128. }
129.
130. KeyGenerator kg = KeyGenerator.getInstance(ALGORITHM);
131. kg.init(secureRandom);
132.
133. SecretKey secretKey = kg.generateKey();
134.
135. return encryptBASE64(secretKey.getEncoded());
136. }
137. }
延续上一个类的实现,我们通过MD5以及SHA对字符串加密生成密钥,这是比较常见的密钥生成方式。
再给出一个测试类:
Java代码
1. import static org.junit.Assert.*;
2.
3.
4. import org.junit.Test;
5.
6. /**
7. *
8. * @author 梁栋
9. * @version 1.0
10. * @since 1.0
11. */
12. public class DESCoderTest {
13.
14. @Test
15. public void test() throws Exception {
16. String inputStr = "DES";
17. String key = DESCoder.initKey();
18. System.err.println("原文:\t" + inputStr);
19.
20. System.err.println("密钥:\t" + key);
21.
22. byte[] inputData = inputStr.getBytes();
23. inputData = DESCoder.encrypt(inputData, key);
24.
25. System.err.println("加密后:\t" + DESCoder.encryptBASE64(inputData));
26.
27. byte[] outputData = DESCoder.decrypt(inputData, key);
28. String outputStr = new String(outputData);
29.
30. System.err.println("解密后:\t" + outputStr);
31.
32. assertEquals(inputStr, outputStr);
33. }
34. }
得到的输出内容如下:
Console代码
1. 原文: DES
2. 密钥: f3wEtRrV6q0=
3.
4. 加密后: C6qe9oNIzRY=
5.
6. 解密后: DES
由控制台得到的输出,我们能够比对加密、解密后结果一致。这是一种简单的加密解密方式,只有一个密钥。
其实DES有很多同胞兄弟,如DESede(TripleDES)、AES、Blowfish、RC2、RC4(ARCFOUR)。这里就不过多阐述了,大同小异,只要换掉ALGORITHM换成对应的值,同时做一个代码替换SecretKey secretKey = new SecretKeySpec(key, ALGORITHM);就可以了,此外就是密钥长度不同了。
Java代码
1. /**
2. * DES key size must be equal to 56
3. * DESede(TripleDES) key size must be equal to 112 or 168
4. * AES key size must be equal to 128, 192 or 256,but 192 and 256 bits may not be available
5. * Blowfish key size must be multiple of 8, and can only range from 32 to 448 (inclusive)
6. * RC2 key size must be between 40 and 1024 bits
7. * RC4(ARCFOUR) key size must be between 40 and 1024 bits
8. **/
Java加密技术(三)
文章分类:Java编程
除了DES,我们还知道有DESede(TripleDES,就是3DES)、AES、Blowfish、RC2、RC4(ARCFOUR)等多种对称加密方式,其实现方式大同小异,这里介绍对称加密的另一个算法——PBE
PBE
PBE——Password-based encryption(基于密码加密)。其特点在于口令由用户自己掌管,不借助任何物理媒体;采用随机数(这里我们叫做盐)杂凑多重加密等方法保证数据的安全性。是一种简便的加密方式。
通过java代码实现如下:Coder类见 Java加密技术(一)
Java代码
1. import java.security.Key;
2. import java.util.Random;
3.
4. import javax.crypto.Cipher;
5. import javax.crypto.SecretKey;
6. import javax.crypto.SecretKeyFactory;
7. import javax.crypto.spec.PBEKeySpec;
8. import javax.crypto.spec.PBEParameterSpec;
9.
10. /**
11. * PBE安全编码组件
12. *
13. * @author 梁栋
14. * @version 1.0
15. * @since 1.0
16. */
17. public abstract class PBECoder extends Coder {
18. /**
19. * 支持以下任意一种算法
20. *
21. *
22. * PBEWithMD5AndDES
23. * PBEWithMD5AndTripleDES
24. * PBEWithSHA1AndDESede
25. * PBEWithSHA1AndRC2_40
26. *
27. */
28. public static final String ALGORITHM = "PBEWITHMD5andDES";
29.
30. /**
31. * 盐初始化
32. *
33. * @return
34. * @throws Exception
35. */
36. public static byte[] initSalt() throws Exception {
37. byte[] salt = new byte[8];
38. Random random = new Random();
39. random.nextBytes(salt);
40. return salt;
41. }
42.
43. /**
44. * 转换密钥
45. *
46. * @param password
47. * @return
48. * @throws Exception
49. */
50. private static Key toKey(String password) throws Exception {
51. PBEKeySpec keySpec = new PBEKeySpec(password.toCharArray());
52. SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(ALGORITHM);
53. SecretKey secretKey = keyFactory.generateSecret(keySpec);
54.
55. return secretKey;
56. }
57.
58. /**
59. * 加密
60. *
61. * @param data
62. * 数据
63. * @param password
64. * 密码
65. * @param salt
66. * 盐
67. * @return
68. * @throws Exception
69. */
70. public static byte[] encrypt(byte[] data, String password, byte[] salt)
71. throws Exception {
72.
73. Key key = toKey(password);
74.
75. PBEParameterSpec paramSpec = new PBEParameterSpec(salt, 100);
76. Cipher cipher = Cipher.getInstance(ALGORITHM);
77. cipher.init(Cipher.ENCRYPT_MODE, key, paramSpec);
78.
79. return cipher.doFinal(data);
80.
81. }
82.
83. /**
84. * 解密
85. *
86. * @param data
87. * 数据
88. * @param password
89. * 密码
90. * @param salt
91. * 盐
92. * @return
93. * @throws Exception
94. */
95. public static byte[] decrypt(byte[] data, String password, byte[] salt)
96. throws Exception {
97.
98. Key key = toKey(password);
99.
100. PBEParameterSpec paramSpec = new PBEParameterSpec(salt, 100);
101. Cipher cipher = Cipher.getInstance(ALGORITHM);
102. cipher.init(Cipher.DECRYPT_MODE, key, paramSpec);
103.
104. return cipher.doFinal(data);
105.
106. }
107. }
再给出一个测试类:
Java代码
1. import static org.junit.Assert.*;
2.
3. import org.junit.Test;
4.
5. /**
6. *
7. * @author 梁栋
8. * @version 1.0
9. * @since 1.0
10. */
11. public class PBECoderTest {
12.
13. @Test
14. public void test() throws Exception {
15. String inputStr = "abc";
16. System.err.println("原文: " + inputStr);
17. byte[] input = inputStr.getBytes();
18.
19. String pwd = "efg";
20. System.err.println("密码: " + pwd);
21.
22. byte[] salt = PBECoder.initSalt();
23.
24. byte[] data = PBECoder.encrypt(input, pwd, salt);
25.
26. System.err.println("加密后: " + PBECoder.encryptBASE64(data));
27.
28. byte[] output = PBECoder.decrypt(data, pwd, salt);
29. String outputStr = new String(output);
30.
31. System.err.println("解密后: " + outputStr);
32. assertEquals(inputStr, outputStr);
33. }
34.
35. }
控制台输出:
Console代码
1. 原文: abc
2. 密码: efg
3. 加密后: iCZ0uRtaAhE=
4.
5. 解密后: abc
后续我们会介绍非对称加密算法,如RSA、DSA、DH、ECC等。
Java加密技术(四)
文章分类:Java编程
接下来我们介绍典型的非对称加密算法——RSA
RSA
这种算法1978年就出现了,它是第一个既能用于数据加密也能用于数字签名的算法。它易于理解和操作,也很流行。算法的名字以发明者的名字命名:Ron Rivest, AdiShamir 和Leonard Adleman。
这种加密算法的特点主要是密钥的变化,上文我们看到DES只有一个密钥。相当于只有一把钥匙,如果这把钥匙丢了,数据也就不安全了。RSA同时有两把钥匙,公钥与私钥。同时支持数字签名。数字签名的意义在于,对传输过来的数据进行校验。确保数据在传输工程中不被修改。
流程分析:
1. 甲方构建密钥对儿,将公钥公布给乙方,将私钥保留。
2. 甲方使用私钥加密数据,然后用私钥对加密后的数据签名,发送给乙方签名以及加密后的数据;乙方使用公钥、签名来验证待解密数据是否有效,如果有效使用公钥对数据解密。
3. 乙方使用公钥加密数据,向甲方发送经过加密后的数据;甲方获得加密数据,通过私钥解密。
按如上步骤给出序列图,如下:
1.
2.
3.
通过java代码实现如下:Coder类见 Java加密技术(一)
Java代码
1. import java.security.Key;
2. import java.security.KeyFactory;
3. import java.security.KeyPair;
4. import java.security.KeyPairGenerator;
5. import java.security.PrivateKey;
6. import java.security.PublicKey;
7. import java.security.Signature;
8. import java.security.interfaces.RSAPrivateKey;
9. import java.security.interfaces.RSAPublicKey;
10. import java.security.spec.PKCS8EncodedKeySpec;
11. import java.security.spec.X509EncodedKeySpec;
12.
13. import java.util.HashMap;
14. import java.util.Map;
15.
16. import javax.crypto.Cipher;
17.
18. /**
19. * RSA安全编码组件
20. *
21. * @author 梁栋
22. * @version 1.0
23. * @since 1.0
24. */
25. public abstract class RSACoder extends Coder {
26. public static final String KEY_ALGORITHM = "RSA";
27. public static final String SIGNATURE_ALGORITHM = "MD5withRSA";
28.
29. private static final String PUBLIC_KEY = "RSAPublicKey";
30. private static final String PRIVATE_KEY = "RSAPrivateKey";
31.
32. /**
33. * 用私钥对信息生成数字签名
34. *
35. * @param data
36. * 加密数据
37. * @param privateKey
38. * 私钥
39. *
40. * @return
41. * @throws Exception
42. */
43. public static String sign(byte[] data, String privateKey) throws Exception {
44. // 解密由base64编码的私钥
45. byte[] keyBytes = decryptBASE64(privateKey);
46.
47. // 构造PKCS8EncodedKeySpec对象
48. PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
49.
50. // KEY_ALGORITHM 指定的加密算法
51. KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
52.
53. // 取私钥匙对象
54. PrivateKey priKey = keyFactory.generatePrivate(pkcs8KeySpec);
55.
56. // 用私钥对信息生成数字签名
57. Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
58. signature.initSign(priKey);
59. signature.update(data);
60.
61. return encryptBASE64(signature.sign());
62. }
63.
64. /**
65. * 校验数字签名
66. *
67. * @param data
68. * 加密数据
69. * @param publicKey
70. * 公钥
71. * @param sign
72. * 数字签名
73. *
74. * @return 校验成功返回true 失败返回false
75. * @throws Exception
76. *
77. */
78. public static boolean verify(byte[] data, String publicKey, String sign)
79. throws Exception {
80.
81. // 解密由base64编码的公钥
82. byte[] keyBytes = decryptBASE64(publicKey);
83.
84. // 构造X509EncodedKeySpec对象
85. X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
86.
87. // KEY_ALGORITHM 指定的加密算法
88. KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
89.
90. // 取公钥匙对象
91. PublicKey pubKey = keyFactory.generatePublic(keySpec);
92.
93. Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
94. signature.initVerify(pubKey);
95. signature.update(data);
96.
97. // 验证签名是否正常
98. return signature.verify(decryptBASE64(sign));
99. }
100.
101. /**
102. * 解密
103. * 用私钥解密
104. *
105. * @param data
106. * @param key
107. * @return
108. * @throws Exception
109. */
110. public static byte[] decryptByPrivateKey(byte[] data, String key)
111. throws Exception {
112. // 对密钥解密
113. byte[] keyBytes = decryptBASE64(key);
114.
115. // 取得私钥
116. PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
117. KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
118. Key privateKey = keyFactory.generatePrivate(pkcs8KeySpec);
119.
120. // 对数据解密
121. Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
122. cipher.init(Cipher.DECRYPT_MODE, privateKey);
123.
124. return cipher.doFinal(data);
125. }
126.
127. /**
128. * 解密
129. * 用私钥解密
130. *
131. * @param data
132. * @param key
133. * @return
134. * @throws Exception
135. */
136. public static byte[] decryptByPublicKey(byte[] data, String key)
137. throws Exception {
138. // 对密钥解密
139. byte[] keyBytes = decryptBASE64(key);
140.
141. // 取得公钥
142. X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
143. KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
144. Key publicKey = keyFactory.generatePublic(x509KeySpec);
145.
146. // 对数据解密
147. Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
148. cipher.init(Cipher.DECRYPT_MODE, publicKey);
149.
150. return cipher.doFinal(data);
151. }
152.
153. /**
154. * 加密
155. * 用公钥加密
156. *
157. * @param data
158. * @param key
159. * @return
160. * @throws Exception
161. */
162. public static byte[] encryptByPublicKey(byte[] data, String key)
163. throws Exception {
164. // 对公钥解密
165. byte[] keyBytes = decryptBASE64(key);
166.
167. // 取得公钥
168. X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
169. KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
170. Key publicKey = keyFactory.generatePublic(x509KeySpec);
171.
172. // 对数据加密
173. Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
174. cipher.init(Cipher.ENCRYPT_MODE, publicKey);
175.
176. return cipher.doFinal(data);
177. }
178.
179. /**
180. * 加密
181. * 用私钥加密
182. *
183. * @param data
184. * @param key
185. * @return
186. * @throws Exception
187. */
188. public static byte[] encryptByPrivateKey(byte[] data, String key)
189. throws Exception {
190. // 对密钥解密
191. byte[] keyBytes = decryptBASE64(key);
192.
193. // 取得私钥
194. PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
195. KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
196. Key privateKey = keyFactory.generatePrivate(pkcs8KeySpec);
197.
198. // 对数据加密
199. Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
200. cipher.init(Cipher.ENCRYPT_MODE, privateKey);
201.
202. return cipher.doFinal(data);
203. }
204.
205. /**
206. * 取得私钥
207. *
208. * @param keyMap
209. * @return
210. * @throws Exception
211. */
212. public static String getPrivateKey(Map keyMap)
213. throws Exception {
214. Key key = (Key) keyMap.get(PRIVATE_KEY);
215.
216. return encryptBASE64(key.getEncoded());
217. }
218.
219. /**
220. * 取得公钥
221. *
222. * @param keyMap
223. * @return
224. * @throws Exception
225. */
226. public static String getPublicKey(Map keyMap)
227. throws Exception {
228. Key key = (Key) keyMap.get(PUBLIC_KEY);
229.
230. return encryptBASE64(key.getEncoded());
231. }
232.
233. /**
234. * 初始化密钥
235. *
236. * @return
237. * @throws Exception
238. */
239. public static Map initKey() throws Exception {
240. KeyPairGenerator keyPairGen = KeyPairGenerator
241. .getInstance(KEY_ALGORITHM);
242. keyPairGen.initialize(1024);
243.
244. KeyPair keyPair = keyPairGen.generateKeyPair();
245.
246. // 公钥
247. RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
248.
249. // 私钥
250. RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
251.
252. Map keyMap = new HashMap(2);
253.
254. keyMap.put(PUBLIC_KEY, publicKey);
255. keyMap.put(PRIVATE_KEY, privateKey);
256. return keyMap;
257. }
258. }
再给出一个测试类:
Java代码
1. import static org.junit.Assert.*;
2.
3. import org.junit.Before;
4. import org.junit.Test;
5.
6. import java.util.Map;
7.
8. /**
9. *
10. * @author 梁栋
11. * @version 1.0
12. * @since 1.0
13. */
14. public class RSACoderTest {
15. private String publicKey;
16. private String privateKey;
17.
18. @Before
19. public void setUp() throws Exception {
20. Map keyMap = RSACoder.initKey();
21.
22. publicKey = RSACoder.getPublicKey(keyMap);
23. privateKey = RSACoder.getPrivateKey(keyMap);
24. System.err.println("公钥: \n\r" + publicKey);
25. System.err.println("私钥: \n\r" + privateKey);
26. }
27.
28. @Test
29. public void test() throws Exception {
30. System.err.println("公钥加密——私钥解密");
31. String inputStr = "abc";
32. byte[] data = inputStr.getBytes();
33.
34. byte[] encodedData = RSACoder.encryptByPublicKey(data, publicKey);
35.
36. byte[] decodedData = RSACoder.decryptByPrivateKey(encodedData,
37. privateKey);
38.
39. String outputStr = new String(decodedData);
40. System.err.println("加密前: " + inputStr + "\n\r" + "解密后: " + outputStr);
41. assertEquals(inputStr, outputStr);
42.
43. }
44.
45. @Test
46. public void testSign() throws Exception {
47. System.err.println("私钥加密——公钥解密");
48. String inputStr = "sign";
49. byte[] data = inputStr.getBytes();
50.
51. byte[] encodedData = RSACoder.encryptByPrivateKey(data, privateKey);
52.
53. byte[] decodedData = RSACoder
54. .decryptByPublicKey(encodedData, publicKey);
55.
56. String outputStr = new String(decodedData);
57. System.err.println("加密前: " + inputStr + "\n\r" + "解密后: " + outputStr);
58. assertEquals(inputStr, outputStr);
59.
60. System.err.println("私钥签名——公钥验证签名");
61. // 产生签名
62. String sign = RSACoder.sign(encodedData, privateKey);
63. System.err.println("签名:\r" + sign);
64.
65. // 验证签名
66. boolean status = RSACoder.verify(encodedData, publicKey, sign);
67. System.err.println("状态:\r" + status);
68. assertTrue(status);
69.
70. }
71.
72. }
控制台输出:
Console代码
1. 公钥:
2.
3. MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCYU/+I0+z1aBl5X6DUUOHQ7FZpmBSDbKTtx89J
4. EcB64jFCkunELT8qiKly7fzEqD03g8ALlu5XvX+bBqHFy7YPJJP0ekE2X3wjUnh2NxlqpH3/B/xm
5. 1ZdSlCwDIkbijhBVDjA/bu5BObhZqQmDwIxlQInL9oVz+o6FbAZCyHBd7wIDAQAB
6.
7. 私钥:
8.
9. MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAJhT/4jT7PVoGXlfoNRQ4dDsVmmY
10. FINspO3Hz0kRwHriMUKS6cQtPyqIqXLt/MSoPTeDwAuW7le9f5sGocXLtg8kk/R6QTZffCNSeHY3
11. GWqkff8H/GbVl1KULAMiRuKOEFUOMD9u7kE5uFmpCYPAjGVAicv2hXP6joVsBkLIcF3vAgMBAAEC
12. gYBvZHWoZHmS2EZQqKqeuGr58eobG9hcZzWQoJ4nq/CarBAjw/VovUHE490uK3S9ht4FW7Yzg3LV
13. /MB06Huifh6qf/X9NQA7SeZRRC8gnCQk6JuDIEVJOud5jU+9tyumJakDKodQ3Jf2zQtNr+5ZdEPl
14. uwWgv9c4kmpjhAdyMuQmYQJBANn6pcgvyYaia52dnu+yBUsGkaFfwXkzFSExIbi0MXTkhEb/ER/D
15. rLytukkUu5S5ecz/KBa8U4xIslZDYQbLz5ECQQCy5dutt7RsxN4+dxCWn0/1FrkWl2G329Ucewm3
16. QU9CKu4D+7Kqdj+Ha3lXP8F0Etaaapi7+EfkRUpukn2ItZV/AkEAlk+I0iphxT1rCB0Q5CjWDY5S
17. Df2B5JmdEG5Y2o0nLXwG2w44OLct/k2uD4cEcuITY5Dvi/4BftMCZwm/dnhEgQJACIktJSnJwxLV
18. o9dchENPtlsCM9C/Sd2EWpqISSUlmfugZbJBwR5pQ5XeMUqKeXZYpP+HEBj1nS+tMH9u2/IGEwJA
19. fL8mZiZXan/oBKrblAbplNcKWGRVD/3y65042PAEeghahlJMiYquV5DzZajuuT0wbJ5xQuZB01+X
20. nfpFpBJ2dw==
21.
22. 公钥加密——私钥解密
23. 加密前: abc
24.
25. 解密后: abc
26. 公钥:
27.
28. MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDdOj40yEB48XqWxmPILmJAc7UecIN7F32etSHF
29. 9rwbuEh3+iTPOGSxhoSQpOED0vOb0ZIMkBXZSgsxLaBSin2RZ09YKWRjtpCA0kDkiD11gj4tzTiM
30. l9qq1kwSK7ZkGAgodEn3yIILVmQDuEImHOXFtulvJ71ka07u3LuwUNdB/wIDAQAB
31.
32. 私钥:
33.
34. MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAN06PjTIQHjxepbGY8guYkBztR5w
35. g3sXfZ61IcX2vBu4SHf6JM84ZLGGhJCk4QPS85vRkgyQFdlKCzEtoFKKfZFnT1gpZGO2kIDSQOSI
36. PXWCPi3NOIyX2qrWTBIrtmQYCCh0SffIggtWZAO4QiYc5cW26W8nvWRrTu7cu7BQ10H/AgMBAAEC
37. gYEAz2JWBizjI31bqhP4XiP9PuY5F3vqBW4T+L9cFbQiyumKJc58yzTWUAUGKIIn3enXLG7dNqGr
38. mbJro4JeFIJ3CiVDpXR9+FluIgI4SXm7ioGKF2NOMA9LR5Fu82W+pLfpTN2y2SaLYWEDZyp53BxY
39. j9gUxaxi1MQs+C1ZgDF2xmECQQDy70bQntbRfysP+ppCtd56YRnES1Tyekw0wryS2tr+ivQJl7JF
40. gp5rPAOXpgrq36xHDwUspQ0sJ0vj0O7ywxr1AkEA6SAaLhrJJrYucC0jxwAhUYyaPN+aOsWymaRh
41. 9jA/Wc0wp29SbGTh5CcMuGpXm1g0M+FKW3dGiHgS3rVUKim4owJAbnxgapUzAgiiHxxMeDaavnHW
42. 9C2GrtjsO7qtZOTgYI/1uT8itvZW8lJTF+9OW8/qXE76fXl7ai9dFnl5kzMk2QJBALfHz/vCsArt
43. mkRiwY6zApE4Z6tPl1V33ymSVovvUzHnOdD1SKQdD5t+UV/crb3QVi8ED0t2B0u0ZSPfDT/D7kMC
44. QDpwdj9k2F5aokLHBHUNJPFDAp7a5QMaT64gv/d48ITJ68Co+v5WzLMpzJBYXK6PAtqIhxbuPEc2
45. I2k1Afmrwyw=
46.
47. 私钥加密——公钥解密
48. 加密前: sign
49.
50. 解密后: sign
51. 私钥签名——公钥验证签名
52. 签名:
53. ud1RsIwmSC1pN22I4IXteg1VD2FbiehKUfNxgVSHzvQNIK+d20FCkHCqh9djP3h94iWnIUY0ifU+
54. mbJkhAl/i5krExOE0hknOnPMcEP+lZV1RbJI2zG2YooSp2XDleqrQk5e/QF2Mx0Zxt8Xsg7ucVpn
55. i3wwbYWs9wSzIf0UjlM=
56.
57. 状态:
58. true
简要总结一下,使用公钥加密、私钥解密,完成了乙方到甲方的一次数据传递,通过私钥加密、公钥解密,同时通过私钥签名、公钥验证签名,完成了一次甲方到乙方的数据传递与验证,两次数据传递完成一整套的数据交互!
类似数字签名,数字信封是这样描述的:
数字信封
数字信封用加密技术来保证只有特定的收信人才能阅读信的内容。
流程:
信息发送方采用对称密钥来加密信息,然后再用接收方的公钥来加密此对称密钥(这部分称为数字信封),再将它和信息一起发送给接收方;接收方先用相应的私钥打开数字信封,得到对称密钥,然后使用对称密钥再解开信息。
Java加密技术(五)
文章分类:Java编程
接下来我们分析DH加密算法,一种适基于密钥一致协议的加密算法。
DH
Diffie-Hellman算法(D-H算法),密钥一致协议。是由公开密钥密码体制的奠基人Diffie和Hellman所提出的一种思想。简单的说就是允许两名用户在公开媒体上交换信息以生成"一致"的、可以共享的密钥。换句话说,就是由甲方产出一对密钥(公钥、私钥),乙方依照甲方公钥产生乙方密钥对(公钥、私钥)。以此为基线,作为数据传输保密基础,同时双方使用同一种对称加密算法构建本地密钥(SecretKey)对数据加密。这样,在互通了本地密钥(SecretKey)算法后,甲乙双方公开自己的公钥,使用对方的公钥和刚才产生的私钥加密数据,同时可以使用对方的公钥和自己的私钥对数据解密。不单单是甲乙双方两方,可以扩展为多方共享数据通讯,这样就完成了网络交互数据的安全通讯!该算法源于中国的同余定理——中国馀数定理。
流程分析:
1.甲方构建密钥对儿,将公钥公布给乙方,将私钥保留;双方约定数据加密算法;乙方通过甲方公钥构建密钥对儿,将公钥公布给甲方,将私钥保留。
2.甲方使用私钥、乙方公钥、约定数据加密算法构建本地密钥,然后通过本地密钥加密数据,发送给乙方加密后的数据;乙方使用私钥、甲方公钥、约定数据加密算法构建本地密钥,然后通过本地密钥对数据解密。
3.乙方使用私钥、甲方公钥、约定数据加密算法构建本地密钥,然后通过本地密钥加密数据,发送给甲方加密后的数据;甲方使用私钥、乙方公钥、约定数据加密算法构建本地密钥,然后通过本地密钥对数据解密。
1.
2.
3.
通过java代码实现如下:Coder类见 Java加密技术(一)
Java代码
1. import java.security.Key;
2. import java.security.KeyFactory;
3. import java.security.KeyPair;
4. import java.security.KeyPairGenerator;
5. import java.security.PublicKey;
6. import java.security.spec.PKCS8EncodedKeySpec;
7. import java.security.spec.X509EncodedKeySpec;
8. import java.util.HashMap;
9. import java.util.Map;
10.
11. import javax.crypto.Cipher;
12. import javax.crypto.KeyAgreement;
13. import javax.crypto.SecretKey;
14. import javax.crypto.interfaces.DHPrivateKey;
15. import javax.crypto.interfaces.DHPublicKey;
16. import javax.crypto.spec.DHParameterSpec;
17.
18. /**
19. * DH安全编码组件
20. *
21. * @author 梁栋
22. * @version 1.0
23. * @since 1.0
24. */
25. public abstract class DHCoder extends Coder {
26. public static final String ALGORITHM = "DH";
27.
28. /**
29. * 默认密钥字节数
30. *
31. *
32. * DH
33. * Default Keysize 1024
34. * Keysize must be a multiple of 64, ranging from 512 to 1024 (inclusive).
35. *
36. */
37. private static final int KEY_SIZE = 1024;
38.
39. /**
40. * DH加密下需要一种对称加密算法对数据加密,这里我们使用DES,也可以使用其他对称加密算法。
41. */
42. public static final String SECRET_ALGORITHM = "DES";
43. private static final String PUBLIC_KEY = "DHPublicKey";
44. private static final String PRIVATE_KEY = "DHPrivateKey";
45.
46. /**
47. * 初始化甲方密钥
48. *
49. * @return
50. * @throws Exception
51. */
52. public static Map initKey() throws Exception {
53. KeyPairGenerator keyPairGenerator = KeyPairGenerator
54. .getInstance(ALGORITHM);
55. keyPairGenerator.initialize(KEY_SIZE);
56.
57. KeyPair keyPair = keyPairGenerator.generateKeyPair();
58.
59. // 甲方公钥
60. DHPublicKey publicKey = (DHPublicKey) keyPair.getPublic();
61.
62. // 甲方私钥
63. DHPrivateKey privateKey = (DHPrivateKey) keyPair.getPrivate();
64.
65. Map keyMap = new HashMap(2);
66.
67. keyMap.put(PUBLIC_KEY, publicKey);
68. keyMap.put(PRIVATE_KEY, privateKey);
69. return keyMap;
70. }
71.
72. /**
73. * 初始化乙方密钥
74. *
75. * @param key
76. * 甲方公钥
77. * @return
78. * @throws Exception
79. */
80. public static Map initKey(String key) throws Exception {
81. // 解析甲方公钥
82. byte[] keyBytes = decryptBASE64(key);
83. X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
84. KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);
85. PublicKey pubKey = keyFactory.generatePublic(x509KeySpec);
86.
87. // 由甲方公钥构建乙方密钥
88. DHParameterSpec dhParamSpec = ((DHPublicKey) pubKey).getParams();
89.
90. KeyPairGenerator keyPairGenerator = KeyPairGenerator
91. .getInstance(keyFactory.getAlgorithm());
92. keyPairGenerator.initialize(dhParamSpec);
93.
94. KeyPair keyPair = keyPairGenerator.generateKeyPair();
95.
96. // 乙方公钥
97. DHPublicKey publicKey = (DHPublicKey) keyPair.getPublic();
98.
99. // 乙方私钥
100. DHPrivateKey privateKey = (DHPrivateKey) keyPair.getPrivate();
101.
102. Map keyMap = new HashMap(2);
103.
104. keyMap.put(PUBLIC_KEY, publicKey);
105. keyMap.put(PRIVATE_KEY, privateKey);
106.
107. return keyMap;
108. }
109.
110. /**
111. * 加密
112. *
113. * @param data
114. * 待加密数据
115. * @param publicKey
116. * 甲方公钥
117. * @param privateKey
118. * 乙方私钥
119. * @return
120. * @throws Exception
121. */
122. public static byte[] encrypt(byte[] data, String publicKey,
123. String privateKey) throws Exception {
124.
125. // 生成本地密钥
126. SecretKey secretKey = getSecretKey(publicKey, privateKey);
127.
128. // 数据加密
129. Cipher cipher = Cipher.getInstance(secretKey.getAlgorithm());
130. cipher.init(Cipher.ENCRYPT_MODE, secretKey);
131.
132. return cipher.doFinal(data);
133. }
134.
135. /**
136. * 解密
137. *
138. * @param data
139. * 待解密数据
140. * @param publicKey
141. * 乙方公钥
142. * @param privateKey
143. * 乙方私钥
144. * @return
145. * @throws Exception
146. */
147. public static byte[] decrypt(byte[] data, String publicKey,
148. String privateKey) throws Exception {
149.
150. // 生成本地密钥
151. SecretKey secretKey = getSecretKey(publicKey, privateKey);
152. // 数据解密
153. Cipher cipher = Cipher.getInstance(secretKey.getAlgorithm());
154. cipher.init(Cipher.DECRYPT_MODE, secretKey);
155.
156. return cipher.doFinal(data);
157. }
158.
159. /**
160. * 构建密钥
161. *
162. * @param publicKey
163. * 公钥
164. * @param privateKey
165. * 私钥
166. * @return
167. * @throws Exception
168. */
169. private static SecretKey getSecretKey(String publicKey, String privateKey)
170. throws Exception {
171. // 初始化公钥
172. byte[] pubKeyBytes = decryptBASE64(publicKey);
173.
174. KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);
175. X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(pubKeyBytes);
176. PublicKey pubKey = keyFactory.generatePublic(x509KeySpec);
177.
178. // 初始化私钥
179. byte[] priKeyBytes = decryptBASE64(privateKey);
180.
181. PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(priKeyBytes);
182. Key priKey = keyFactory.generatePrivate(pkcs8KeySpec);
183.
184. KeyAgreement keyAgree = KeyAgreement.getInstance(keyFactory
185. .getAlgorithm());
186. keyAgree.init(priKey);
187. keyAgree.doPhase(pubKey, true);
188.
189. // 生成本地密钥
190. SecretKey secretKey = keyAgree.generateSecret(SECRET_ALGORITHM);
191.
192. return secretKey;
193. }
194.
195. /**
196. * 取得私钥
197. *
198. * @param keyMap
199. * @return
200. * @throws Exception
201. */
202. public static String getPrivateKey(Map keyMap)
203. throws Exception {
204. Key key = (Key) keyMap.get(PRIVATE_KEY);
205.
206. return encryptBASE64(key.getEncoded());
207. }
208.
209. /**
210. * 取得公钥
211. *
212. * @param keyMap
213. * @return
214. * @throws Exception
215. */
216. public static String getPublicKey(Map keyMap)
217. throws Exception {
218. Key key = (Key) keyMap.get(PUBLIC_KEY);
219.
220. return encryptBASE64(key.getEncoded());
221. }
222. }
再给出一个测试类:
Java代码
1. import static org.junit.Assert.*;
2.
3. import java.util.Map;
4.
5. import org.junit.Test;
6.
7. /**
8. *
9. * @author 梁栋
10. * @version 1.0
11. * @since 1.0
12. */
13. public class DHCoderTest {
14.
15. @Test
16. public void test() throws Exception {
17. // 生成甲方密钥对儿
18. Map aKeyMap = DHCoder.initKey();
19. String aPublicKey = DHCoder.getPublicKey(aKeyMap);
20. String aPrivateKey = DHCoder.getPrivateKey(aKeyMap);
21.
22. System.err.println("甲方公钥:\r" + aPublicKey);
23. System.err.println("甲方私钥:\r" + aPrivateKey);
24.
25. // 由甲方公钥产生本地密钥对儿
26. Map bKeyMap = DHCoder.initKey(aPublicKey);
27. String bPublicKey = DHCoder.getPublicKey(bKeyMap);
28. String bPrivateKey = DHCoder.getPrivateKey(bKeyMap);
29.
30. System.err.println("乙方公钥:\r" + bPublicKey);
31. System.err.println("乙方私钥:\r" + bPrivateKey);
32.
33. String aInput = "abc ";
34. System.err.println("原文: " + aInput);
35.
36. // 由甲方公钥,乙方私钥构建密文
37. byte[] aCode = DHCoder.encrypt(aInput.getBytes(), aPublicKey,
38. bPrivateKey);
39.
40. // 由乙方公钥,甲方私钥解密
41. byte[] aDecode = DHCoder.decrypt(aCode, bPublicKey, aPrivateKey);
42. String aOutput = (new String(aDecode));
43.
44. System.err.println("解密: " + aOutput);
45.
46. assertEquals(aInput, aOutput);
47.
48. System.err.println(" ===============反过来加密解密================== ");
49. String bInput = "def ";
50. System.err.println("原文: " + bInput);
51.
52. // 由乙方公钥,甲方私钥构建密文
53. byte[] bCode = DHCoder.encrypt(bInput.getBytes(), bPublicKey,
54. aPrivateKey);
55.
56. // 由甲方公钥,乙方私钥解密
57. byte[] bDecode = DHCoder.decrypt(bCode, aPublicKey, bPrivateKey);
58. String bOutput = (new String(bDecode));
59.
60. System.err.println("解密: " + bOutput);
61.
62. assertEquals(bInput, bOutput);
63. }
64.
65. }
控制台输出:
Console代码
1. 甲方公钥:
2. MIHfMIGXBgkqhkiG9w0BAwEwgYkCQQD8poLOjhLKuibvzPcRDlJtsHiwXt7LzR60ogjzrhYXrgHz
3. W5Gkfm32NBPF4S7QiZvNEyrNUNmRUb3EPuc3WS4XAkBnhHGyepz0TukaScUUfbGpqvJE8FpDTWSG
4. kx0tFCcbnjUDC3H9c9oXkGmzLik1Yw4cIGI1TQ2iCmxBblC+eUykAgIBgANDAAJAdAWBVmIzqcko
5. Ej6qFjLDL2+Y3FPq1iRbnOyOpDj71yKaK1K+FhTv04B0zy4DKcvAASV7/Gv0W+bgqdmffRkqrQ==
6.
7. 甲方私钥:
8. MIHRAgEAMIGXBgkqhkiG9w0BAwEwgYkCQQD8poLOjhLKuibvzPcRDlJtsHiwXt7LzR60ogjzrhYX
9. rgHzW5Gkfm32NBPF4S7QiZvNEyrNUNmRUb3EPuc3WS4XAkBnhHGyepz0TukaScUUfbGpqvJE8FpD
10. TWSGkx0tFCcbnjUDC3H9c9oXkGmzLik1Yw4cIGI1TQ2iCmxBblC+eUykAgIBgAQyAjACJRfy1LyR
11. eHyD+4Hfb+xR0uoIGR1oL9i9Nk6g2AAuaDPgEVWHn+QXID13yL/uDos=
12.
13. 乙方公钥:
14. MIHfMIGXBgkqhkiG9w0BAwEwgYkCQQD8poLOjhLKuibvzPcRDlJtsHiwXt7LzR60ogjzrhYXrgHz
15. W5Gkfm32NBPF4S7QiZvNEyrNUNmRUb3EPuc3WS4XAkBnhHGyepz0TukaScUUfbGpqvJE8FpDTWSG
16. kx0tFCcbnjUDC3H9c9oXkGmzLik1Yw4cIGI1TQ2iCmxBblC+eUykAgIBgANDAAJAVEYSfBA+I9nr
17. dWw3OBv475C+eBrWBBYqt0m6/eu4ptuDQHwV4MmUtKAC2wc2nNrdb1wmBhY1X8RnWkJ1XmdDbQ==
18.
19. 乙方私钥:
20. MIHSAgEAMIGXBgkqhkiG9w0BAwEwgYkCQQD8poLOjhLKuibvzPcRDlJtsHiwXt7LzR60ogjzrhYX
21. rgHzW5Gkfm32NBPF4S7QiZvNEyrNUNmRUb3EPuc3WS4XAkBnhHGyepz0TukaScUUfbGpqvJE8FpD
22. TWSGkx0tFCcbnjUDC3H9c9oXkGmzLik1Yw4cIGI1TQ2iCmxBblC+eUykAgIBgAQzAjEAqaZiCdXp
23. 2iNpdBlHRaO9ir70wo2n32xNlIzIX19VLSPCDdeUWkgRv4CEj/8k+/yd
24.
25. 原文: abc
26. 解密: abc
27. ===============反过来加密解密==================
28. 原文: def
29. 解密: def
如我所言,甲乙双方在获得对方公钥后可以对发送给对方的数据加密,同时也能对接收到的数据解密,达到了数据安全通信的目的!
Java加密技术(六)
文章分类:Java编程
接下来我们介绍DSA数字签名,非对称加密的另一种实现。
DSA
DSA-Digital Signature Algorithm 是Schnorr和ElGamal签名算法的变种,被美国NIST作为DSS(DigitalSignature Standard)。简单的说,这是一种更高级的验证方式,用作数字签名。不单单只有公钥、私钥,还有数字签名。私钥加密生成数字签名,公钥验证数据及签名。如果数据和签名不匹配则认为验证失败!数字签名的作用就是校验数据在传输过程中不被修改。数字签名,是单向加密的升级!
1.
2.
通过java代码实现如下:Coder类见 Java加密技术(一)
Java代码
1. import java.security.Key;
2. import java.security.KeyFactory;
3. import java.security.KeyPair;
4. import java.security.KeyPairGenerator;
5. import java.security.PrivateKey;
6. import java.security.PublicKey;
7. import java.security.SecureRandom;
8. import java.security.Signature;
9. import java.security.interfaces.DSAPrivateKey;
10. import java.security.interfaces.DSAPublicKey;
11. import java.security.spec.PKCS8EncodedKeySpec;
12. import java.security.spec.X509EncodedKeySpec;
13. import java.util.HashMap;
14. import java.util.Map;
15.
16. /**
17. * DSA安全编码组件
18. *
19. * @author 梁栋
20. * @version 1.0
21. * @since 1.0
22. */
23. public abstract class DSACoder extends Coder {
24.
25. public static final String ALGORITHM = "DSA";
26.
27. /**
28. * 默认密钥字节数
29. *
30. *
31. * DSA
32. * Default Keysize 1024
33. * Keysize must be a multiple of 64, ranging from 512 to 1024 (inclusive).
34. *
35. */
36. private static final int KEY_SIZE = 1024;
37.
38. /**
39. * 默认种子
40. */
41. private static final String DEFAULT_SEED = "0f22507a10bbddd07d8a3082122966e3";
42.
43. private static final String PUBLIC_KEY = "DSAPublicKey";
44. private static final String PRIVATE_KEY = "DSAPrivateKey";
45.
46. /**
47. * 用私钥对信息生成数字签名
48. *
49. * @param data
50. * 加密数据
51. * @param privateKey
52. * 私钥
53. *
54. * @return
55. * @throws Exception
56. */
57. public static String sign(byte[] data, String privateKey) throws Exception {
58. // 解密由base64编码的私钥
59. byte[] keyBytes = decryptBASE64(privateKey);
60.
61. // 构造PKCS8EncodedKeySpec对象
62. PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
63.
64. // KEY_ALGORITHM 指定的加密算法
65. KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);
66.
67. // 取私钥匙对象
68. PrivateKey priKey = keyFactory.generatePrivate(pkcs8KeySpec);
69.
70. // 用私钥对信息生成数字签名
71. Signature signature = Signature.getInstance(keyFactory.getAlgorithm());
72. signature.initSign(priKey);
73. signature.update(data);
74.
75. return encryptBASE64(signature.sign());
76. }
77.
78. /**
79. * 校验数字签名
80. *
81. * @param data
82. * 加密数据
83. * @param publicKey
84. * 公钥
85. * @param sign
86. * 数字签名
87. *
88. * @return 校验成功返回true 失败返回false
89. * @throws Exception
90. *
91. */
92. public static boolean verify(byte[] data, String publicKey, String sign)
93. throws Exception {
94.
95. // 解密由base64编码的公钥
96. byte[] keyBytes = decryptBASE64(publicKey);
97.
98. // 构造X509EncodedKeySpec对象
99. X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
100.
101. // ALGORITHM 指定的加密算法
102. KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);
103.
104. // 取公钥匙对象
105. PublicKey pubKey = keyFactory.generatePublic(keySpec);
106.
107. Signature signature = Signature.getInstance(keyFactory.getAlgorithm());
108. signature.initVerify(pubKey);
109. signature.update(data);
110.
111. // 验证签名是否正常
112. return signature.verify(decryptBASE64(sign));
113. }
114.
115. /**
116. * 生成密钥
117. *
118. * @param seed
119. * 种子
120. * @return 密钥对象
121. * @throws Exception
122. */
123. public static Map initKey(String seed) throws Exception {
124. KeyPairGenerator keygen = KeyPairGenerator.getInstance(ALGORITHM);
125. // 初始化随机产生器
126. SecureRandom secureRandom = new SecureRandom();
127. secureRandom.setSeed(seed.getBytes());
128. keygen.initialize(KEY_SIZE, secureRandom);
129.
130. KeyPair keys = keygen.genKeyPair();
131.
132. DSAPublicKey publicKey = (DSAPublicKey) keys.getPublic();
133. DSAPrivateKey privateKey = (DSAPrivateKey) keys.getPrivate();
134.
135. Map map = new HashMap(2);
136. map.put(PUBLIC_KEY, publicKey);
137. map.put(PRIVATE_KEY, privateKey);
138.
139. return map;
140. }
141.
142. /**
143. * 默认生成密钥
144. *
145. * @return 密钥对象
146. * @throws Exception
147. */
148. public static Map initKey() throws Exception {
149. return initKey(DEFAULT_SEED);
150. }
151.
152. /**
153. * 取得私钥
154. *
155. * @param keyMap
156. * @return
157. * @throws Exception
158. */
159. public static String getPrivateKey(Map keyMap)
160. throws Exception {
161. Key key = (Key) keyMap.get(PRIVATE_KEY);
162.
163. return encryptBASE64(key.getEncoded());
164. }
165.
166. /**
167. * 取得公钥
168. *
169. * @param keyMap
170. * @return
171. * @throws Exception
172. */
173. public static String getPublicKey(Map keyMap)
174. throws Exception {
175. Key key = (Key) keyMap.get(PUBLIC_KEY);
176.
177. return encryptBASE64(key.getEncoded());
178. }
179. }
再给出一个测试类:
Java代码
1. import static org.junit.Assert.*;
2.
3. import java.util.Map;
4.
5. import org.junit.Test;
6.
7. /**
8. *
9. * @author 梁栋
10. * @version 1.0
11. * @since 1.0
12. */
13. public class DSACoderTest {
14.
15. @Test
16. public void test() throws Exception {
17. String inputStr = "abc";
18. byte[] data = inputStr.getBytes();
19.
20. // 构建密钥
21. Map keyMap = DSACoder.initKey();
22.
23. // 获得密钥
24. String publicKey = DSACoder.getPublicKey(keyMap);
25. String privateKey = DSACoder.getPrivateKey(keyMap);
26.
27. System.err.println("公钥:\r" + publicKey);
28. System.err.println("私钥:\r" + privateKey);
29.
30. // 产生签名
31. String sign = DSACoder.sign(data, privateKey);
32. System.err.println("签名:\r" + sign);
33.
34. // 验证签名
35. boolean status = DSACoder.verify(data, publicKey, sign);
36. System.err.println("状态:\r" + status);
37. assertTrue(status);
38.
39. }
40.
41. }
控制台输出:
Console代码
1. 公钥:
2. MIIBtzCCASwGByqGSM44BAEwggEfAoGBAP1/U4EddRIpUt9KnC7s5Of2EbdSPO9EAMMeP4C2USZp
3. RV1AIlH7WT2NWPq/xfW6MPbLm1Vs14E7gB00b/JmYLdrmVClpJ+f6AR7ECLCT7up1/63xhv4O1fn
4. xqimFQ8E+4P208UewwI1VBNaFpEy9nXzrith1yrv8iIDGZ3RSAHHAhUAl2BQjxUjC8yykrmCouuE
5. C/BYHPUCgYEA9+GghdabPd7LvKtcNrhXuXmUr7v6OuqC+VdMCz0HgmdRWVeOutRZT+ZxBxCBgLRJ
6. FnEj6EwoFhO3zwkyjMim4TwWeotUfI0o4KOuHiuzpnWRbqN/C/ohNWLx+2J6ASQ7zKTxvqhRkImo
7. g9/hWuWfBpKLZl6Ae1UlZAFMO/7PSSoDgYQAAoGAIu4RUlcQLp49PI0MrbssOY+3uySVnp0TULSv
8. 5T4VaHoKzsLHgGTrwOvsGA+V3yCNl2WDu3D84bSLF7liTWgOj+SMOEaPk4VyRTlLXZWGPsf1Mfd9
9. 21XAbMeVyKDSHHVGbMjBScajf3bXooYQMlyoHiOt/WrCo+mv7efstMM0PGo=
10.
11. 私钥:
12. MIIBTAIBADCCASwGByqGSM44BAEwggEfAoGBAP1/U4EddRIpUt9KnC7s5Of2EbdSPO9EAMMeP4C2
13. USZpRV1AIlH7WT2NWPq/xfW6MPbLm1Vs14E7gB00b/JmYLdrmVClpJ+f6AR7ECLCT7up1/63xhv4
14. O1fnxqimFQ8E+4P208UewwI1VBNaFpEy9nXzrith1yrv8iIDGZ3RSAHHAhUAl2BQjxUjC8yykrmC
15. ouuEC/BYHPUCgYEA9+GghdabPd7LvKtcNrhXuXmUr7v6OuqC+VdMCz0HgmdRWVeOutRZT+ZxBxCB
16. gLRJFnEj6EwoFhO3zwkyjMim4TwWeotUfI0o4KOuHiuzpnWRbqN/C/ohNWLx+2J6ASQ7zKTxvqhR
17. kImog9/hWuWfBpKLZl6Ae1UlZAFMO/7PSSoEFwIVAIegLUtmm2oQKQJTOiLugHTSjl/q
18.
19. 签名:
20. MC0CFQCMg0J/uZmF8GuRpr3TNq48w60nDwIUJCyYNah+HtbU6NcQfy8Ac6LeLQs=
21.
22. 状态:
23. true
注意状态为true,就验证成功!
Java加密技术(七)
文章分类:Java编程
ECC
ECC-Elliptic Curves Cryptography,椭圆曲线密码编码学,是目前已知的公钥体制中,对每比特所提供加密强度最高的一种体制。在软件注册保护方面起到很大的作用,一般的序列号通常由该算法产生。
当我开始整理《Java加密技术(二)》的时候,我就已经在开始研究ECC了,但是关于Java实现ECC算法的资料实在是太少了,无论是国内还是国外的资料,无论是官方还是非官方的解释,最终只有一种答案——ECC算法在jdk1.5后加入支持,目前仅仅只能完成密钥的生成与解析。 如果想要获得ECC算法实现,需要调用硬件完成加密/解密(ECC算法相当耗费资源,如果单纯使用CPU进行加密/解密,效率低下),涉及到Java Card领域,PKCS#11。 其实,PKCS#11配置很简单,但缺乏硬件设备,无法尝试!
尽管如此,我照旧提供相应的Java实现代码,以供大家参考。
通过java代码实现如下:Coder类见 Java加密技术(一)
Java代码
1. import java.math.BigInteger;
2. import java.security.Key;
3. import java.security.KeyFactory;
4. import java.security.interfaces.ECPrivateKey;
5. import java.security.interfaces.ECPublicKey;
6. import java.security.spec.ECFieldF2m;
7. import java.security.spec.ECParameterSpec;
8. import java.security.spec.ECPoint;
9. import java.security.spec.ECPrivateKeySpec;
10. import java.security.spec.ECPublicKeySpec;
11. import java.security.spec.EllipticCurve;
12. import java.security.spec.PKCS8EncodedKeySpec;
13. import java.security.spec.X509EncodedKeySpec;
14. import java.util.HashMap;
15. import java.util.Map;
16.
17. import javax.crypto.Cipher;
18. import javax.crypto.NullCipher;
19.
20. import sun.security.ec.ECKeyFactory;
21. import sun.security.ec.ECPrivateKeyImpl;
22. import sun.security.ec.ECPublicKeyImpl;
23.
24. /**
25. * ECC安全编码组件
26. *
27. * @author 梁栋
28. * @version 1.0
29. * @since 1.0
30. */
31. public abstract class ECCCoder extends Coder {
32.
33. public static final String ALGORITHM = "EC";
34. private static final String PUBLIC_KEY = "ECCPublicKey";
35. private static final String PRIVATE_KEY = "ECCPrivateKey";
36.
37. /**
38. * 解密
39. * 用私钥解密
40. *
41. * @param data
42. * @param key
43. * @return
44. * @throws Exception
45. */
46. public static byte[] decrypt(byte[] data, String key) throws Exception {
47. // 对密钥解密
48. byte[] keyBytes = decryptBASE64(key);
49.
50. // 取得私钥
51. PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
52. KeyFactory keyFactory = ECKeyFactory.INSTANCE;
53.
54. ECPrivateKey priKey = (ECPrivateKey) keyFactory
55. .generatePrivate(pkcs8KeySpec);
56.
57. ECPrivateKeySpec ecPrivateKeySpec = new ECPrivateKeySpec(priKey.getS(),
58. priKey.getParams());
59.
60. // 对数据解密
61. // TODO Chipher不支持EC算法 未能实现
62. Cipher cipher = new NullCipher();
63. // Cipher.getInstance(ALGORITHM, keyFactory.getProvider());
64. cipher.init(Cipher.DECRYPT_MODE, priKey, ecPrivateKeySpec.getParams());
65.
66. return cipher.doFinal(data);
67. }
68.
69. /**
70. * 加密
71. * 用公钥加密
72. *
73. * @param data
74. * @param privateKey
75. * @return
76. * @throws Exception
77. */
78. public static byte[] encrypt(byte[] data, String privateKey)
79. throws Exception {
80. // 对公钥解密
81. byte[] keyBytes = decryptBASE64(privateKey);
82.
83. // 取得公钥
84. X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
85. KeyFactory keyFactory = ECKeyFactory.INSTANCE;
86.
87. ECPublicKey pubKey = (ECPublicKey) keyFactory
88. .generatePublic(x509KeySpec);
89.
90. ECPublicKeySpec ecPublicKeySpec = new ECPublicKeySpec(pubKey.getW(),
91. pubKey.getParams());
92.
93. // 对数据加密
94. // TODO Chipher不支持EC算法 未能实现
95. Cipher cipher = new NullCipher();
96. // Cipher.getInstance(ALGORITHM, keyFactory.getProvider());
97. cipher.init(Cipher.ENCRYPT_MODE, pubKey, ecPublicKeySpec.getParams());
98.
99. return cipher.doFinal(data);
100. }
101.
102. /**
103. * 取得私钥
104. *
105. * @param keyMap
106. * @return
107. * @throws Exception
108. */
109. public static String getPrivateKey(Map keyMap)
110. throws Exception {
111. Key key = (Key) keyMap.get(PRIVATE_KEY);
112.
113. return encryptBASE64(key.getEncoded());
114. }
115.
116. /**
117. * 取得公钥
118. *
119. * @param keyMap
120. * @return
121. * @throws Exception
122. */
123. public static String getPublicKey(Map keyMap)
124. throws Exception {
125. Key key = (Key) keyMap.get(PUBLIC_KEY);
126.
127. return encryptBASE64(key.getEncoded());
128. }
129.
130. /**
131. * 初始化密钥
132. *
133. * @return
134. * @throws Exception
135. */
136. public static Map initKey() throws Exception {
137. BigInteger x1 = new BigInteger(
138. "2fe13c0537bbc11acaa07d793de4e6d5e5c94eee8", 16);
139. BigInteger x2 = new BigInteger(
140. "289070fb05d38ff58321f2e800536d538ccdaa3d9", 16);
141.
142. ECPoint g = new ECPoint(x1, x2);
143.
144. // the order of generator
145. BigInteger n = new BigInteger(
146. "5846006549323611672814741753598448348329118574063", 10);
147. // the cofactor
148. int h = 2;
149. int m = 163;
150. int[] ks = { 7, 6, 3 };
151. ECFieldF2m ecField = new ECFieldF2m(m, ks);
152. // y^2+xy=x^3+x^2+1
153. BigInteger a = new BigInteger("1", 2);
154. BigInteger b = new BigInteger("1", 2);
155.
156. EllipticCurve ellipticCurve = new EllipticCurve(ecField, a, b);
157.
158. ECParameterSpec ecParameterSpec = new ECParameterSpec(ellipticCurve, g,
159. n, h);
160. // 公钥
161. ECPublicKey publicKey = new ECPublicKeyImpl(g, ecParameterSpec);
162.
163. BigInteger s = new BigInteger(
164. "1234006549323611672814741753598448348329118574063", 10);
165. // 私钥
166. ECPrivateKey privateKey = new ECPrivateKeyImpl(s, ecParameterSpec);
167.
168. Map keyMap = new HashMap(2);
169.
170. keyMap.put(PUBLIC_KEY, publicKey);
171. keyMap.put(PRIVATE_KEY, privateKey);
172.
173. return keyMap;
174. }
175.
176. }
请注意上述代码中的TODO内容,再次提醒注意,Chipher不支持EC算法 ,以上代码仅供参考。Chipher、Signature、KeyPairGenerator、KeyAgreement、SecretKey均不支持EC算法。为了确保程序能够正常执行,我们使用了NullCipher类,验证程序。
照旧提供一个测试类:
Java代码
1. import static org.junit.Assert.*;
2.
3. import java.math.BigInteger;
4. import java.security.spec.ECFieldF2m;
5. import java.security.spec.ECParameterSpec;
6. import java.security.spec.ECPoint;
7. import java.security.spec.ECPrivateKeySpec;
8. import java.security.spec.ECPublicKeySpec;
9. import java.security.spec.EllipticCurve;
10. import java.util.Map;
11.
12. import org.junit.Test;
13.
14. /**
15. *
16. * @author 梁栋
17. * @version 1.0
18. * @since 1.0
19. */
20. public class ECCCoderTest {
21.
22. @Test
23. public void test() throws Exception {
24. String inputStr = "abc";
25. byte[] data = inputStr.getBytes();
26.
27. Map keyMap = ECCCoder.initKey();
28.
29. String publicKey = ECCCoder.getPublicKey(keyMap);
30. String privateKey = ECCCoder.getPrivateKey(keyMap);
31. System.err.println("公钥: \n" + publicKey);
32. System.err.println("私钥: \n" + privateKey);
33.
34. byte[] encodedData = ECCCoder.encrypt(data, publicKey);
35.
36. byte[] decodedData = ECCCoder.decrypt(encodedData, privateKey);
37.
38. String outputStr = new String(decodedData);
39. System.err.println("加密前: " + inputStr + "\n\r" + "解密后: " + outputStr);
40. assertEquals(inputStr, outputStr);
41. }
42. }
控制台输出:
Console代码
1. 公钥:
2. MEAwEAYHKoZIzj0CAQYFK4EEAAEDLAAEAv4TwFN7vBGsqgfXk95ObV5clO7oAokHD7BdOP9YMh8u
3. gAU21TjM2qPZ
4.
5. 私钥:
6. MDICAQAwEAYHKoZIzj0CAQYFK4EEAAEEGzAZAgEBBBTYJsR3BN7TFw7JHcAHFkwNmfil7w==
7.
8. 加密前: abc
9.
10. 解密后: abc
Java加密技术(八)
文章分类:Java编程
本篇的主要内容为Java证书体系的实现。
请大家在阅读本篇内容时先阅读 Java加密技术(四),预先了解RSA加密算法。
在构建Java代码实现前,我们需要完成证书的制作。
1.生成keyStroe文件
在命令行下执行以下命令:
Shell代码
1. keytool -genkey -validity 36000 -alias www.zlex.org -keyalg RSA -keystore d:\zlex.keystore
其中
-genkey表示生成密钥
-validity指定证书有效期,这里是36000天
-alias指定别名,这里是www.zlex.org
-keyalg指定算法,这里是RSA
-keystore指定存储位置,这里是d:\zlex.keystore
在这里我使用的密码为 123456
控制台输出:
Console代码
1. 输入keystore密码:
2. 再次输入新密码:
3. 您的名字与姓氏是什么?
4. [Unknown]: www.zlex.org
5. 您的组织单位名称是什么?
6. [Unknown]: zlex
7. 您的组织名称是什么?
8. [Unknown]: zlex
9. 您所在的城市或区域名称是什么?
10. [Unknown]: BJ
11. 您所在的州或省份名称是什么?
12. [Unknown]: BJ
13. 该单位的两字母国家代码是什么
14. [Unknown]: CN
15. CN=www.zlex.org, OU=zlex, O=zlex, L=BJ, ST=BJ, C=CN 正确吗?
16. [否]: Y
17.
18. 输入的主密码
19. (如果和 keystore 密码相同,按回车):
20. 再次输入新密码:
这时,在D盘下会生成一个zlex.keystore的文件。
2.生成自签名证书
光有keyStore文件是不够的,还需要证书文件,证书才是直接提供给外界使用的公钥凭证。
导出证书:
Shell代码
1. keytool -export -keystore d:\zlex.keystore -alias www.zlex.org -file d:\zlex.cer -rfc
其中
-export指定为导出操作
-keystore指定keystore文件
-alias指定导出keystore文件中的别名
-file指向导出路径
-rfc以文本格式输出,也就是以BASE64编码输出
这里的密码是 123456
控制台输出:
Console代码
1. 输入keystore密码:
2. 保存在文件中的认证
当然,使用方是需要导入证书的!
可以通过自签名证书完成CAS单点登录系统的构建!
Ok,准备工作完成,开始Java实现!
通过java代码实现如下:Coder类见 Java加密技术(一)
Java代码
1. import java.io.FileInputStream;
2. import java.security.KeyStore;
3. import java.security.PrivateKey;
4. import java.security.PublicKey;
5. import java.security.Signature;
6. import java.security.cert.Certificate;
7. import java.security.cert.CertificateFactory;
8. import java.security.cert.X509Certificate;
9. import java.util.Date;
10.
11. import javax.crypto.Cipher;
12.
13. /**
14. * 证书组件
15. *
16. * @author 梁栋
17. * @version 1.0
18. * @since 1.0
19. */
20. public abstract class CertificateCoder extends Coder {
21.
22.
23. /**
24. * Java密钥库(Java Key Store,JKS)KEY_STORE
25. */
26. public static final String KEY_STORE = "JKS";
27.
28. public static final String X509 = "X.509";
29.
30. /**
31. * 由KeyStore获得私钥
32. *
33. * @param keyStorePath
34. * @param alias
35. * @param password
36. * @return
37. * @throws Exception
38. */
39. private static PrivateKey getPrivateKey(String keyStorePath, String alias,
40. String password) throws Exception {
41. KeyStore ks = getKeyStore(keyStorePath, password);
42. PrivateKey key = (PrivateKey) ks.getKey(alias, password.toCharArray());
43. return key;
44. }
45.
46. /**
47. * 由Certificate获得公钥
48. *
49. * @param certificatePath
50. * @return
51. * @throws Exception
52. */
53. private static PublicKey getPublicKey(String certificatePath)
54. throws Exception {
55. Certificate certificate = getCertificate(certificatePath);
56. PublicKey key = certificate.getPublicKey();
57. return key;
58. }
59.
60. /**
61. * 获得Certificate
62. *
63. * @param certificatePath
64. * @return
65. * @throws Exception
66. */
67. private static Certificate getCertificate(String certificatePath)
68. throws Exception {
69. CertificateFactory certificateFactory = CertificateFactory
70. .getInstance(X509);
71. FileInputStream in = new FileInputStream(certificatePath);
72.
73. Certificate certificate = certificateFactory.generateCertificate(in);
74. in.close();
75.
76. return certificate;
77. }
78.
79. /**
80. * 获得Certificate
81. *
82. * @param keyStorePath
83. * @param alias
84. * @param password
85. * @return
86. * @throws Exception
87. */
88. private static Certificate getCertificate(String keyStorePath,
89. String alias, String password) throws Exception {
90. KeyStore ks = getKeyStore(keyStorePath, password);
91. Certificate certificate = ks.getCertificate(alias);
92.
93. return certificate;
94. }
95.
96. /**
97. * 获得KeyStore
98. *
99. * @param keyStorePath
100. * @param password
101. * @return
102. * @throws Exception
103. */
104. private static KeyStore getKeyStore(String keyStorePath, String password)
105. throws Exception {
106. FileInputStream is = new FileInputStream(keyStorePath);
107. KeyStore ks = KeyStore.getInstance(KEY_STORE);
108. ks.load(is, password.toCharArray());
109. is.close();
110. return ks;
111. }
112.
113. /**
114. * 私钥加密
115. *
116. * @param data
117. * @param keyStorePath
118. * @param alias
119. * @param password
120. * @return
121. * @throws Exception
122. */
123. public static byte[] encryptByPrivateKey(byte[] data, String keyStorePath,
124. String alias, String password) throws Exception {
125. // 取得私钥
126. PrivateKey privateKey = getPrivateKey(keyStorePath, alias, password);
127.
128. // 对数据加密
129. Cipher cipher = Cipher.getInstance(privateKey.getAlgorithm());
130. cipher.init(Cipher.ENCRYPT_MODE, privateKey);
131.
132. return cipher.doFinal(data);
133.
134. }
135.
136. /**
137. * 私钥解密
138. *
139. * @param data
140. * @param keyStorePath
141. * @param alias
142. * @param password
143. * @return
144. * @throws Exception
145. */
146. public static byte[] decryptByPrivateKey(byte[] data, String keyStorePath,
147. String alias, String password) throws Exception {
148. // 取得私钥
149. PrivateKey privateKey = getPrivateKey(keyStorePath, alias, password);
150.
151. // 对数据加密
152. Cipher cipher = Cipher.getInstance(privateKey.getAlgorithm());
153. cipher.init(Cipher.DECRYPT_MODE, privateKey);
154.
155. return cipher.doFinal(data);
156.
157. }
158.
159. /**
160. * 公钥加密
161. *
162. * @param data
163. * @param certificatePath
164. * @return
165. * @throws Exception
166. */
167. public static byte[] encryptByPublicKey(byte[] data, String certificatePath)
168. throws Exception {
169.
170. // 取得公钥
171. PublicKey publicKey = getPublicKey(certificatePath);
172. // 对数据加密
173. Cipher cipher = Cipher.getInstance(publicKey.getAlgorithm());
174. cipher.init(Cipher.ENCRYPT_MODE, publicKey);
175.
176. return cipher.doFinal(data);
177.
178. }
179.
180. /**
181. * 公钥解密
182. *
183. * @param data
184. * @param certificatePath
185. * @return
186. * @throws Exception
187. */
188. public static byte[] decryptByPublicKey(byte[] data, String certificatePath)
189. throws Exception {
190. // 取得公钥
191. PublicKey publicKey = getPublicKey(certificatePath);
192.
193. // 对数据加密
194. Cipher cipher = Cipher.getInstance(publicKey.getAlgorithm());
195. cipher.init(Cipher.DECRYPT_MODE, publicKey);
196.
197. return cipher.doFinal(data);
198.
199. }
200.
201. /**
202. * 验证Certificate
203. *
204. * @param certificatePath
205. * @return
206. */
207. public static boolean verifyCertificate(String certificatePath) {
208. return verifyCertificate(new Date(), certificatePath);
209. }
210.
211. /**
212. * 验证Certificate是否过期或无效
213. *
214. * @param date
215. * @param certificatePath
216. * @return
217. */
218. public static boolean verifyCertificate(Date date, String certificatePath) {
219. boolean status = true;
220. try {
221. // 取得证书
222. Certificate certificate = getCertificate(certificatePath);
223. // 验证证书是否过期或无效
224. status = verifyCertificate(date, certificate);
225. } catch (Exception e) {
226. status = false;
227. }
228. return status;
229. }
230.
231. /**
232. * 验证证书是否过期或无效
233. *
234. * @param date
235. * @param certificate
236. * @return
237. */
238. private static boolean verifyCertificate(Date date, Certificate certificate) {
239. boolean status = true;
240. try {
241. X509Certificate x509Certificate = (X509Certificate) certificate;
242. x509Certificate.checkValidity(date);
243. } catch (Exception e) {
244. status = false;
245. }
246. return status;
247. }
248.
249. /**
250. * 签名
251. *
252. * @param keyStorePath
253. * @param alias
254. * @param password
255. *
256. * @return
257. * @throws Exception
258. */
259. public static String sign(byte[] sign, String keyStorePath, String alias,
260. String password) throws Exception {
261. // 获得证书
262. X509Certificate x509Certificate = (X509Certificate) getCertificate(
263. keyStorePath, alias, password);
264. // 获取私钥
265. KeyStore ks = getKeyStore(keyStorePath, password);
266. // 取得私钥
267. PrivateKey privateKey = (PrivateKey) ks.getKey(alias, password
268. .toCharArray());
269.
270. // 构建签名
271. Signature signature = Signature.getInstance(x509Certificate
272. .getSigAlgName());
273. signature.initSign(privateKey);
274. signature.update(sign);
275. return encryptBASE64(signature.sign());
276. }
277.
278. /**
279. * 验证签名
280. *
281. * @param data
282. * @param sign
283. * @param certificatePath
284. * @return
285. * @throws Exception
286. */
287. public static boolean verify(byte[] data, String sign,
288. String certificatePath) throws Exception {
289. // 获得证书
290. X509Certificate x509Certificate = (X509Certificate) getCertificate(certificatePath);
291. // 获得公钥
292. PublicKey publicKey = x509Certificate.getPublicKey();
293. // 构建签名
294. Signature signature = Signature.getInstance(x509Certificate
295. .getSigAlgName());
296. signature.initVerify(publicKey);
297. signature.update(data);
298.
299. return signature.verify(decryptBASE64(sign));
300.
301. }
302.
303. /**
304. * 验证Certificate
305. *
306. * @param keyStorePath
307. * @param alias
308. * @param password
309. * @return
310. */
311. public static boolean verifyCertificate(Date date, String keyStorePath,
312. String alias, String password) {
313. boolean status = true;
314. try {
315. Certificate certificate = getCertificate(keyStorePath, alias,
316. password);
317. status = verifyCertificate(date, certificate);
318. } catch (Exception e) {
319. status = false;
320. }
321. return status;
322. }
323.
324. /**
325. * 验证Certificate
326. *
327. * @param keyStorePath
328. * @param alias
329. * @param password
330. * @return
331. */
332. public static boolean verifyCertificate(String keyStorePath, String alias,
333. String password) {
334. return verifyCertificate(new Date(), keyStorePath, alias, password);
335. }
336. }
再给出一个测试类:
Java代码
1. import static org.junit.Assert.*;
2.
3. import org.junit.Test;
4.
5. /**
6. *
7. * @author 梁栋
8. * @version 1.0
9. * @since 1.0
10. */
11. public class CertificateCoderTest {
12. private String password = "123456";
13. private String alias = "www.zlex.org";
14. private String certificatePath = "d:/zlex.cer";
15. private String keyStorePath = "d:/zlex.keystore";
16.
17. @Test
18. public void test() throws Exception {
19. System.err.println("公钥加密——私钥解密");
20. String inputStr = "Ceritifcate";
21. byte[] data = inputStr.getBytes();
22.
23. byte[] encrypt = CertificateCoder.encryptByPublicKey(data,
24. certificatePath);
25.
26. byte[] decrypt = CertificateCoder.decryptByPrivateKey(encrypt,
27. keyStorePath, alias, password);
28. String outputStr = new String(decrypt);
29.
30. System.err.println("加密前: " + inputStr + "\n\r" + "解密后: " + outputStr);
31.
32. // 验证数据一致
33. assertArrayEquals(data, decrypt);
34.
35. // 验证证书有效
36. assertTrue(CertificateCoder.verifyCertificate(certificatePath));
37.
38. }
39.
40. @Test
41. public void testSign() throws Exception {
42. System.err.println("私钥加密——公钥解密");
43.
44. String inputStr = "sign";
45. byte[] data = inputStr.getBytes();
46.
47. byte[] encodedData = CertificateCoder.encryptByPrivateKey(data,
48. keyStorePath, alias, password);
49.
50. byte[] decodedData = CertificateCoder.decryptByPublicKey(encodedData,
51. certificatePath);
52.
53. String outputStr = new String(decodedData);
54. System.err.println("加密前: " + inputStr + "\n\r" + "解密后: " + outputStr);
55. assertEquals(inputStr, outputStr);
56.
57. System.err.println("私钥签名——公钥验证签名");
58. // 产生签名
59. String sign = CertificateCoder.sign(encodedData, keyStorePath, alias,
60. password);
61. System.err.println("签名:\r" + sign);
62.
63. // 验证签名
64. boolean status = CertificateCoder.verify(encodedData, sign,
65. certificatePath);
66. System.err.println("状态:\r" + status);
67. assertTrue(status);
68.
69. }
70. }
控制台输出:
Console代码
1. 公钥加密——私钥解密
2. 加密前: Ceritificate
3.
4. 解密后: Ceritificate
5.
6. 私钥加密——公钥解密
7. 加密前: sign
8.
9. 解密后: sign
10. 私钥签名——公钥验证签名
11. 签名:
12. pqBn5m6PJlfOjH0A6U2o2mUmBsfgyEY1NWCbiyA/I5Gc3gaVNVIdj/zkGNZRqTjhf3+J9a9z9EI7
13. 6F2eWYd7punHx5oh6hfNgcKbVb52EfItl4QEN+djbXiPynn07+Lbg1NOjULnpEd6ZhLP1YwrEAuM
14. OfvX0e7/wplxLbySaKQ=
15.
16. 状态:
17. true
由此完成了证书验证体系!
同样,我们可以对代码做签名——代码签名!
通过工具JarSigner可以完成代码签名。
这里我们对tools.jar做代码签名,命令如下:
Shell代码
1. jarsigner -storetype jks -keystore zlex.keystore -verbose tools.jar www.zlex.org
控制台输出:
Console代码
1. 输入密钥库的口令短语:
2. 正在更新: META-INF/WWW_ZLEX.SF
3. 正在更新: META-INF/WWW_ZLEX.RSA
4. 正在签名: org/zlex/security/Security.class
5. 正在签名: org/zlex/tool/Main$1.class
6. 正在签名: org/zlex/tool/Main$2.class
7. 正在签名: org/zlex/tool/Main.class
8.
9. 警告:
10. 签名者证书将在六个月内过期。
此时,我们可以对签名后的jar做验证!
验证tools.jar,命令如下:
Shell代码
1. jarsigner -verify -verbose -certs tools.jar
控制台输出:
Console代码
1. 402 Sat Jun 20 16:25:14 CST 2009 META-INF/MANIFEST.MF
2. 532 Sat Jun 20 16:25:14 CST 2009 META-INF/WWW_ZLEX.SF
3. 889 Sat Jun 20 16:25:14 CST 2009 META-INF/WWW_ZLEX.RSA
4. sm 590 Wed Dec 10 13:03:42 CST 2008 org/zlex/security/Security.class
5.
6. X.509, CN=www.zlex.org, OU=zlex, O=zlex, L=BJ, ST=BJ, C=CN
7. [证书将在 09-9-18 下午3:27 到期]
8.
9. sm 705 Tue Dec 16 18:00:56 CST 2008 org/zlex/tool/Main$1.class
10.
11. X.509, CN=www.zlex.org, OU=zlex, O=zlex, L=BJ, ST=BJ, C=CN
12. [证书将在 09-9-18 下午3:27 到期]
13.
14. sm 779 Tue Dec 16 18:00:56 CST 2008 org/zlex/tool/Main$2.class
15.
16. X.509, CN=www.zlex.org, OU=zlex, O=zlex, L=BJ, ST=BJ, C=CN
17. [证书将在 09-9-18 下午3:27 到期]
18.
19. sm 12672 Tue Dec 16 18:00:56 CST 2008 org/zlex/tool/Main.class
20.
21. X.509, CN=www.zlex.org, OU=zlex, O=zlex, L=BJ, ST=BJ, C=CN
22. [证书将在 09-9-18 下午3:27 到期]
23.
24.
25. s = 已验证签名
26. m = 在清单中列出条目
27. k = 在密钥库中至少找到了一个证书
28. i = 在身份作用域内至少找到了一个证书
29.
30. jar 已验证。
31.
32. 警告:
33. 此 jar 包含签名者证书将在六个月内过期的条目。
代码签名认证的用途主要是对发布的软件做验证,支持 Sun Java .jar (Java Applet) 文件(J2SE)和 J2ME MIDlet Suite 文件。
Java加密技术(九)
文章分类:Java编程
在Java加密技术(八)中,我们模拟了一个基于RSA非对称加密网络的安全通信。现在我们深度了解一下现有的安全网络通信——SSL。
我们需要构建一个由CA机构签发的有效证书,这里我们使用上文中生成的自签名证书zlex.cer
这里,我们将证书导入到我们的密钥库。
Shell代码
1. keytool -import -alias www.zlex.org -file d:/zlex.cer -keystore d:/zlex.keystore
其中
-import表示导入
-alias指定别名,这里是www.zlex.org
-file指定算法,这里是d:/zlex.cer
-keystore指定存储位置,这里是d:/zlex.keystore
在这里我使用的密码为654321
控制台输出:
Console代码
1. 输入keystore密码:
2. 再次输入新密码:
3. 所有者:CN=www.zlex.org, OU=zlex, O=zlex, L=BJ, ST=BJ, C=CN
4. 签发人:CN=www.zlex.org, OU=zlex, O=zlex, L=BJ, ST=BJ, C=CN
5. 序列号:4a1e48df
6. 有效期: Thu May 28 16:18:39 CST 2009 至Wed Aug 26 16:18:39 CST 2009
7. 证书指纹:
8. MD5:19:CA:E6:36:E2:DF:AD:96:31:97:2F:A9:AD:FC:37:6A
9. SHA1:49:88:30:59:29:45:F1:69:CA:97:A9:6D:8A:CF:08:D2:C3:D5:C0:C4
10. 签名算法名称:SHA1withRSA
11. 版本: 3
12. 信任这个认证? [否]: y
13. 认证已添加至keystore中
OK,最复杂的准备工作已经完成。
接下来我们将域名www.zlex.org定位到本机上。打开C:\Windows\System32\drivers\etc\hosts文件,将www.zlex.org绑定在本机上。在文件末尾追加127.0.0.1 www.zlex.org。现在通过地址栏访问http://www.zlex.org,或者通过ping命令,如果能够定位到本机,域名映射就搞定了。
现在,配置tomcat。先将zlex.keystore拷贝到tomcat的conf目录下,然后配置server.xml。将如下内容加入配置文件
Xml代码
1.
注意clientAuth="false"测试阶段,置为false,正式使用时建议使用true。现在启动tomcat,访问https://www.zlex.org/。
显然,证书未能通过认证,这个时候你可以选择安装证书(上文中的zlex.cer文件就是证书),作为受信任的根证书颁发机构导入,再次重启浏览器(IE,其他浏览器对于域名www.zlex.org不支持本地方式访问),访问https://www.zlex.org/,你会看到地址栏中会有个小锁 ,就说明安装成功。所有的浏览器联网操作已经在RSA加密解密系统的保护之下了。但似乎我们感受不到。
这个时候很多人开始怀疑,如果我们要手工做一个这样的https的访问是不是需要把浏览器的这些个功能都实现呢?不需要!
接着上篇内容,给出如下代码实现:
Java代码
1. import java.io.FileInputStream;
2. import java.security.KeyStore;
3. import java.security.PrivateKey;
4. import java.security.PublicKey;
5. import java.security.Signature;
6. import java.security.cert.Certificate;
7. import java.security.cert.CertificateFactory;
8. import java.security.cert.X509Certificate;
9. import java.util.Date;
10.
11. import javax.crypto.Cipher;
12. import javax.net.ssl.HttpsURLConnection;
13. import javax.net.ssl.KeyManagerFactory;
14. import javax.net.ssl.SSLContext;
15. import javax.net.ssl.SSLSocketFactory;
16. import javax.net.ssl.TrustManagerFactory;
17.
18. /**
19. * 证书组件
20. *
21. * @author 梁栋
22. * @version 1.0
23. * @since 1.0
24. */
25. public abstract class CertificateCoder extends Coder {
26.
27. /**
28. * Java密钥库(Java Key Store,JKS)KEY_STORE
29. */
30. public static final String KEY_STORE = "JKS";
31.
32. public static final String X509 = "X.509";
33. public static final String SunX509 = "SunX509";
34. public static final String SSL = "SSL";
35.
36. /**
37. * 由KeyStore获得私钥
38. *
39. * @param keyStorePath
40. * @param alias
41. * @param password
42. * @return
43. * @throws Exception
44. */
45. private static PrivateKey getPrivateKey(String keyStorePath, String alias,
46. String password) throws Exception {
47. KeyStore ks = getKeyStore(keyStorePath, password);
48. PrivateKey key = (PrivateKey) ks.getKey(alias, password.toCharArray());
49. return key;
50. }
51.
52. /**
53. * 由Certificate获得公钥
54. *
55. * @param certificatePath
56. * @return
57. * @throws Exception
58. */
59. private static PublicKey getPublicKey(String certificatePath)
60. throws Exception {
61. Certificate certificate = getCertificate(certificatePath);
62. PublicKey key = certificate.getPublicKey();
63. return key;
64. }
65.
66. /**
67. * 获得Certificate
68. *
69. * @param certificatePath
70. * @return
71. * @throws Exception
72. */
73. private static Certificate getCertificate(String certificatePath)
74. throws Exception {
75. CertificateFactory certificateFactory = CertificateFactory
76. .getInstance(X509);
77. FileInputStream in = new FileInputStream(certificatePath);
78.
79. Certificate certificate = certificateFactory.generateCertificate(in);
80. in.close();
81.
82. return certificate;
83. }
84.
85. /**
86. * 获得Certificate
87. *
88. * @param keyStorePath
89. * @param alias
90. * @param password
91. * @return
92. * @throws Exception
93. */
94. private static Certificate getCertificate(String keyStorePath,
95. String alias, String password) throws Exception {
96. KeyStore ks = getKeyStore(keyStorePath, password);
97. Certificate certificate = ks.getCertificate(alias);
98.
99. return certificate;
100. }
101.
102. /**
103. * 获得KeyStore
104. *
105. * @param keyStorePath
106. * @param password
107. * @return
108. * @throws Exception
109. */
110. private static KeyStore getKeyStore(String keyStorePath, String password)
111. throws Exception {
112. FileInputStream is = new FileInputStream(keyStorePath);
113. KeyStore ks = KeyStore.getInstance(KEY_STORE);
114. ks.load(is, password.toCharArray());
115. is.close();
116. return ks;
117. }
118.
119. /**
120. * 私钥加密
121. *
122. * @param data
123. * @param keyStorePath
124. * @param alias
125. * @param password
126. * @return
127. * @throws Exception
128. */
129. public static byte[] encryptByPrivateKey(byte[] data, String keyStorePath,
130. String alias, String password) throws Exception {
131. // 取得私钥
132. PrivateKey privateKey = getPrivateKey(keyStorePath, alias, password);
133.
134. // 对数据加密
135. Cipher cipher = Cipher.getInstance(privateKey.getAlgorithm());
136. cipher.init(Cipher.ENCRYPT_MODE, privateKey);
137.
138. return cipher.doFinal(data);
139.
140. }
141.
142. /**
143. * 私钥解密
144. *
145. * @param data
146. * @param keyStorePath
147. * @param alias
148. * @param password
149. * @return
150. * @throws Exception
151. */
152. public static byte[] decryptByPrivateKey(byte[] data, String keyStorePath,
153. String alias, String password) throws Exception {
154. // 取得私钥
155. PrivateKey privateKey = getPrivateKey(keyStorePath, alias, password);
156.
157. // 对数据加密
158. Cipher cipher = Cipher.getInstance(privateKey.getAlgorithm());
159. cipher.init(Cipher.DECRYPT_MODE, privateKey);
160.
161. return cipher.doFinal(data);
162.
163. }
164.
165. /**
166. * 公钥加密
167. *
168. * @param data
169. * @param certificatePath
170. * @return
171. * @throws Exception
172. */
173. public static byte[] encryptByPublicKey(byte[] data, String certificatePath)
174. throws Exception {
175.
176. // 取得公钥
177. PublicKey publicKey = getPublicKey(certificatePath);
178. // 对数据加密
179. Cipher cipher = Cipher.getInstance(publicKey.getAlgorithm());
180. cipher.init(Cipher.ENCRYPT_MODE, publicKey);
181.
182. return cipher.doFinal(data);
183.
184. }
185.
186. /**
187. * 公钥解密
188. *
189. * @param data
190. * @param certificatePath
191. * @return
192. * @throws Exception
193. */
194. public static byte[] decryptByPublicKey(byte[] data, String certificatePath)
195. throws Exception {
196. // 取得公钥
197. PublicKey publicKey = getPublicKey(certificatePath);
198.
199. // 对数据加密
200. Cipher cipher = Cipher.getInstance(publicKey.getAlgorithm());
201. cipher.init(Cipher.DECRYPT_MODE, publicKey);
202.
203. return cipher.doFinal(data);
204.
205. }
206.
207. /**
208. * 验证Certificate
209. *
210. * @param certificatePath
211. * @return
212. */
213. public static boolean verifyCertificate(String certificatePath) {
214. return verifyCertificate(new Date(), certificatePath);
215. }
216.
217. /**
218. * 验证Certificate是否过期或无效
219. *
220. * @param date
221. * @param certificatePath
222. * @return
223. */
224. public static boolean verifyCertificate(Date date, String certificatePath) {
225. boolean status = true;
226. try {
227. // 取得证书
228. Certificate certificate = getCertificate(certificatePath);
229. // 验证证书是否过期或无效
230. status = verifyCertificate(date, certificate);
231. } catch (Exception e) {
232. status = false;
233. }
234. return status;
235. }
236.
237. /**
238. * 验证证书是否过期或无效
239. *
240. * @param date
241. * @param certificate
242. * @return
243. */
244. private static boolean verifyCertificate(Date date, Certificate certificate) {
245. boolean status = true;
246. try {
247. X509Certificate x509Certificate = (X509Certificate) certificate;
248. x509Certificate.checkValidity(date);
249. } catch (Exception e) {
250. status = false;
251. }
252. return status;
253. }
254.
255. /**
256. * 签名
257. *
258. * @param keyStorePath
259. * @param alias
260. * @param password
261. *
262. * @return
263. * @throws Exception
264. */
265. public static String sign(byte[] sign, String keyStorePath, String alias,
266. String password) throws Exception {
267. // 获得证书
268. X509Certificate x509Certificate = (X509Certificate) getCertificate(
269. keyStorePath, alias, password);
270. // 获取私钥
271. KeyStore ks = getKeyStore(keyStorePath, password);
272. // 取得私钥
273. PrivateKey privateKey = (PrivateKey) ks.getKey(alias, password
274. .toCharArray());
275.
276. // 构建签名
277. Signature signature = Signature.getInstance(x509Certificate
278. .getSigAlgName());
279. signature.initSign(privateKey);
280. signature.update(sign);
281. return encryptBASE64(signature.sign());
282. }
283.
284. /**
285. * 验证签名
286. *
287. * @param data
288. * @param sign
289. * @param certificatePath
290. * @return
291. * @throws Exception
292. */
293. public static boolean verify(byte[] data, String sign,
294. String certificatePath) throws Exception {
295. // 获得证书
296. X509Certificate x509Certificate = (X509Certificate) getCertificate(certificatePath);
297. // 获得公钥
298. PublicKey publicKey = x509Certificate.getPublicKey();
299. // 构建签名
300. Signature signature = Signature.getInstance(x509Certificate
301. .getSigAlgName());
302. signature.initVerify(publicKey);
303. signature.update(data);
304.
305. return signature.verify(decryptBASE64(sign));
306.
307. }
308.
309. /**
310. * 验证Certificate
311. *
312. * @param keyStorePath
313. * @param alias
314. * @param password
315. * @return
316. */
317. public static boolean verifyCertificate(Date date, String keyStorePath,
318. String alias, String password) {
319. boolean status = true;
320. try {
321. Certificate certificate = getCertificate(keyStorePath, alias,
322. password);
323. status = verifyCertificate(date, certificate);
324. } catch (Exception e) {
325. status = false;
326. }
327. return status;
328. }
329.
330. /**
331. * 验证Certificate
332. *
333. * @param keyStorePath
334. * @param alias
335. * @param password
336. * @return
337. */
338. public static boolean verifyCertificate(String keyStorePath, String alias,
339. String password) {
340. return verifyCertificate(new Date(), keyStorePath, alias, password);
341. }
342.
343. /**
344. * 获得SSLSocektFactory
345. *
346. * @param password
347. * 密码
348. * @param keyStorePath
349. * 密钥库路径
350. *
351. * @param trustKeyStorePath
352. * 信任库路径
353. * @return
354. * @throws Exception
355. */
356. private static SSLSocketFactory getSSLSocketFactory(String password,
357. String keyStorePath, String trustKeyStorePath) throws Exception {
358. // 初始化密钥库
359. KeyManagerFactory keyManagerFactory = KeyManagerFactory
360. .getInstance(SunX509);
361. KeyStore keyStore = getKeyStore(keyStorePath, password);
362. keyManagerFactory.init(keyStore, password.toCharArray());
363.
364. // 初始化信任库
365. TrustManagerFactory trustManagerFactory = TrustManagerFactory
366. .getInstance(SunX509);
367. KeyStore trustkeyStore = getKeyStore(trustKeyStorePath, password);
368. trustManagerFactory.init(trustkeyStore);
369.
370. // 初始化SSL上下文
371. SSLContext ctx = SSLContext.getInstance(SSL);
372. ctx.init(keyManagerFactory.getKeyManagers(), trustManagerFactory
373. .getTrustManagers(), null);
374. SSLSocketFactory sf = ctx.getSocketFactory();
375.
376. return sf;
377. }
378.
379. /**
380. * 为HttpsURLConnection配置SSLSocketFactory
381. *
382. * @param conn
383. * HttpsURLConnection
384. * @param password
385. * 密码
386. * @param keyStorePath
387. * 密钥库路径
388. *
389. * @param trustKeyStorePath
390. * 信任库路径
391. * @throws Exception
392. */
393. public static void configSSLSocketFactory(HttpsURLConnection conn,
394. String password, String keyStorePath, String trustKeyStorePath)
395. throws Exception {
396. conn.setSSLSocketFactory(getSSLSocketFactory(password, keyStorePath,
397. trustKeyStorePath));
398. }
399. }
增加了configSSLSocketFactory方法供外界调用,该方法为HttpsURLConnection配置了SSLSocketFactory。当HttpsURLConnection配置了SSLSocketFactory后,我们就可以通过HttpsURLConnection的getInputStream、getOutputStream,像往常使用HttpURLConnection做操作了。尤其要说明一点,未配置SSLSocketFactory前,HttpsURLConnection的getContentLength()获得值永远都是-1。
给出相应测试类:
Java代码
1. import static org.junit.Assert.*;
2.
3. import java.io.DataInputStream;
4. import java.io.InputStream;
5. import java.net.URL;
6.
7. import javax.net.ssl.HttpsURLConnection;
8.
9. import org.junit.Test;
10.
11. /**
12. *
13. * @author 梁栋
14. * @version 1.0
15. * @since 1.0
16. */
17. public class CertificateCoderTest {
18. private String password = "123456";
19. private String alias = "www.zlex.org";
20. private String certificatePath = "d:/zlex.cer";
21. private String keyStorePath = "d:/zlex.keystore";
22. private String clientKeyStorePath = "d:/zlex-client.keystore";
23. private String clientPassword = "654321";
24.
25. @Test
26. public void test() throws Exception {
27. System.err.println("公钥加密——私钥解密");
28. String inputStr = "Ceritifcate";
29. byte[] data = inputStr.getBytes();
30.
31. byte[] encrypt = CertificateCoder.encryptByPublicKey(data,
32. certificatePath);
33.
34. byte[] decrypt = CertificateCoder.decryptByPrivateKey(encrypt,
35. keyStorePath, alias, password);
36. String outputStr = new String(decrypt);
37.
38. System.err.println("加密前: " + inputStr + "\n\r" + "解密后: " + outputStr);
39.
40. // 验证数据一致
41. assertArrayEquals(data, decrypt);
42.
43. // 验证证书有效
44. assertTrue(CertificateCoder.verifyCertificate(certificatePath));
45.
46. }
47.
48. @Test
49. public void testSign() throws Exception {
50. System.err.println("私钥加密——公钥解密");
51.
52. String inputStr = "sign";
53. byte[] data = inputStr.getBytes();
54.
55. byte[] encodedData = CertificateCoder.encryptByPrivateKey(data,
56. keyStorePath, alias, password);
57.
58. byte[] decodedData = CertificateCoder.decryptByPublicKey(encodedData,
59. certificatePath);
60.
61. String outputStr = new String(decodedData);
62. System.err.println("加密前: " + inputStr + "\n\r" + "解密后: " + outputStr);
63. assertEquals(inputStr, outputStr);
64.
65. System.err.println("私钥签名——公钥验证签名");
66. // 产生签名
67. String sign = CertificateCoder.sign(encodedData, keyStorePath, alias,
68. password);
69. System.err.println("签名:\r" + sign);
70.
71. // 验证签名
72. boolean status = CertificateCoder.verify(encodedData, sign,
73. certificatePath);
74. System.err.println("状态:\r" + status);
75. assertTrue(status);
76.
77. }
78.
79. @Test
80. public void testHttps() throws Exception {
81. URL url = new URL("https://www.zlex.org/examples/");
82. HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
83.
84. conn.setDoInput(true);
85. conn.setDoOutput(true);
86.
87. CertificateCoder.configSSLSocketFactory(conn, clientPassword,
88. clientKeyStorePath, clientKeyStorePath);
89.
90. InputStream is = conn.getInputStream();
91.
92. int length = conn.getContentLength();
93.
94. DataInputStream dis = new DataInputStream(is);
95. byte[] data = new byte[length];
96. dis.readFully(data);
97.
98. dis.close();
99. System.err.println(new String(data));
100. conn.disconnect();
101. }
102. }
注意testHttps方法,几乎和我们往常做HTTP访问没有差别,我们来看控制台输出:
Console代码
1.
17.
18. Apache Tomcat Examples
19.
20.
21.
22.
23.
Apache Tomcat Examples
24.
25.
26. - Servlets examples
27. - JSP Examples
28.
29.
通过浏览器直接访问https://www.zlex.org/examples/你也会获得上述内容。也就是说应用甲方作为服务器构建tomcat服务,乙方可以通过上述方式访问甲方受保护的SSL应用,并且不需要考虑具体的加密解密问题。甲乙双方可以经过相应配置,通过双方的tomcat配置有效的SSL服务,简化上述代码实现,完全通过证书配置完成SSL双向认证!
Java加密技术(十)
文章分类:Java编程
在Java 加密技术(九)中,我们使用自签名证书完成了认证。接下来,我们使用第三方CA签名机构完成证书签名。
这里我们使用thawte提供的测试用21天免费ca证书。
1.要在该网站上注明你的域名,这里使用www.zlex.org作为测试用域名(请勿使用该域名作为你的域名地址,该域名受法律保护!请使用其他非注册域名!)。
2.如果域名有效,你会收到邮件要求你访问https://www.thawte.com/cgi/server/try.exe获得ca证书。
3.复述密钥库的创建。
Shell代码
1. keytool -genkey -validity 36000 -alias www.zlex.org -keyalg RSA -keystore d:\zlex.keystore
在这里我使用的密码为 123456
控制台输出:
Console代码
1. 输入keystore密码:
2. 再次输入新密码:
3. 您的名字与姓氏是什么?
4. [Unknown]: www.zlex.org
5. 您的组织单位名称是什么?
6. [Unknown]: zlex
7. 您的组织名称是什么?
8. [Unknown]: zlex
9. 您所在的城市或区域名称是什么?
10. [Unknown]: BJ
11. 您所在的州或省份名称是什么?
12. [Unknown]: BJ
13. 该单位的两字母国家代码是什么
14. [Unknown]: CN
15. CN=www.zlex.org, OU=zlex, O=zlex, L=BJ, ST=BJ, C=CN 正确吗?
16. [否]: Y
17.
18. 输入的主密码
19. (如果和 keystore 密码相同,按回车):
20. 再次输入新密码:
4.通过如下命令,从zlex.keystore中导出CA证书申请。
Shell代码
1. keytool -certreq -alias www.zlex.org -file d:\zlex.csr -keystore d:\zlex.keystore -v
你会获得zlex.csr文件,可以用记事本打开,内容如下格式:
Text代码
1. -----BEGIN NEW CERTIFICATE REQUEST-----
2. MIIBnDCCAQUCAQAwXDELMAkGA1UEBhMCQ04xCzAJBgNVBAgTAkJKMQswCQYDVQQHEwJCSjENMAsG
3. A1UEChMEemxleDENMAsGA1UECxMEemxleDEVMBMGA1UEAxMMd3d3LnpsZXgub3JnMIGfMA0GCSqG
4. SIb3DQEBAQUAA4GNADCBiQKBgQCR6DXU9Mp+mCKO7cv9JPsj0n1Ec/GpM09qvhpgX3FNad/ZWSDc
5. vU77YXZSoF9hQp3w1LC+eeKgd2MlVpXTvbVwBNVd2HiQPp37ic6BUUjSaX8LHtCl7l0BIEye9qQ2
6. j8G0kak7e8ZA0s7nb3Ymq/K8BV7v0MQIdhIc1bifK9ZDewIDAQABoAAwDQYJKoZIhvcNAQEFBQAD
7. gYEAMA1r2fbZPtNx37U9TRwadCH2TZZecwKJS/hskNm6ryPKIAp9APWwAyj8WJHRBz5SpZM4zmYO
8. oMCI8BcnY2A4JP+R7/SwXTdH/xcg7NVghd9A2SCgqMpF7KMfc5dE3iygdiPu+UhY200Dvpjx8gmJ
9. 1UbH3+nqMUyCrZgURFslOUY=
10. -----END NEW CERTIFICATE REQUEST-----
5.将上述文件内容拷贝到https://www.thawte.com/cgi/server/try.exe中,点击next,获得回应内容,这里是p7b格式。
内容如下:
Text代码
1. -----BEGIN PKCS7-----
2. MIIF3AYJKoZIhvcNAQcCoIIFzTCCBckCAQExADALBgkqhkiG9w0BBwGgggWxMIID
3. EDCCAnmgAwIBAgIQA/mx/pKoaB+KGX2hveFU9zANBgkqhkiG9w0BAQUFADCBhzEL
4. MAkGA1UEBhMCWkExIjAgBgNVBAgTGUZPUiBURVNUSU5HIFBVUlBPU0VTIE9OTFkx
5. HTAbBgNVBAoTFFRoYXd0ZSBDZXJ0aWZpY2F0aW9uMRcwFQYDVQQLEw5URVNUIFRF
6. U1QgVEVTVDEcMBoGA1UEAxMTVGhhd3RlIFRlc3QgQ0EgUm9vdDAeFw0wOTA1Mjgw
7. MDIxMzlaFw0wOTA2MTgwMDIxMzlaMFwxCzAJBgNVBAYTAkNOMQswCQYDVQQIEwJC
8. SjELMAkGA1UEBxMCQkoxDTALBgNVBAoTBHpsZXgxDTALBgNVBAsTBHpsZXgxFTAT
9. BgNVBAMTDHd3dy56bGV4Lm9yZzCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA
10. keg11PTKfpgiju3L/ST7I9J9RHPxqTNPar4aYF9xTWnf2Vkg3L1O+2F2UqBfYUKd
11. 8NSwvnnioHdjJVaV0721cATVXdh4kD6d+4nOgVFI0ml/Cx7Qpe5dASBMnvakNo/B
12. tJGpO3vGQNLO5292JqvyvAVe79DECHYSHNW4nyvWQ3sCAwEAAaOBpjCBozAMBgNV
13. HRMBAf8EAjAAMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjBABgNVHR8E
14. OTA3MDWgM6Axhi9odHRwOi8vY3JsLnRoYXd0ZS5jb20vVGhhd3RlUHJlbWl1bVNl
15. cnZlckNBLmNybDAyBggrBgEFBQcBAQQmMCQwIgYIKwYBBQUHMAGGFmh0dHA6Ly9v
16. Y3NwLnRoYXd0ZS5jb20wDQYJKoZIhvcNAQEFBQADgYEATPuxZbtJJSPmXvfrr1yz
17. xqM06IwTZ6UU0lZRG7I0WufMjNMKdpn8hklUhE17mxAhGSpewLVVeLR7uzBLFkuC
18. X7wMXxhoYdJZtNai72izU6Rd1oknao7diahvRxPK4IuQ7y2oZ511/4T4vgY6iRAj
19. q4q76HhPJrVRL/sduaiu+gYwggKZMIICAqADAgECAgEAMA0GCSqGSIb3DQEBBAUA
20. MIGHMQswCQYDVQQGEwJaQTEiMCAGA1UECBMZRk9SIFRFU1RJTkcgUFVSUE9TRVMg
21. T05MWTEdMBsGA1UEChMUVGhhd3RlIENlcnRpZmljYXRpb24xFzAVBgNVBAsTDlRF
22. U1QgVEVTVCBURVNUMRwwGgYDVQQDExNUaGF3dGUgVGVzdCBDQSBSb290MB4XDTk2
23. MDgwMTAwMDAwMFoXDTIwMTIzMTIxNTk1OVowgYcxCzAJBgNVBAYTAlpBMSIwIAYD
24. VQQIExlGT1IgVEVTVElORyBQVVJQT1NFUyBPTkxZMR0wGwYDVQQKExRUaGF3dGUg
25. Q2VydGlmaWNhdGlvbjEXMBUGA1UECxMOVEVTVCBURVNUIFRFU1QxHDAaBgNVBAMT
26. E1RoYXd0ZSBUZXN0IENBIFJvb3QwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGB
27. ALV9kG+Os6x/DOhm+tKUQfzVMWGhE95sFmEtkMMTX2Zi4n6i6BvzoReJ5njzt1LF
28. cqu4EUk9Ji20egKKfmqRzmQFLP7+1niSdfJEUE7cKY40QoI99270PTrLjJeaMcCl
29. +AYl+kD+RL5BtuKKU3PurYcsCsre6aTvjMcqpTJOGeSPAgMBAAGjEzARMA8GA1Ud
30. EwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEEBQADgYEAgozj7BkD9O8si2V0v+EZ/t7E
31. fz/LC8y6mD7IBUziHy5/53ymGAGLtyhXHvX+UIE6UWbHro3IqVkrmY5uC93Z2Wew
32. A/6edK3KFUcUikrLeewM7gmqsiASEKx2mKRKlu12jXyNS5tXrPWRDvUKtFC1uL9a
33. 12rFAQS2BkIk7aU+ghYxAA==
34. -----END PKCS7-----
将其存储为zlex.p7b
6.将由CA签发的证书导入密钥库。
Shell代码
1. keytool -import -trustcacerts -alias www.zlex.org -file d:\zlex.p7b -keystore d:\zlex.keystore -v
在这里我使用的密码为 123456
控制台输出:
Console代码
1. 输入keystore密码:
2.
3. 回复中的最高级认证:
4.
5. 所有者:CN=Thawte Test CA Root, OU=TEST TEST TEST, O=Thawte Certification, ST=FOR
6. TESTING PURPOSES ONLY, C=ZA
7. 签发人:CN=Thawte Test CA Root, OU=TEST TEST TEST, O=Thawte Certification, ST=FOR
8. TESTING PURPOSES ONLY, C=ZA
9. 序列号:0
10. 有效期: Thu Aug 01 08:00:00 CST 1996 至Fri Jan 01 05:59:59 CST 2021
11. 证书指纹:
12. MD5:5E:E0:0E:1D:17:B7:CA:A5:7D:36:D6:02:DF:4D:26:A4
13. SHA1:39:C6:9D:27:AF:DC:EB:47:D6:33:36:6A:B2:05:F1:47:A9:B4:DA:EA
14. 签名算法名称:MD5withRSA
15. 版本: 3
16.
17. 扩展:
18.
19. #1: ObjectId: 2.5.29.19 Criticality=true
20. BasicConstraints:[
21. CA:true
22. PathLen:2147483647
23. ]
24.
25.
26. ... 是不可信的。 还是要安装回复? [否]: Y
27. 认证回复已安装在 keystore中
28. [正在存储 d:\zlex.keystore]
7.域名定位
将域名www.zlex.org定位到本机上。打开C:\Windows\System32\drivers\etc\hosts文件,将www.zlex.org绑定在本机上。在文件末尾追加127.0.0.1 www.zlex.org。现在通过地址栏访问http://www.zlex.org,或者通过ping命令,如果能够定位到本机,域名映射就搞定了。
8.配置server.xml
Xml代码
1.
将文件zlex.keystore拷贝到tomcat的conf目录下,重新启动tomcat。访问https://www.zlex.org/,我们发现联网有些迟钝。大约5秒钟后,网页正常显示,同时有如下图所示:
浏览器验证了该CA机构的有效性。
打开证书,如下图所示:
调整测试类:
Java代码
1. import static org.junit.Assert.*;
2.
3. import java.io.DataInputStream;
4. import java.io.InputStream;
5. import java.net.URL;
6.
7. import javax.net.ssl.HttpsURLConnection;
8.
9. import org.junit.Test;
10.
11. /**
12. *
13. * @author 梁栋
14. * @version 1.0
15. * @since 1.0
16. */
17. public class CertificateCoderTest {
18. private String password = "123456";
19. private String alias = "www.zlex.org";
20. private String certificatePath = "d:/zlex.cer";
21. private String keyStorePath = "d:/zlex.keystore";
22.
23. @Test
24. public void test() throws Exception {
25. System.err.println("公钥加密——私钥解密");
26. String inputStr = "Ceritifcate";
27. byte[] data = inputStr.getBytes();
28.
29. byte[] encrypt = CertificateCoder.encryptByPublicKey(data,
30. certificatePath);
31.
32. byte[] decrypt = CertificateCoder.decryptByPrivateKey(encrypt,
33. keyStorePath, alias, password);
34. String outputStr = new String(decrypt);
35.
36. System.err.println("加密前: " + inputStr + "\n\r" + "解密后: " + outputStr);
37.
38. // 验证数据一致
39. assertArrayEquals(data, decrypt);
40.
41. // 验证证书有效
42. assertTrue(CertificateCoder.verifyCertificate(certificatePath));
43.
44. }
45.
46. @Test
47. public void testSign() throws Exception {
48. System.err.println("私钥加密——公钥解密");
49.
50. String inputStr = "sign";
51. byte[] data = inputStr.getBytes();
52.
53. byte[] encodedData = CertificateCoder.encryptByPrivateKey(data,
54. keyStorePath, alias, password);
55.
56. byte[] decodedData = CertificateCoder.decryptByPublicKey(encodedData,
57. certificatePath);
58.
59. String outputStr = new String(decodedData);
60. System.err.println("加密前: " + inputStr + "\n\r" + "解密后: " + outputStr);
61. assertEquals(inputStr, outputStr);
62.
63. System.err.println("私钥签名——公钥验证签名");
64. // 产生签名
65. String sign = CertificateCoder.sign(encodedData, keyStorePath, alias,
66. password);
67. System.err.println("签名:\r" + sign);
68.
69. // 验证签名
70. boolean status = CertificateCoder.verify(encodedData, sign,
71. certificatePath);
72. System.err.println("状态:\r" + status);
73. assertTrue(status);
74.
75. }
76.
77. @Test
78. public void testHttps() throws Exception {
79. URL url = new URL("https://www.zlex.org/examples/");
80. HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
81.
82. conn.setDoInput(true);
83. conn.setDoOutput(true);
84.
85. CertificateCoder.configSSLSocketFactory(conn, password, keyStorePath,
86. keyStorePath);
87.
88. InputStream is = conn.getInputStream();
89.
90. int length = conn.getContentLength();
91.
92. DataInputStream dis = new DataInputStream(is);
93. byte[] data = new byte[length];
94. dis.readFully(data);
95.
96. dis.close();
97. conn.disconnect();
98. System.err.println(new String(data));
99. }
100. }
再次执行,验证通过!
由此,我们了基于SSL协议的认证过程。测试类的testHttps方法模拟了一次浏览器的HTTPS访问。