Hutool 实现非对称加密(RSA)

目录


对称加密中,我们只需要一个密钥,通信双方同时持有。而非对称加密需要4个密钥。通信双方各自准备一对公钥和私钥。其中公钥是公开的,由信息接受方提供给信息发送方。公钥用来对信息加密。私钥由信息接受方保留,用来解密。既然公钥是公开的,就不存在保密问题。也就是说非对称加密完全不存在密钥配送问题!

公钥只能用做数据加密。公钥加密的数据,只能用对应的私钥才能解密。

思路

生成RAS密钥

  • A 生成 A 的 私钥(private_key_A.pem)、公钥(public_key_A.pem)
  • B 生成 B 的 私钥(private_key_B.pem)、公钥(public_key_B.pem)
  • A 将公钥(public_key_A.pem) 交给 B
  • B 将公钥(public_key_B.pem) 交给 A

消息公钥加密、私钥解密

A 发消息给 B

  • A 用 B 的 公钥(public_key_B.pem),将消息加密,发给 B
  • B 收到消息后,用 私钥(private_key_B.pem),将消息进行解密

B 发消息给 A

  • B 用 A 的 公钥(public_key_A.pem),将消息加密,发给 A
  • A 收到消息后,用 A的私钥(private_key_A.pem),将消息进行解密

代码Demo

java 复制代码
import cn.hutool.core.codec.Base64;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.asymmetric.AsymmetricAlgorithm;
import cn.hutool.crypto.asymmetric.KeyType;
import cn.hutool.crypto.asymmetric.RSA;
import com.alibaba.fastjson.JSON;
import com.thoth.his.base.util.FileUtil;
import org.junit.jupiter.api.Test;

import java.nio.charset.StandardCharsets;
import java.security.KeyPair;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.HashMap;
import java.util.Map;

生成 A 的密钥

java 复制代码
/**
 * 生成 A 的公钥、私钥
 */
@Test
public void generateKeyA() {
    KeyPair pair = SecureUtil.generateKeyPair(AsymmetricAlgorithm.RSA.getValue());
    PrivateKey privateKey = pair.getPrivate();
    PublicKey publicKey = pair.getPublic();
    //获得私钥
    String privateKeyStr = Base64.encode(privateKey.getEncoded());
    System.out.println("A私钥:" + privateKeyStr);
    FileUtil.writeString(privateKeyStr, "D:\\RAS\\private_key_A.pem");
    //获得公钥 -- 发给对方
    String publicKeyStr = Base64.encode(publicKey.getEncoded());
    System.out.println("A公钥:" + publicKeyStr);
    FileUtil.writeString(publicKeyStr, "D:\\RAS\\public_key_A.pem");
}

生成 B 的密钥

java 复制代码
/**
 * 生成 B 的公钥、私钥
 */
@Test
public void generateKeyB() {
    KeyPair pair = SecureUtil.generateKeyPair(AsymmetricAlgorithm.RSA.getValue());
    PrivateKey privateKey = pair.getPrivate();
    PublicKey publicKey = pair.getPublic();
    //获得私钥
    String privateKeyStr = Base64.encode(privateKey.getEncoded());
    System.out.println("B私钥:" + privateKeyStr);
    FileUtil.writeString(privateKeyStr, "D:\\RAS\\private_key_B.pem");
    //获得公钥 -- 发给对方
    String publicKeyStr = Base64.encode(publicKey.getEncoded());
    System.out.println("B公钥:" + publicKeyStr);
    FileUtil.writeString(publicKeyStr, "D:\\RAS\\public_key_B.pem");
}

A 发送消息给 B

java 复制代码
/**
 * A 发消息给B ,用 B 的公钥进行加密
 */
@Test
public void sendMsg() {
    String privateKeyStr = FileUtil.readUtf8String("D:\\RAS\\private_key_A.pem");
    String publicKeyStr = FileUtil.readUtf8String("D:\\RAS\\public_key_B.pem");
    RSA rsa = new RSA(privateKeyStr, publicKeyStr);
    System.out.println(rsa);

    Map<String, String> map = new HashMap<>();
    map.put("Name", "张三");
    map.put("Age", "30");
    String json = JSON.toJSONString(map);
    //公钥加密,私钥解密
    byte[] encrypt = rsa.encrypt(StrUtil.bytes(json, CharsetUtil.CHARSET_UTF_8), KeyType.PublicKey);
    String msg = Base64.encode(encrypt);
    //将消息存文件,模拟HTTP传送,供B去解密
    FileUtil.writeString(msg, "D:\\RAS\\msg_A.txt");
    System.out.println("A 公钥加密后的内容:" + msg);
}

B 解密 A 消息

java 复制代码
/**
 * B 收到 A 的消息,用 B 的私钥进行解密
 */
@Test
public void receiveMsg() {
    String privateKeyStr = FileUtil.readUtf8String("D:\\RAS\\private_key_B.pem");
    String publicKeyStr = FileUtil.readUtf8String("D:\\RAS\\public_key_A.pem");
    //RSA rsa = new RSA(privateKeyStr, publicKeyStr);
    RSA rsa = new RSA(privateKeyStr, null); //单纯解密的话,可以不需要 A 的公钥
    String msgAStr = FileUtil.readUtf8String("D:\\RAS\\msg_A.txt");
    byte[] decrypt = rsa.decrypt(msgAStr, KeyType.PrivateKey);
    //把解密的结果转换成String字符串输出
    System.out.println("私钥解密:" + StrUtil.str(decrypt, StandardCharsets.UTF_8));
}

参考:https://zhuanlan.zhihu.com/p/436455172