Java中RSA的使用

RSA为非对称加密,根据应用场景不同,分如下两种:
1.签名:使用私钥加密,公钥解密。用于验证私钥持有者的身份,防止私钥所有者发布的内容被篡改,但是公钥一般是公开的,所以无法保证加密内容不被其他人获得。
2.加密:公钥加密,私钥解密。加密内容无法被他人获得,但是信息可能被他人篡改伪造。
以下为Java中RSA算法的实现:

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
public class RSACoder {
public static final String KEY_ALGORITHM = "RSA";
public static final String SIGNATURE_ALGORITHM = "MD5withRSA";
private static final String PUBLIC_KEY = "RSAPublicKey";
private static final String PRIVATE_KEY = "RSAPrivateKey";
/**
* 初始化密钥
*
* @return
* @throws Exception
*/
public static Map<String, Object> initKey() throws Exception {
KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(KEY_ALGORITHM);
keyPairGen.initialize(512);
KeyPair keyPair = keyPairGen.generateKeyPair();
// 公钥
RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
// 私钥
RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
Map<String, Object> keyMap = new HashMap<String, Object>(2);
keyMap.put(PUBLIC_KEY, publicKey);
keyMap.put(PRIVATE_KEY, privateKey);
return keyMap;
}
/**
* 取得私钥
*
* @param keyMap
* @return
* @throws Exception
*/
public static String getPrivateKey(Map<String, Object> keyMap) throws Exception {
Key key = (Key) keyMap.get(PRIVATE_KEY);
return encryptHex(key.getEncoded());
}
/**
* 取得公钥
*
* @param keyMap
* @return
* @throws Exception
*/
public static String getPublicKey(Map<String, Object> keyMap) throws Exception {
Key key = (Key) keyMap.get(PUBLIC_KEY);
return encryptHex(key.getEncoded());
}
/**
* from koduser 加密<br>
* 用私钥加密
*
* @param data
* @param key
* @return
* @throws Exception
*/
public static byte[] encryptByPrivateKey(byte[] data, String key) throws Exception {
// 对密钥解密
byte[] keyBytes = decryptHex(key);
// 取得私钥
PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
Key privateKey = keyFactory.generatePrivate(pkcs8KeySpec);
// 对数据加密
Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
cipher.init(Cipher.ENCRYPT_MODE, privateKey);
return cipher.doFinal(data);
}
/**
* 用私钥解密
*
* @param data
* @param key
* @return
* @throws Exception
*/
public static byte[] decryptByPrivateKey(byte[] data, String key) throws Exception {
byte[] keyBytes = decryptHex(key);
PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
Key privateKey = keyFactory.generatePrivate(pkcs8KeySpec);
Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
cipher.init(Cipher.DECRYPT_MODE, privateKey);
return cipher.doFinal(data);
}
/**
* 用公钥加密
*
* @param data
* @param key
* @return
* @throws Exception
*/
public static byte[] encryptByPublicKey(byte[] data, String key) throws Exception {
byte[] keyBytes = decryptHex(key);
// 取得公钥
X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
Key publicKey = keyFactory.generatePublic(x509KeySpec);
// 对数据加密
Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
return cipher.doFinal(data);
}
/**
* 用公钥解密
*
* @param data
* @param key
* @return
* @throws Exception
*/
public static byte[] decryptByPublicKey(byte[] data, String key) throws Exception {
byte[] keyBytes = decryptHex(key);
// 取得公钥
X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
Key publicKey = keyFactory.generatePublic(x509KeySpec);
// 对数据解密
Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
cipher.init(Cipher.DECRYPT_MODE, publicKey);
return cipher.doFinal(data);
}
/**
* 用私钥来签名
*
* @param data
* @param privateKey
* @return
* @throws Exception
*/
public static String sign(byte[] data, String privateKey) throws Exception {
byte[] keyBytes = decryptHex(privateKey);
PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
PrivateKey priKey = keyFactory.generatePrivate(pkcs8KeySpec);
Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
signature.initSign(priKey);
signature.update(data);
return encryptHex(signature.sign());
}
/**
* 用公钥来验签
*
* @param data
* @param publicKey
* @param sign
* @return
* @throws Exception
*/
public static boolean verify(byte[] data, String publicKey, String sign) throws Exception {
byte[] keyBytes = decryptHex(publicKey);
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
PublicKey pubKey = keyFactory.generatePublic(keySpec);
Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
signature.initVerify(pubKey);
signature.update(data);
return signature.verify(decryptHex(sign));
}
}

