Java 安全编程:RSA 加密解密

RSA 是最常用非对称加密算法。常用于消息签名。它的加解密的密钥是成对出现的。使用私钥加密只能用对应的公钥才能解密。这样防止了类似 DES 等对称加密算法的密钥传输的问题。其加密效率比 DES 慢。

详细内容请查看:http://zh.wikipedia.org/wiki/RSA%E5%8A%A0%E5%AF%86%E6%BC%94%E7%AE%97%E6%B3%95

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
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
/**
* RSA 算法加密解密例子
* 注意:RSA 加密对明文的长度是有限制的,RSA 加密明文最大长度 117 字节,解密要求密文最大长度为 128 字节,所以在加密和解密的过程中需要分块进行。
*
*/
public class RSACoder extends TestCase {

/**
* 测试用私钥加密再用公钥解密
*
* @throws Exception
*/
public void test1() throws Exception {
byte[] arr = this.readFileIntoByteArr(new File("D:/data.txt"));
System.out.println("加密前的明文:" + new String(arr, "GBK"));

Map<String, Object> keyMap = this.initKey();
PrivateKey priKey = (PrivateKey) keyMap.get("PrivateKey");
PublicKey pubKey = (PublicKey) keyMap.get("PublicKey");

byte[] encryptData = this.encryptByPriKey(arr, priKey.getEncoded());
System.out.println("密文:" + Hex.encodeHexString(encryptData));

byte[] data = this.decryptByPubKey(encryptData, pubKey.getEncoded());
System.out.println("解密后的明文:" + new String(data, "GBK"));
}

/**
* 测试数字签名与验签
*
* @throws Exception
*/
public void test2() throws Exception {
byte[] arr = this.readFileIntoByteArr(new File("D:/data.txt"));
System.out.println("签名主体:" + new String(arr, "GBK"));

Map<String, Object> keyMap = this.initKey();
PrivateKey priKey = (PrivateKey) keyMap.get("PrivateKey");
PublicKey pubKey = (PublicKey) keyMap.get("PublicKey");

byte[] sign = this.sign(arr, priKey.getEncoded());
System.out.println("签名:" + Hex.encodeHexString(sign));

boolean b = this.verify(arr, pubKey.getEncoded(), sign);
System.out.println(b ? "验签成功" : "验签失败");
}

/**
* 测试直接对主体签名与先对主体加摘要再签名得到的签名是不一样的。
*
* @throws Exception
*/
public void test3() throws Exception {
byte[] arr = this.readFileIntoByteArr(new File("D:/data.txt"));
System.out.println("签名主体:" + new String(arr, "GBK"));

Map<String, Object> keyMap = this.initKey();
PrivateKey priKey = (PrivateKey) keyMap.get("PrivateKey");
PublicKey pubKey = (PublicKey) keyMap.get("PublicKey");
// 直接签名
byte[] sign1 = this.sign(arr, priKey.getEncoded());
System.out.println("签名:" + Hex.encodeHexString(sign1));

// 先得到 MD5 摘要再签名
byte[] md5 = DigestUtils.md5(arr);
byte[] sign2 = this.sign(md5, priKey.getEncoded());
System.out.println("签名:" + Hex.encodeHexString(sign2));
}

/**
* 生成密钥对
*
* @return
* @throws NoSuchAlgorithmException
*/
public Map<String, Object> initKey() throws NoSuchAlgorithmException {
// 实例化密钥对生成器
KeyPairGenerator gener = KeyPairGenerator.getInstance("RSA");
gener.initialize(1024);
KeyPair pair = gener.generateKeyPair();
PrivateKey priKey = pair.getPrivate();
PublicKey pubKey = pair.getPublic();

Map<String, Object> keyMap = new HashMap<String, Object>(2);
keyMap.put("PrivateKey", priKey);
keyMap.put("PublicKey", pubKey);

System.out.println("私钥:" + Hex.encodeHexString(priKey.getEncoded()));
System.out.println("公钥:" + Hex.encodeHexString(pubKey.getEncoded()));

return keyMap;
}

/**
* 读取文件内容到 byte 数组
*
* @param file
* @return
* @throws IOException
*/
public byte[] readFileIntoByteArr(File file) throws IOException {
ByteArrayOutputStream bout = new ByteArrayOutputStream();
try {
FileInputStream in = new FileInputStream(file);
byte[] tmpbuf = new byte[1024];
int count = 0;
while ((count = in.read(tmpbuf)) != -1) {
bout.write(tmpbuf, 0, count);
tmpbuf = new byte[1024];
}
in.close();
} catch (FileNotFoundException e) {
throw new FileNotFoundException("文件" + file.getPath() + "不存在");
} catch (IOException e) {
throw new IOException("读取文件内容到 BYTE 数组中出现 IO 异常", e);
}
return bout.toByteArray();
}

/**
* 私钥加密
*
* @param data
* @param priKeyByte
* @return
* @throws NoSuchAlgorithmException
* @throws InvalidKeySpecException
* @throws NoSuchPaddingException
* @throws InvalidKeyException
* @throws IllegalBlockSizeException
* @throws BadPaddingException
*/
public byte[] encryptByPriKey(byte[] data, byte[] priKeyByte) throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchPaddingException,
InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
// 获取私钥
KeySpec spec = new PKCS8EncodedKeySpec(priKeyByte);
// 生成私钥
Key priKey = keyFactory.generatePrivate(spec);
Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
cipher.init(Cipher.ENCRYPT_MODE, priKey);
return cipher.doFinal(data);
}

