RSA加密解密(Java)

RSA加密解密

一、RSA的原理

1.1、RSA的介绍

RSA加密算法是一种可逆的非对称加密算法,即RSA加密时候用的密钥(公钥)和RSA解密时用的密钥(私钥)不是同一把。基本原理是将两个很大的质数相乘很容易得到乘积,但是该乘积分解质因数却很困难。RSA算法被广泛的用于加密解密和RSA签名/验证等领域。

1.2、RSA算法的速度与安全性

​ 比起AES等其它对称算法来说,RSA运算更为复杂,所以要慢得多。

​ 从安全角度来讲,一般建议RSA密钥长度至少为2048位。世界上还没有任何可靠的攻击RSA算法的方式,如果密钥足够长或者没有密钥,想要RSA解密或者破解RSA解密基本是不可能的。RSA从提出到现在已近二十年,经历了各种攻击的考验,逐渐为人们接受,普遍认为是目前最优秀的公钥方案之一。

1.3、RSA存储格式

​ DER是RSA密钥的二进制格式,PEM是DER转码为Base64的字符格式,由于DER是二进制格式,不便于阅读和理解。一般而言,密钥都是通过PEM的格式进行存储的,本工具所选择的RSA密钥格式也就是PEM编码存储的格式。

二、项目中使用

​ 因为网站的代码和js对用户是可见的,如果采用对称加密的话那么用户可以通过看js就知道怎么解密数据了。所以为了保证传输重要数据的安全性,需要非对称加密。js给后台提交的数据用服务端给js提供的公钥加密数据。提交到后台用自己私钥解密数据。服务器给js端返回的数据用js给服务器提供的公钥加密,js用自己持有的私钥解密。这样用户在网页端只能看到一个秘钥。就能保证传输的安全性。

1、Java后端使用
1.1、RSA加密工具类–RSAUtils.Class
import org.apache.commons.codec.binary.Base64;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.HashMap;
import java.util.Map;

/**
 * @Author: AhNorth
 * @Desc: RSA工具类
 * @DateTime: 2023/6/15 14:51
 */
public class RSAUtils {
    //随机生成密钥对
    public static Map<String, String> genKeyPair() {
        // KeyPairGenerator类用于生成公钥和私钥对,基于RSA算法生成对象
        KeyPairGenerator keyPairGen = null;

        try {
            keyPairGen = KeyPairGenerator.getInstance("RSA");
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }

        // 初始化密钥对生成器,密钥大小为96-1024位
        assert keyPairGen != null;
        keyPairGen.initialize(1024, new SecureRandom());
        // 生成一个密钥对,保存在keyPair中
        KeyPair keyPair = keyPairGen.generateKeyPair();
        RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();   // 得到私钥
        RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();  // 得到公钥
        String publicKeyString = new String(Base64.encodeBase64(publicKey.getEncoded()));
        // 得到私钥字符串
        String privateKeyString = new String(Base64.encodeBase64((privateKey.getEncoded())));
        // 将公钥和私钥保存到Map
        Map<String, String> keyMap = new HashMap<>();
        keyMap.put("publicKey", publicKeyString);	//公钥
        keyMap.put("privateKey", privateKeyString);	//私钥
        return keyMap;
    }

    /** RSA公钥加密
     * @param str  加密字符串
     * @param publicKey  公钥
     * @return  密文
     */
    public static String encrypt(String str, String publicKey) {
        //base64编码的公钥
        byte[] decoded = Base64.decodeBase64(publicKey);
        RSAPublicKey pubKey = null;
        String outStr = null;

        try {
            pubKey = (RSAPublicKey) KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(decoded));
            Cipher cipher = Cipher.getInstance("RSA");
            cipher.init(Cipher.ENCRYPT_MODE, pubKey);
            outStr = Base64.encodeBase64String(cipher.doFinal(str.getBytes(StandardCharsets.UTF_8)));
        } catch (InvalidKeySpecException | BadPaddingException | IllegalBlockSizeException | InvalidKeyException | NoSuchPaddingException | NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        //RSA加密
        return outStr;
    }

    /**  RSA私钥解密
     * @param str   加密字符串
     * @param privateKey  私钥
     * @return  铭文
     */
    public static String decrypt(String str, String privateKey) {
        //64位解码加密后的字符串
        byte[] inputByte = Base64.decodeBase64(str.getBytes(StandardCharsets.UTF_8));
        //base64编码的私钥
        byte[] decoded = Base64.decodeBase64(privateKey);
        RSAPrivateKey priKey = null;
        //RSA解密
        Cipher cipher = null;
        String outStr = null;

        try {
            priKey = (RSAPrivateKey) KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(decoded));
            cipher = Cipher.getInstance("RSA");
            cipher.init(Cipher.DECRYPT_MODE, priKey);
            outStr = new String(cipher.doFinal(inputByte));
        } catch (InvalidKeySpecException | NoSuchAlgorithmException | NoSuchPaddingException | BadPaddingException | IllegalBlockSizeException | InvalidKeyException e) {
            e.printStackTrace();
        }
        return outStr;
    }
}
2.2、初始化秘钥

