赞
踩
RSA是一种非对称加密算法。
非对称加密算法需要两个密钥:公开密钥(publickey:简称公钥)和私有密钥(privatekey:简称私钥)。公钥与私钥是一对,如果用公钥对数据进行加密,只有用对应的私钥才能解密。因为加密和解密使用的是两个不同的密钥,所以这种算法叫作非对称加密算法。 非对称加密算法实现机密信息交换的基本过程是:甲方生成一对密钥并将公钥公开,需要向甲方发送信息的其他角色(乙方)使用该密钥(甲方的公钥)对机密信息进行加密后再发送给甲方;甲方再用自己私钥对加密后的信息进行解密。甲方想要回复乙方时正好相反,使用乙方的公钥对数据进行加密,同理,乙方使用自己的私钥来进行解密。
在我们Web环境中,甲方即为我们的服务端,乙方即为客户端。服务端需要生成一个公钥和一个私钥,私钥保管在服务端不能泄露,公钥可以公开给所有客户端。
下面的所有实现代码均在:https://github.com/passerbyYSQ/forum
- /**
- * @author passerbyYSQ
- * @create 2022-09-25 16:42
- */
- public interface RSAService {
- KeyPair generateKeyPair();
-
- String getPublicKey();
-
- String getPrivateKey();
-
- String decryptByPrivateKey(String encryptedText);
-
- String encryptByPublicKey(String rawText);
- }
- /**
- * @author passerbyYSQ
- * @create 2022-09-25 15:36
- */
- @Service
- @Slf4j
- public class RSAServiceImpl implements RSAService, ApplicationListener<ContextRefreshedEvent> {
- @Resource
- private RedisService redisService;
-
- @Override
- public void onApplicationEvent(ContextRefreshedEvent event) {
- // 对于web应用会出现父子容器,这样就会触发两次
- if (event.getApplicationContext().getParent() == null) {
- redisService.saveRSAKeyPair();
- log.info("成功将RSA的KeyPair缓存至Redis");
- }
- }
-
- @Override
- public KeyPair generateKeyPair() {
- try {
- KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA");
- generator.initialize(1024, new SecureRandom());
- return generator.generateKeyPair();
- } catch (NoSuchAlgorithmException e) {
- e.printStackTrace();
- return null;
- }
- }
-
- public String getPublicKey() {
- KeyPair keyPair = redisService.getRSAKeyPair();
- byte[] publicBytes = keyPair.getPublic().getEncoded();
- byte[] base64Bytes = Base64.getEncoder().encode(publicBytes);
- return new String(base64Bytes);
- }
-
- public String getPrivateKey() {
- KeyPair keyPair = redisService.getRSAKeyPair();
- byte[] privateBytes = keyPair.getPrivate().getEncoded();
- byte[] base64Bytes = Base64.getEncoder().encode(privateBytes);
- return new String(base64Bytes);
- }
-
- public String decryptByPrivateKey(String encryptedText) {
- if (ObjectUtils.isEmpty(encryptedText)) {
- return null;
- }
- try {
- KeyPair keyPair = redisService.getRSAKeyPair();
- byte[] decodedBytes = Base64.getDecoder().decode(encryptedText.getBytes());
- Cipher cipher = Cipher.getInstance("RSA");
- cipher.init(Cipher.DECRYPT_MODE, keyPair.getPrivate());
- byte[] decryptedBytes = cipher.doFinal(decodedBytes);
- return new String(decryptedBytes);
- } catch (Exception e) {
- e.printStackTrace();
- return null;
- }
- }
-
- public String encryptByPublicKey(String rawText) {
- if (ObjectUtils.isEmpty(rawText)) {
- return null;
- }
- try {
- KeyPair keyPair = redisService.getRSAKeyPair();
- Cipher cipher = Cipher.getInstance("RSA");
- cipher.init(Cipher.ENCRYPT_MODE, keyPair.getPublic());
- byte[] encryptedBytes = cipher.doFinal(rawText.getBytes());
- byte[] base64Bytes = Base64.getEncoder().encode(encryptedBytes);
- return new String(base64Bytes);
- } catch (Exception e) {
- e.printStackTrace();
- return null;
- }
- }
- }
- /**
- * @author passerbyYSQ
- * @create 2021-06-02 13:18
- */
- public interface RedisService {
-
- void saveRSAKeyPair();
-
- KeyPair getRSAKeyPair();
- }
- /**
- * @author passerbyYSQ
- * @create 2021-06-02 13:21
- */
- @Slf4j
- @Service
- public class RedisServiceImpl implements RedisService {
- @Resource
- private RedisTemplate<String, Object> redisTemplate;
- @Resource
- private RSAService rsaService;
-
- @Override
- public void saveRSAKeyPair() {
- String key = String.format(Constant.REDIS_KEY_KEY_PAIR, "RSA");
- KeyPair keyPair = rsaService.generateKeyPair();
- if (!ObjectUtils.isEmpty(keyPair)) {
- redisTemplate.opsForValue().setIfAbsent(key, keyPair, Constant.DURATION_KEY_PAIR);
- }
- }
-
- @Override
- public KeyPair getRSAKeyPair() {
- String key = String.format(Constant.REDIS_KEY_KEY_PAIR, "RSA");
- KeyPair keyPair = (KeyPair) redisTemplate.opsForValue().get(key);
- if (ObjectUtils.isEmpty(keyPair)) {
- saveRSAKeyPair(); // KeyPair过期,则重新续费
- }
- // 重新获取KeyPair,因为如果并发续费,成功续上的有可能不是本机生成的KeyPair
- return (KeyPair) redisTemplate.opsForValue().get(key);
- }
- }
Controller中新增一个方法,调用RedisService中的RsaService的getPublicKey()方法获取公钥。此处略,在项目中我是直接通过模板引擎,将公钥植入到登录页面的。如下图,代码仅供参考,思路是一样的。
前端使用的加密类库是 jsencrypt(https://github.com/travist/jsencrypt),js文件下载:https://github.com/travist/jsencrypt/tree/master/bin
使用比较简单,参见上面截图
服务端调用 RsaService的decryptByPrivateKey()方法解密即可获得明文。下面代码截图,仅供参考,思路是一样的。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。