测试以上方法如下:

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
/**
* 私钥签名,公钥验签
*/
public void testSignVerify() throws Exception {
Map<String, Object> key = RSACoder.initKey();
String publicKey = RSACoder.getPublicKey(key);
String privateKey = RSACoder.getPrivateKey(key);
String data = "testData";
String sign = RSACoder.sign(data.getBytes(), privateKey);
RSACoder.verify(data.getBytes(), publicKey, sign);
assertTrue(RSACoder.verify(data.getBytes(), publicKey, sign));
}
/**
* 私钥加密,公钥解密
*
* @throws Exception
*/
public void testPrivateEncryt() throws Exception {
Map<String, Object> key = RSACoder.initKey();
String publicKey = RSACoder.getPublicKey(key);
String privateKey = RSACoder.getPrivateKey(key);
String data = "testDataPrivate";
byte[] encryDataByte = RSACoder.encryptByPrivateKey(data.getBytes(), privateKey);
byte[] decryDataByte = RSACoder.decryptByPublicKey(encryDataByte, publicKey);
String decryData = new String(decryDataByte);
System.out.println(decryData);
assertTrue(decryData.equals(data));
}
/**
* 公钥加密,私钥解密
*/
public void testPublicEncryt() throws Exception {
Map<String, Object> key = RSACoder.initKey();
String publicKey = RSACoder.getPublicKey(key);
String privateKey = RSACoder.getPrivateKey(key);
String data = "testDataPublic";
byte[] encryDataByte = RSACoder.encryptByPublicKey(data.getBytes(), publicKey);
byte[] decryDataByte = RSACoder.decryptByPrivateKey(encryDataByte, privateKey);
String decryData = new String(decryDataByte);
System.out.println(decryData);
assertTrue(decryData.equals(data));
}

一般使用私钥签名,将签名内容一同发送给接收者验签,这样保证了发送者的身份有效性(服务器A给服务器B发送内容,使用服务器A的公钥私钥)。
如果公钥不对外暴露,服务器之间采用相互信任的机制,各自保留对方的公钥,那么可采取公钥加密私钥解密的方式(服务器A给服务器B发送内容,使用服务器B的公钥私钥)。
注:加解密参数及结果经常为字节数组,此时涉及字节数组与字符串的比较,那么就要转换类型。
String a转byte[] b为方法为a.getBytes(),getBytes()参数为编码类型,无参则使用环境编码(Charset.defaultCharset()查看当前系统编码),从jdk源码看,若编码不支持,则使用ISO-8859-1编码,建议制定兼容的UTF-8编码。
byte[] b转String a的方法为new String(b),同样可指定编码,切勿使用b.toString(),字节数组未覆盖Object的toString方法,toString()获得的是内存地址(也非实际物理地址,只是约定为getClass().getName() + “@” + Integer.toHexString(hashCode()))。

拓展

推荐一个Java工具类,功能强大,其中封装了各种加解密方法。简单测试其AES算法为

1
2
3
4
5
6
7
8
9
public void testAESHex() throws UnsupportedEncodingException {
String content = "justForTest";
byte[] key = SecureUtil.generateKey(SymmetricAlgorithm.AES.getValue()).getEncoded();
System.out.println("key length: " + key.length);
SymmetricCrypto aes = new SymmetricCrypto(SymmetricAlgorithm.AES, key);
String encryptHex = aes.encryptHex(content);
String decrypt = aes.decryptStr(encryptHex, CharsetUtil.CHARSET_UTF_8);
assertTrue(decrypt.equals(content));
}

aes加解密encryptHex对应decryptStr,encrypt对应decrypt,以上key为工具类生成的,若自己指定key,则key必须为16、24、32位等,如下ct为毫秒级的Unix时间戳,只有13位,为凑足16位,随机加上3位,如”123”:

1
2
3
4
5
6
7
8
9
10
public void testAES1() throws UnsupportedEncodingException {
String content = "justForTest";
long ct = System.currentTimeMillis(); //13位
String key = String.valueOf(ct) + "123";
SymmetricCrypto aes = new SymmetricCrypto(SymmetricAlgorithm.AES, key.getBytes("ISO-8859-1"));
byte[] encrypt = aes.encrypt(content);
byte[] decrypt = aes.decrypt(encrypt);
String result = new String(decrypt, "ISO-8859-1");
assertTrue(result.equals(content));
}