​ 编写获取秘钥接口,前端随机生成uuid并传递给后端,后端初始化密钥对,公钥传递给前端用于加密,私钥保存到redis或本地缓存中方便解密。

/**
 * Desc: 获取公钥
 * @param uuid
 * @return: java.lang.String
 */
@GetMapping("/key")
public Result<String> publicKey(@RequestParam String uuid){
    try {
        //初始化公钥私钥对
        Map<String, String> keyMap = RSAUtils.genKeyPair();
        String publicKey = keyMap.get("publicKey");
        String privateKey = keyMap.get("privateKey");
        //保存私钥到缓存
        if(open){
            String key = RedisKeys.getPrivateKey(uuid);
            redisUtils.set(key, privateKey, 300);
        }else {
            localCache.put("privateKey" + uuid, privateKey);
        }
        return publicKey;
    } catch (Exception e) {
        e.printStackTrace();
    }
    return null;
}

2.3、解密

​ 根据获取公钥的uuid从缓存中获取对应的私钥后使用工具类中的方法解密即可。

/**
 * Desc: 解密
 * @param uuid
 * @param data	加密后的数据
 * @return: java.lang.String
 */
public static String getDecryptStr(uuid, data){
    String privateKey = null;
    if(open){
        uuid = RedisKeys.getPrivateKey(uuid);
        privateKey = (String)redisUtils.get(uuid);
        //删除Redis中的私钥
        if(privateKey != null){
            redisUtils.delete(uuid);
        }
    }else {
        privateKey = localCache.getIfPresent("privateKey" + uuid);
        //删除缓存中的私钥
        if(privateKey != null){
            localCache.invalidate("privateKey" + uuid);
        }
    }
 	//解密
    String str = null;
    try {
        str = RSAUtils.decrypt(data, privateKey);
    } catch (Exception e) {
        System.out.println("解密错误");
        e.printStackTrace();
    }
    return str;
}
        
2、Vue前端使用

​ jsencrypt和encryptlong都是rsa加密,加密的对象一定要是字符串。 简单数据用前者,如果加密的是对象并且数据还挺多的,比如含有token 用后者。

2.1、引入依赖
npm install jsencrypt --save 
npm install encryptlong --save 
2.2、RSA加密工具类–rsa.js
/* 产引入jsencrypt实现数据RSA加密 */
import JSEncrypt from 'jsencrypt' // 处理长文本数据时报错 jsencrypt.js Message too long for RSA

/* 产引入encryptlong实现数据RSA加密 */
import Encrypt from 'encryptlong' // encryptlong是基于jsencrypt扩展的长文本分段加解密功能。

/**
 * JSEncrypt加密
 * @param data  数据
 * @param publicKey 公钥
 * @returns {string}
 */
export function rsaPublicData(data, publicKey) {
    const jsencrypt = new JSEncrypt()
    jsencrypt.setPublicKey(publicKey)
    // 如果是对象/数组的话,需要先JSON.stringify转换成字符串
    return jsencrypt.encrypt(data)
}

/**
 * JSEncrypt解密
 * @param data  数据
 * @param privateKey    私钥
 * @returns {string}
 */
export function rsaPrivateData(data, privateKey) {
    const jsencrypt = new JSEncrypt()
    jsencrypt.setPrivateKey(privateKey)
    // 如果是对象/数组的话,需要先JSON.stringify转换成字符串
    return jsencrypt.encrypt(data)
}

/**
 * encryptlong加密
 * @param data  数据
 * @param publicKey 公钥
 * @returns {string}
 */
export function encrypt(data, publicKey) {
    const encryptor = new Encrypt()
    encryptor.setPublicKey(publicKey)
    // 如果是对象/数组的话,需要先JSON.stringify转换成字符串
    return encryptor.encryptLong(data)
}

/**
 * encryptlong解密
 * @param data  数据
 * @param privateKey    私钥
 * @returns {string}
 */
export function decrypt(data, privateKey) {
    const encryptor = new Encrypt()
    encryptor.setPrivateKey(privateKey)
    // 如果是对象/数组的话,需要先JSON.stringify转换成字符串
    return encryptor.decryptLong(data)
}
2.3、生成uuid

​ 前端生成uuid传给后端,在初始化密钥对的时候使用该uuid作为key将私钥保存到缓存中,登录时也需要上传该uuid使后端可以从缓存中获取到对应的秘钥。

/**
 * 获取uuid
 * @returns {string}
 */
export function getUUID () {
    return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => {
        return (c === 'x' ? (Math.random() * 16 | 0) : ('r&0x3' | '0x8')).toString(16)
    })
}
2.4、加密

​ 获取到公钥后根据公钥加密后传输给后端。

const password = encrypt(this.password.toString(), publicKey)

感谢大家的观看。

  • 8
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值