/**
* 公钥解密
*
* @param data
* @param pubKeyByte
* @return
* @throws NoSuchAlgorithmException
* @throws InvalidKeySpecException
* @throws NoSuchPaddingException
* @throws InvalidKeyException
* @throws IllegalBlockSizeException
* @throws BadPaddingException
*/
public byte[] decryptByPubKey(byte[] data, byte[] pubKeyByte) throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchPaddingException,
InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
// 获取公钥
KeySpec spec = new X509EncodedKeySpec(pubKeyByte);
// 生成公钥
Key pubKey = keyFactory.generatePublic(spec);
Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
cipher.init(Cipher.DECRYPT_MODE, pubKey);
return cipher.doFinal(data);
}

/**
* 使用 MD5withRSA 算法,加签
*
* @param data
* @param priKeyByte
* @return
* @throws NoSuchAlgorithmException
* @throws InvalidKeySpecException
* @throws InvalidKeyException
* @throws SignatureException
*/
public byte[] sign(byte[] data, byte[] priKeyByte) throws NoSuchAlgorithmException, InvalidKeySpecException, InvalidKeyException, SignatureException {
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
// 获取私钥
KeySpec spec = new PKCS8EncodedKeySpec(priKeyByte);
// 生成私钥
PrivateKey priKey = keyFactory.generatePrivate(spec);

Signature signature = Signature.getInstance("MD5withRSA");
signature.initSign(priKey);
signature.update(data);

return signature.sign();
}

/**
* 使用 MD5withRSA 算法,解签
*
* @param data
* @param pubKeyByte
* @param sign
* @return
* @throws NoSuchAlgorithmException
* @throws InvalidKeySpecException
* @throws InvalidKeyException
* @throws SignatureException
*/
public boolean verify(byte[] data, byte[] pubKeyByte, byte[] sign) throws NoSuchAlgorithmException, InvalidKeySpecException, InvalidKeyException,
SignatureException {
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
// 获取公钥
KeySpec spec = new X509EncodedKeySpec(pubKeyByte);
// 生成公钥
PublicKey pubKey = keyFactory.generatePublic(spec);
Signature signature = Signature.getInstance("MD5withRSA");
signature.initVerify(pubKey);
signature.update(data);
return signature.verify(sign);
}

}