阿影
JS客户端RSA加密,Java服务端解密
阿影 发布于 2011年06月17日 16时, 15评/7967阅
分享到 新浪微博腾讯微博收藏+41踩顶0
在客户端浏览器,Javascript使用RSA算法,以公钥对密码进行加密,服务端使用相应的私钥进行解密。一般用于注册时或登录时填写的密码。
Java引用到的包:
commons-lang
bouncycastle
slf4j
commons-codec
commons-io
标签:
Bouncy Castle RSA JavaScript Java Bouncy Castle
代码片段(3)[全屏查看所有代码]
1. [文件] security.js ~ 19KB 下载(1449)
2. [文件] RSAUtils.java ~ 15KB 下载(943)
001
package my.tools.security;
002
003
import java.io.File;
004
import java.io.FileInputStream;
005
import java.io.FileOutputStream;
006
import java.io.ObjectInputStream;
007
import java.io.ObjectOutputStream;
008
import java.math.BigInteger;
009
import java.security.KeyPair;
010
import java.security.KeyFactory;
011
import java.security.KeyPairGenerator;
012
import java.security.Provider;
013
import java.security.PublicKey;
014
import java.security.PrivateKey;
015
import java.security.SecureRandom;
016
import java.security.NoSuchAlgorithmException;
017
import java.security.InvalidParameterException;
018
import java.security.interfaces.RSAPublicKey;
019
import java.security.interfaces.RSAPrivateKey;
020
import java.security.spec.RSAPublicKeySpec;
021
import java.security.spec.RSAPrivateKeySpec;
022
import java.security.spec.InvalidKeySpecException;
023
024
import javax.crypto.Cipher;
025
026
import org.apache.commons.io.IOUtils;
027
import org.apache.commons.io.FileUtils;
028
import org.apache.commons.codec.DecoderException;
029
import org.apache.commons.codec.binary.Hex;
030
import org.bouncycastle.jce.provider.BouncyCastleProvider;
031
import org.slf4j.Logger;
032
import org.slf4j.LoggerFactory;
033
034
import org.apache.commons.lang.StringUtils;
035
import org.apache.commons.lang.time.DateFormatUtils;
036
037
/**
038
* RSA算法加密/解密工具类。
039
*
040
* @author fuchun
041
* @version 1.0.0, 2010-05-05
042
*/
043
public abstract class RSAUtils {
044
045
private static final Logger LOGGER = LoggerFactory.getLogger(RSAUtils.class);
046
047
/** 算法名称 */
048
private static final String ALGORITHOM = "RSA";
049
/**保存生成的密钥对的文件名称。 */
050
private static final String RSA_PAIR_FILENAME = "/__RSA_PAIR.txt";
051
/** 密钥大小 */
052
private static final int KEY_SIZE = 1024;
053
/** 默认的安全服务提供者 */
054
private static final Provider DEFAULT_PROVIDER = new BouncyCastleProvider();
055
056
private static KeyPairGenerator keyPairGen = null;
057
private static KeyFactory keyFactory = null;
058
/** 缓存的密钥对。 */
059
private static KeyPair oneKeyPair = null;
060
061
private static File rsaPairFile = null;
062
063
static {
064
try {
065
keyPairGen = KeyPairGenerator.getInstance(ALGORITHOM, DEFAULT_PROVIDER);
066
keyFactory = KeyFactory.getInstance(ALGORITHOM, DEFAULT_PROVIDER);
067
} catch (NoSuchAlgorithmException ex) {
068
LOGGER.error(ex.getMessage());
069
}
070
rsaPairFile = new File(getRSAPairFilePath());
071
}
072
073
private RSAUtils() {
074
}
075
076
/**
077
* 生成并返回RSA密钥对。
078
*/
079
private static synchronized KeyPair generateKeyPair() {
080
try {
081
keyPairGen.initialize(KEY_SIZE, newSecureRandom(DateFormatUtils.format("yyyyMMdd").getBytes()));
082
oneKeyPair = keyPairGen.generateKeyPair();
083
saveKeyPair(oneKeyPair);
084
return oneKeyPair;
085
} catch (InvalidParameterException ex) {
086
LOGGER.error("KeyPairGenerator does not support a key length of " + KEY_SIZE + ".", ex);
087
} catch (NullPointerException ex) {
088
LOGGER.error("RSAUtils#KEY_PAIR_GEN is null, can not generate KeyPairGenerator instance.",
089
ex);
090
}
091
return null;
092
}
093
094
/**
095
* 返回生成/读取的密钥对文件的路径。
096
*/
097
private static String getRSAPairFilePath() {
098
String urlPath = RSAUtils.class.getResource("/").getPath();
099
return (new File(urlPath).getParent() + RSA_PAIR_FILENAME);
100
}
101
102
/**
103
* 若需要创建新的密钥对文件,则返回 {@code true},否则 {@code false}。
104
*/
105
private static boolean isCreateKeyPairFile() {
106
// 是否创建新的密钥对文件
107
boolean createNewKeyPair = false;
108
if (!rsaPairFile.exists() || rsaPairFile.isDirectory()) {
109
createNewKeyPair = true;
110
}
111
return createNewKeyPair;
112
}
113
114
/**
115
* 将指定的RSA密钥对以文件形式保存。
116
*
117
* @param keyPair 要保存的密钥对。
118
*/
119
private static void saveKeyPair(KeyPair keyPair) {
120
FileOutputStream fos = null;
121
ObjectOutputStream oos = null;
122
try {
123
fos = FileUtils.openOutputStream(rsaPairFile);
124
oos = new ObjectOutputStream(fos);
125
oos.writeObject(keyPair);
126
} catch (Exception ex) {
127
ex.printStackTrace();
128
} finally {
129
IOUtils.closeQuietly(oos);
130
IOUtils.closeQuietly(fos);
131
}
132
}
133
134
/**
135
* 返回RSA密钥对。
136
*/
137
public static KeyPair getKeyPair() {
138
// 首先判断是否需要重新生成新的密钥对文件
139
if (isCreateKeyPairFile()) {
140
// 直接强制生成密钥对文件,并存入缓存。
141
return generateKeyPair();
142
}
143
if (oneKeyPair != null) {
144
return oneKeyPair;
145
}
146
return readKeyPair();
147
}
148
149
// 同步读出保存的密钥对
150
private static KeyPair readKeyPair() {
151
FileInputStream fis = null;
152
ObjectInputStream ois = null;
153
try {
154
fis = FileUtils.openInputStream(rsaPairFile);
155
ois = new ObjectInputStream(fis);
156
oneKeyPair = (KeyPair) ois.readObject();
157
return oneKeyPair;
158
} catch (Exception ex) {
159
ex.printStackTrace();
160
} finally {
161
IOUtils.closeQuietly(ois);
162
IOUtils.closeQuietly(fis);
163
}
164
return null;
165
}
166
167
/**
168
* 根据给定的系数和专用指数构造一个RSA专用的公钥对象。
169
*
170
* @param modulus 系数。
171
* @param publicExponent 专用指数。
172
* @return RSA专用公钥对象。
173
*/
174
public static RSAPublicKey generateRSAPublicKey(byte[] modulus, byte[] publicExponent) {
175
RSAPublicKeySpec publicKeySpec = new RSAPublicKeySpec(new BigInteger(modulus),
176
new BigInteger(publicExponent));
177
try {
178
return (RSAPublicKey) keyFactory.generatePublic(publicKeySpec);
179
} catch (InvalidKeySpecException ex) {
180
LOGGER.error("RSAPublicKeySpec is unavailable.", ex);
181
} catch (NullPointerException ex) {
182
LOGGER.error("RSAUtils#KEY_FACTORY is null, can not generate KeyFactory instance.", ex);
183
}
184
return null;
185
}
186
187
/**
188
* 根据给定的系数和专用指数构造一个RSA专用的私钥对象。
189
*
190
* @param modulus 系数。
191
* @param privateExponent 专用指数。
192
* @return RSA专用私钥对象。
193
*/
194
public static RSAPrivateKey generateRSAPrivateKey(byte[] modulus, byte[] privateExponent) {
195
RSAPrivateKeySpec privateKeySpec = new RSAPrivateKeySpec(newBigInteger(modulus),
196
new BigInteger(privateExponent));
197
try {
198
return (RSAPrivateKey) keyFactory.generatePrivate(privateKeySpec);
199
} catch (InvalidKeySpecException ex) {
200
LOGGER.error("RSAPrivateKeySpec is unavailable.", ex);
201
} catch (NullPointerException ex) {
202
LOGGER.error("RSAUtils#KEY_FACTORY is null, can not generate KeyFactory instance.", ex);
203
}
204
return null;
205
}
206
207
/**
208
* 根据给定的16进制系数和专用指数字符串构造一个RSA专用的私钥对象。
209
*
210
* @param modulus 系数。
211
* @param privateExponent 专用指数。
212
* @return RSA专用私钥对象。
213
*/
214
public static RSAPrivateKey getRSAPrivateKey(String hexModulus, String hexPrivateExponent) {
215
if(StringUtils.isBlank(hexModulus) || StringUtils.isBlank(hexPrivateExponent)) {
216
if(LOGGER.isDebugEnabled()) {
217
LOGGER.debug("hexModulus and hexPrivateExponent cannot be empty. RSAPrivateKey value is null to return.");
218
}
219
return null;
220
}
221
byte[] modulus = null;
222
byte[] privateExponent = null;
223
try {
224
modulus = Hex.decodeHex(hexModulus.toCharArray());
225
privateExponent = Hex.decodeHex(hexPrivateExponent.toCharArray());
226
} catch(DecoderException ex) {
227
LOGGER.error("hexModulus or hexPrivateExponent value is invalid. return null(RSAPrivateKey).");
228
}
229
if(modulus != null && privateExponent != null) {
230
return generateRSAPrivateKey(modulus, privateExponent);
231
}
232
return null;
233
}
234
235
/**
236
* 根据给定的16进制系数和专用指数字符串构造一个RSA专用的公钥对象。
237
*
238
* @param modulus 系数。
239
* @param publicExponent 专用指数。
240
* @return RSA专用公钥对象。
241
*/
242
public static RSAPublicKey getRSAPublidKey(String hexModulus, String hexPublicExponent) {
243
if(StringUtils.isBlank(hexModulus) || StringUtils.isBlank(hexPublicExponent)) {
244
if(LOGGER.isDebugEnabled()) {
245
LOGGER.debug("hexModulus and hexPublicExponent cannot be empty. return null(RSAPublicKey).");
246
}
247
return null;
248
}
249
byte[] modulus = null;
250
byte[] publicExponent = null;
251
try {
252
modulus = Hex.decodeHex(hexModulus.toCharArray());
253
publicExponent = Hex.decodeHex(hexPublicExponent.toCharArray());
254
} catch(DecoderException ex) {
255
LOGGER.error("hexModulus or hexPublicExponent value is invalid. return null(RSAPublicKey).");
256
}
257
if(modulus != null && publicExponent != null) {
258
return generateRSAPublicKey(modulus, publicExponent);
259
}
260
return null;
261
}
262
263
/**
264
* 使用指定的公钥加密数据。
265
*
266
* @param publicKey 给定的公钥。
267
* @param data 要加密的数据。
268
* @return 加密后的数据。
269
*/
270
public static byte[] encrypt(PublicKey publicKey, byte[] data) throws Exception {
271
Cipher ci = Cipher.getInstance(ALGORITHOM, DEFAULT_PROVIDER);
272
ci.init(Cipher.ENCRYPT_MODE, publicKey);
273
return ci.doFinal(data);
274
}
275
276
/**
277
* 使用指定的私钥解密数据。
278
*
279
* @param privateKey 给定的私钥。
280
* @param data 要解密的数据。
281
* @return 原数据。
282
*/
283
public static byte[] decrypt(PrivateKey privateKey, byte[] data) throws Exception {
284
Cipher ci = Cipher.getInstance(ALGORITHOM, DEFAULT_PROVIDER);
285
ci.init(Cipher.DECRYPT_MODE, privateKey);
286
return ci.doFinal(data);
287
}
288
289
/**
290
* 使用给定的公钥加密给定的字符串。
291
* <p />
292
* 若 {@code publicKey} 为 {@code null},或者 {@code plaintext} 为 {@code null} 则返回 {@code
293
* null}。
294
*
295
* @param publicKey 给定的公钥。
296
* @param plaintext 字符串。
297
* @return 给定字符串的密文。
298
*/
299
public static String encryptString(PublicKey publicKey, String plaintext) {
300
if (publicKey == null || plaintext == null) {
301
return null;
302
}
303
byte[] data = plaintext.getBytes();
304
try {
305
byte[] en_data = encrypt(publicKey, data);
306
return new String(Hex.encodeHex(en_data));
307
} catch (Exception ex) {
308
LOGGER.error(ex.getCause().getMessage());
309
}
310
return null;
311
}
312
313
/**
314
* 使用默认的公钥加密给定的字符串。
315
* <p />
316
* 若{@code plaintext} 为 {@code null} 则返回 {@code null}。
317
*
318
* @param plaintext 字符串。
319
* @return 给定字符串的密文。
320
*/
321
public static String encryptString(String plaintext) {
322
if(plaintext == null) {
323
return null;
324
}
325
byte[] data = plaintext.getBytes();
326
KeyPair keyPair = getKeyPair();
327
try {
328
byte[] en_data = encrypt((RSAPublicKey)keyPair.getPublic(), data);
329
return new String(Hex.encodeHex(en_data));
330
} catch(NullPointerException ex) {
331
LOGGER.error("keyPair cannot be null.");
332
} catch(Exception ex) {
333
LOGGER.error(ex.getCause().getMessage());
334
}
335
return null;
336
}
337
338
/**
339
* 使用给定的私钥解密给定的字符串。
340
* <p />
341
* 若私钥为 {@code null},或者 {@code encrypttext} 为 {@code null}或空字符串则返回 {@code null}。
342
* 私钥不匹配时,返回 {@code null}。
343
*
344
* @param privateKey 给定的私钥。
345
* @param encrypttext 密文。
346
* @return 原文字符串。
347
*/
348
public static String decryptString(PrivateKey privateKey, String encrypttext) {
349
if (privateKey == null || StringUtils.isBlank(encrypttext)) {
350
return null;
351
}
352
try {
353
byte[] en_data = Hex.decodeHex(encrypttext.toCharArray());
354
byte[] data = decrypt(privateKey, en_data);
355
return new String(data);
356
} catch (Exception ex) {
357
LOGGER.error(String.format("\"%s\" Decryption failed. Cause: %s", encrypttext, ex.getCause().getMessage()));
358
}
359
return null;
360
}
361
362
/**
363
* 使用默认的私钥解密给定的字符串。
364
* <p />
365
* 若{@code encrypttext} 为 {@code null}或空字符串则返回 {@code null}。
366
* 私钥不匹配时,返回 {@code null}。
367
*
368
* @param encrypttext 密文。
369
* @return 原文字符串。
370
*/
371
public static String decryptString(String encrypttext) {
372
if(StringUtils.isBlank(encrypttext)) {
373
return null;
374
}
375
KeyPair keyPair = getKeyPair();
376
try {
377
byte[] en_data = Hex.decodeHex(encrypttext.toCharArray());
378
byte[] data = decrypt((RSAPrivateKey)keyPair.getPrivate(), en_data);
379
return new String(data);
380
} catch(NullPointerException ex) {
381
LOGGER.error("keyPair cannot be null.");
382
} catch (Exception ex) {
383
LOGGER.error(String.format("\"%s\" Decryption failed. Cause: %s", encrypttext, ex.getMessage()));
384
}
385
return null;
386
}
387
388
/**
389
* 使用默认的私钥解密由JS加密(使用此类提供的公钥加密)的字符串。
390
*
391
* @param encrypttext 密文。
392
* @return {@code encrypttext} 的原文字符串。
393
*/
394
public static String decryptStringByJs(String encrypttext) {
395
String text = decryptString(encrypttext);
396
if(text == null) {
397
return null;
398
}
399
return StringUtils.reverse(text);
400
}
401
402
/** 返回已初始化的默认的公钥。*/
403
public static RSAPublicKey getDefaultPublicKey() {
404
KeyPair keyPair = getKeyPair();
405
if(keyPair != null) {
406
return (RSAPublicKey)keyPair.getPublic();
407
}
408
return null;
409
}
410
411
/** 返回已初始化的默认的私钥。*/
412
public static RSAPrivateKey getDefaultPrivateKey() {
413
KeyPair keyPair = getKeyPair();
414
if(keyPair != null) {
415
return (RSAPrivateKey)keyPair.getPrivate();
416
}
417
return null;
418
}
419
}
3. [代码][Java]代码
01
// Struts2 Action方法中:
02
// 将公钥的 modulus 和 exponent 传给页面。
03
// Hex -> apache commons-codec
04
RSAPublicKey publicKey = RSAUtils.getDefaultPublicKey();
05
ActionContext.getContext().put("modulus", newString(Hex.encodeHex(publicKey.getModulus().toByteArray())));
06
ActionContext.getContext().put("exponent", newString(Hex.encodeHex(publicKey.getPublicExponent().toByteArray())));
07
08
// 页面里,Javascript对明文进行加密:
09
var modulus = $('#hid_modulus').val(), exponent = $('#hid_exponent').val();
10
var key = RSAUtils.getKeyPair(exponent, '', modulus);
11
pwd1 = RSAUtils.encryptedString(key, pwd1);
12
pwd2 = RSAUtils.encryptedString(key, pwd2);
13
14
// 服务器端,使用RSAUtils工具类对密文进行解密
15
RSAUtils.decryptStringByJs(password1);