非对称加密算法系列文章,推荐阅读顺序:
一、RSA 部分
1.1 简介
RSA是3个发明者的名字缩写, 是目前最有影响力的公钥加密算法,该算法基于一个十分简单的数论事实:将两个大素数相乘十分容易,但想要对其乘积进行因式分解却极其困难,因此可以将乘积公开作为加密密钥,即公钥,而两个大素数组合成私钥。公钥是可发布的供任何人使用,私钥则为自己所有,供解密之用。
1.2 工作流程
A 要把信息发给 B 为例,确定角色:A 为加密者,B 为解密者。首先由 B 随机确定一个 KEY,称之为私钥,将这个 KEY 始终保存在机器 B 中而不发出来;然后,由这个 KEY 计算出另一个 KEY,称之为公钥。这个公钥的特性是几乎不可能通过它自身计算出生成它的私钥。接下来通过网络把这个公钥传给 A,A 收到公钥后,利用公钥对信息加密,并把密文通过网络发送到 B,最后 B 利用已知的私钥,就能对密文进行解码了。以上就是 RSA 算法的工作流程。
1.3 攻击
2009年12月12日,编号为 RSA-768 (768 bits, 232 digits) 数也被成功分解。这一事件威胁了现通行的1024-bit 密钥的安全性,普遍认为用户应尽快升级到2048-bit或以上。
1.4 例子1
包括生成公私钥对,转换成字符串可以保存在数据库、配置文件或者配置中心。
加解密字符串是发送端使用公钥加密数据,在接收端使用私钥解密数据。数字签名的公私钥使用顺序正好相反。
package com.erbadagang.springboot.rsa;
import javax.crypto.Cipher;
import java.security.*;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
/**
* @description 大一统的例子,包括生成公私钥对,加解密字符串。
* @ClassName: RsaDemo
* @author: 郭秀志 jbcode@126.com
* @date: 2020/7/25 10:33
* @Copyright:
*/
public class RsaDemo {
private static final String ALGO = "RSA";
private static final String CHARSET = "UTF-8";
/*
* 用于存储随机产生的公钥与私钥
*/
private static Map<Integer, String> KEY_CACHE = new HashMap<>();
/**
* 随机生成密钥对
*
* @throws NoSuchAlgorithmException
*/
private static void generateKeyPair() throws NoSuchAlgorithmException {
// KeyPairGenerator 类用于生成公钥和私钥对,基于RSA算法生成对象
KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(ALGO);
// 初始化密钥对生成器,密钥大小为 96-1024 位
keyPairGen.initialize(2048, new SecureRandom());
// 生成一个密钥对,保存在 keyPair 中
KeyPair keyPair = keyPairGen.generateKeyPair();
// 得到私钥
RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
// 得到公钥
RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
String publicKeyString = new String(Base64.getEncoder().encode(publicKey.getEncoded()));
// 得到私钥字符串
String privateKeyString = new String(Base64.getEncoder().encode((privateKey.getEncoded())));
// 将公钥和私钥保存到 Map
KEY_CACHE.put(0, publicKeyString);
KEY_CACHE.put(1, privateKeyString);
}
/**
* RSA公钥加密
*
* @param data 加密字符串
* @param publicKey 公钥
* @return 密文
* @throws Exception 加密过程中的异常信息
*/
private static String encrypt(String data, String publicKey) throws Exception {
// base64 编码的公钥
byte[] decoded = Base64.getDecoder().decode(publicKey);
RSAPublicKey pubKey = (RSAPublicKey) KeyFactory.getInstance(ALGO).generatePublic(new X509EncodedKeySpec(decoded));
// RSA加密
Cipher cipher = Cipher.getInstance(ALGO);
// 公钥加密
cipher.init(Cipher.ENCRYPT_MODE, pubKey);
return Base64.getEncoder().encodeToString(cipher.doFinal(data.getBytes(CHARSET)));
}
/**
* RSA私钥解密
*
* @param data 加密字符串
* @param privateKey 私钥
* @return 铭文
* @throws Exception 解密过程中的异常信息
*/
private static String decrypt(String data, String privateKey) throws Exception {
byte[] inputByte = Base64.getDecoder().decode(data.getBytes(CHARSET));
// base64 编码的私钥
byte[] decoded = Base64.getDecoder().decode(privateKey);
RSAPrivateKey priKey = (RSAPrivateKey) KeyFactory.getInstance(ALGO).generatePrivate(new PKCS8EncodedKeySpec(decoded));
// RSA 解密
Cipher cipher = Cipher.getInstance(ALGO);
// 私钥解密
cipher.init(Cipher.DECRYPT_MODE, priKey);
return new String(cipher.doFinal(inputByte));
}
public static void main(String[] args) {
String originData = "郭秀志 Test Asymmetric encrypt!";
System.out.println("originData = " + originData);
try {
generateKeyPair();
String publicKey = KEY_CACHE.get(0);
System.out.println("publicKey = " + publicKey);
String encryData = encrypt(originData, publicKey);
System.out.println("encryData = " + encryData);
String privateKey = KEY_CACHE.get(1);
System.out.println("privateKey = " + privateKey);
String decryData = decrypt(encryData, privateKey);
System.out.println("decryData = " + decryData);
} catch (Exception e) {
e.printStackTrace();
}
}
}
运行结果:
originData = 郭秀志 Test Asymmetric encrypt!
publicKey = MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAi8DngVAbAS5vBUgJjAaTflHVKSVfRGqDV81pmxT+XAJzZ2M26J+F5U1Vx+bb0MReKTEOs2q6VLwQGEsJN2pSG7zrRwwKiJtkxQoEaeJ0f4mmFlhRnD0q2xbSjkoZaSAbWy7KWuyEaDzjspl8xBodB1kvLORBqVJ07YRy5a3/2KeEoHE8B6brNMBrLUZ5UNs4hn97iMLbuUsYfp07kRaKCCjWemqPOCijKjWfIqpbpzoEw92hr/RDrkOS4N6DMXQ+DOpW7b2JoZC/pqZ/GY0tKgLpFdEN+lUXG9SoB1sRO2CVKUdDZ/iT6FlnQG2Fej8YaYNvKBGGlBPSqW0pdJ9j0wIDAQAB
encryData = h+q0jd4Sf64eT0mdfW8k2RPYNkt5A7qVjqvmv1QN33GEmEeE51TJhnaSDdZyletaS8HVcu+PJw/V/69bJ2SymkXG7cIFcw6OA1iLn6KGvE6lOaKfST8ARZzVteaQr9CRdGijNqum1r/rLrvtUHb+mpyirC9YqIZgH1AcFIw+zZtYphw/sNDRCbIG9F4QsotLJpZmE1ukJw+lOFFEbXVmpaiYmUM3Uw3RCHMVlws/oqZgRElqhB7PUDffuk03dczfX1LL1GarSC6HGHIpsL+WzhM0TeOhxrUyl5rqblAPQ4TtbGXN5tTlvXMFIl/KoL9rv/izY3UsjMJ2RM3ZHSBB7w==
privateKey = MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCLwOeBUBsBLm8FSAmMBpN+UdUpJV9EaoNXzWmbFP5cAnNnYzbon4XlTVXH5tvQxF4pMQ6zarpUvBAYSwk3alIbvOtHDAqIm2TFCgRp4nR/iaYWWFGcPSrbFtKOShlpIBtbLspa7IRoPOOymXzEGh0HWS8s5EGpUnTthHLlrf/Yp4SgcTwHpus0wGstRnlQ2ziGf3uIwtu5Sxh+nTuRFooIKNZ6ao84KKMqNZ8iqlunOgTD3aGv9EOuQ5Lg3oMxdD4M6lbtvYmhkL+mpn8ZjS0qAukV0Q36VRcb1KgHWxE7YJUpR0Nn+JPoWWdAbYV6Pxhpg28oEYaUE9KpbSl0n2PTAgMBAAECggEAVxHNalx9JqRGWRUDlbD3LalQU/1LuHVf9VEuLYfL3YlNWymOKSpSIoWnHw9u/keJnsJItXGWO3qb0HbXfCYKl+uA7lfLLEccZkKSc2G6UUdyKdPGrL/TNoKmli4GXN+7C3lAa5uV9teQyVIlUIwwb8aZxK9FVXfhD6YIa56XmuP/c4z+uqyTdjC1YFS8TPL+mLh4qOiiYejkhhX6KLh9bHvCWTkDGTZIZjLdxoDqV5bjxWpJ0F+6Kn66PlfegrPPcjW5OhP/Z+c3rKtuEPnf51ug69AyZjYzYIqRZOezGYgjaqg8is3MDdWZOx0FsZpUC9o6dwIzsDgYFZDxydFD0QKBgQDJJkrYFluePlajtCyBjyyLkvIkZZKOE3r5jjbzUNZYG9sZWVrvAYsReQZiEw30h5yEOJOFOkmbpBW7TNABWh3N1rPLzqXX+EW9fhA2Y1+uiC+3/7pqK80L/JleJXFVyuVbfiThf0EnwcowHnKOWc8ZfcqhUSpO/lnkEasr1qkY6wKBgQCx3LhZLFc39D/DFxrHZmY02ZN9Ffn6++UVjA/IhPNYiEZl4SUd04hF8b4QQUta00rLr2JRNYlJEg32SxuC9bmf8XbX3UitLdZ5re5s/5WSvNzwf7U06USj1fhPLwfCCjubQiSwP9BNPJ/2ITuOtnQcZmZ5Wmgs15diQMCYucymuQKBgQDHUf+GIpmEtAcMTqRveZ1NbT4uXMwdpyYLliXTc34Cbw/sDYQzI9dXaBKwKmuArMSmrJ1ZvklkRfMW12WigVbZOnCNe2cRHD6XKA0Op+gPPXnznR9ux5p2z0Z2aSnmNpiR0ezf2kaJC9m7VuBzOIEkpGae9Zu0DQysF+oDFcIYIwKBgBOVafbnmvLeQecJNDmgXMCU9FhhgxTPh3nH4jUB7olg999f2uZd1DNfWr4Pcmydty6WMQ0gB+2zvzXPL0hMJhQmUh+Sjd4DngnnzMjTm3R8txcD+L/Kr3QaqyyM0R3cYpPFxKRjYlwewL4pCpW8ISy/Waki+zV0x4ZZ+trWGmKBAoGAR9t9gmX99Yck32RZd2t9wkAQ8TUfaRNtworLXti5cV6bsNPf6XDowEKC2NX9AoXjPbBaTsry37SpOe7NWskVhE8sFAwa2m4uk/PVGLxn5rvEyn6tAPnguSn+Ov6pDYv5vSUFk7R+M/bP1cCoLaqHENt3SaJjnJ9+B2/ZfT7MQLg=
decryData = 郭秀志 Test Asymmetric encrypt!
使用了
java.util的Base64
进行encode和decode。但是要注意:java中有自带的Base64算法类,但是安卓中却没有,之前出现的情况是,使用的Base64类不统一,比如在安卓客户端开发使用的Base64算法是使用第三方提供的jar包,而java服务端中使用的是JDK自带的Base64,导致从客户端传过来的密文,服务端解析出错。
org.apache.commons.codec.binary.Base64
的高版本已经没有对应的方法。
1.5 例子2(文件存储公私钥)
package com.erbadagang.springboot.rsa;
import org.apache.commons.codec.binary.Base64;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.math.BigInteger;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
/**
* 生成公私钥对并存储到本地文件。
*
* @ClassName: RsaDemoWithFile
* @author: 郭秀志 jbcode@126.com
* @date: 2020/7/25 10:58
* @Copyright:
*/
public class RsaDemoWithFile {
/**
* 生成公钥和私钥, 一般一次性生成, 存储在文件中进行分发和使用
*/
public static void generateKey() {
try {
KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
kpg.initialize(1024); //一般来说, 长度为2048是最好的, 也是推荐的
KeyPair kp = kpg.genKeyPair();
PublicKey pbkey = kp.getPublic();
PrivateKey prkey = kp.getPrivate();
// 保存公钥
FileOutputStream f1 = new FileOutputStream("d:/pubkey.dat");
ObjectOutputStream b1 = new ObjectOutputStream(f1);
b1.writeObject(pbkey);
// 保存私钥
FileOutputStream f2 = new FileOutputStream("d:/privatekey.dat");
ObjectOutputStream b2 = new ObjectOutputStream(f2);
b2.writeObject(prkey);
} catch (Exception e) {
}
}
/**
* 公钥加密, 一般调用者传递明文, 从本地存储读取公钥进行加密
*
* @param plainTxt
* @return
* @throws Exception
*/
public static String pubEncrypt(String plainTxt) throws Exception {
String s = Base64.encodeBase64String(plainTxt.getBytes("UTF-8"));
// 获取公钥及参数e,n
FileInputStream f = new FileInputStream("d:/pubkey.dat");
ObjectInputStream b = new ObjectInputStream(f);
RSAPublicKey pbk = (RSAPublicKey) b.readObject();
BigInteger e = pbk.getPublicExponent();
BigInteger n = pbk.getModulus();
// 获取明文m
byte ptext[] = s.getBytes("UTF-8");
BigInteger m = new BigInteger(ptext);
// 计算密文c
BigInteger c = m.modPow(e, n);
// 保存密文
String ciperTxt = c.toString();
return ciperTxt;
}
/**
* 私钥解密, 一般调用者传递密文, 从本地存储读取私钥进行解密
*
* @param ciperTxt
* @return
* @throws Exception
*/
public static String privDecrypt(String ciperTxt) throws Exception {
BigInteger c = new BigInteger(ciperTxt);
// 读取私钥
FileInputStream f = new FileInputStream("d:/privatekey.dat");
ObjectInputStream b = new ObjectInputStream(f);
RSAPrivateKey prk = (RSAPrivateKey) b.readObject();
BigInteger d = prk.getPrivateExponent();
// 获取私钥参数及解密
BigInteger n = prk.getModulus();
BigInteger m = c.modPow(d, n);
// 显示解密结果
byte[] mt = m.toByteArray();
String plainTxt = new String(Base64.decodeBase64(mt), "UTF-8");
return plainTxt;
}
public static void main(String args[]) {
try {
generateKey();
String ciperTxt = pubEncrypt("郭秀志 Test Asymmetric encrypt!");
System.out.println("公钥加密密文:" + ciperTxt);
System.out.println("私钥解密:" + privDecrypt(ciperTxt));
} catch (Exception e) {
System.out.println(e.toString());
}
}
}
运行结果:
公钥加密密文:6099844164314059443431591651751615722055254322730619374416036868657674637290798963105567557375836230466843160570341925833251877311218292252966007503499880207100523576112983947344123430250478502342666667129135836235200008011709395758839438577626394237727966426413570214930347318988576727557310041590164979452
私钥解密:郭秀志 Test Asymmetric encrypt!
1.6 例子3(使用controller加解密)
1.6.1 生成公私钥对的工具类RsaGenerator
package com.erbadagang.springboot.rsa.util;
import lombok.Getter;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
/**
* @description RSA公私钥对生成工具, 加解密处理注意:密钥长度2048
* @ClassName: RsaGenerator
* @author: 郭秀志 jbcode@126.com
* @date: 2020/7/25 11:16
* @Copyright:
*/
@Getter
public class RsaGenerator {
/**
* 加密算法RSA
*/
public static final String KEY_ALGORITHM = "RSA";
/**
* 获取公钥的key
*/
private RSAPublicKey publicKey;
/**
* 获取私钥的key
*/
private RSAPrivateKey privateKey;
/**
* <p>
* 生成密钥对(公钥和私钥)
* </p>
*
* @return
* @throws Exception
*/
public void generateKeyPair() throws NoSuchAlgorithmException {
KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(KEY_ALGORITHM);
keyPairGen.initialize(2048);
KeyPair keyPair = keyPairGen.generateKeyPair();
this.publicKey = (RSAPublicKey) keyPair.getPublic();
this.privateKey = (RSAPrivateKey) keyPair.getPrivate();
}
}
1.6.2 获取公私钥对的Controller方法getRsaKey()
:
package com.erbadagang.springboot.rsa.controller;
import com.erbadagang.springboot.rsa.util.RsaGenerator;
import org.apache.commons.codec.binary.Base64;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
import java.util.Map;
/**
* RsaController作用是:通过controller来生成公私钥对,加密及解密。
*
* @ClassName: RsaController
* @author: 郭秀志 jbcode@126.com
* @date: 2020/7/25 11:09
* @Copyright:
*/
@RestController
@RequestMapping("rsa")
public class RsaController {
/**
* 获取RSA加密形式的publicKey和privateKey
*/
@GetMapping("/genKey")
public Map<String, String> getRsaKey() throws NoSuchAlgorithmException {
RsaGenerator rsaGenerator = new RsaGenerator();
rsaGenerator.generateKeyPair();
Map<String, String> map = new HashMap<>(4);
map.put("privateKey", Base64.encodeBase64String(rsaGenerator.getPrivateKey().getEncoded()));
map.put("publicKey", Base64.encodeBase64String(rsaGenerator.getPublicKey().getEncoded()));
return map;
}
}
访问上面URLhttp://localhost:8080/rsa/genKey
运行返回结果:
{
"privateKey": "MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDKr3Zhb/aM7DHnhT0PN2Fb7hnQxCfjwkurtROSVNZhDvaxAiNhuC/wUIUDd9tAGQSgIgEag6ISaFMcRIMTADpN6QJyiUcVy2M4LmRO55VotmMB9g6OUBmsBju3TL58Iloy05j1x/T0PG9N0KegKDbIwG8/FQsKXm5kfDOV5piPPReveiykfv6kh8ZqCbSBtIVpgdPDD5r13Cj4+Q0xGabznGXv5gC0xhKyZr3ayGPGcUvB2eW3FAFn6qOLMKvbawnhDZPkiNdGU+yD6Uz27R5Fyy8s1wGK3Uk+yPA23mxVXsYu19a+8JINRV1acmYGGXsYJmAi445kNsgFVsO28TLdAgMBAAECggEAC6plqd4D1sCRbr3gccvCMsRVgAqKMTWxnURix/1SCWwPDskMuEcdmztHLJftapcGCSFr5tbEsUKH5gybbrCIqotKtMTp7nsyTr180H3Lv6cfs7ExzUcW8yu4rCginoprnplHKH5FvvjrfxMPUsx9urg4ruzLIeGlgOsVHP+UsEm9oqGSA1O7fzWQfJszuLRSVAu8GFYYzpGMm5FMVfgPHpZZUrZRcnJnMwQVNzwNh121zDmbx7Win/LKAD4C6s6g/KDKs7L1QYhiEBvmI0AkCcaLtdl3YhiZfNSbxRnJ364nnLFZSPvyvLl0zxFyPWIX61jYKSqu6uM3nogT5JnvgQKBgQDrxhDKwLCYmOd2QZ5EPK9/d+y1SOgwKxW243SAZobRIVTUkJW3NzJe5lxisbMEDsxRyUKHm0aL49bNkQeklBmnMAmtSG5MMgTz/fqRjN7o9+bWukdOwFVRNfDJ/dbHKCDr+dwCiHDyu/qkcCfjIBUR+mS1z2x3LWUIxwb0MQkuVQKBgQDcErrH5zeJ86obWDhhEstD0JgYf/pGXpGQadEcuOall0Uhc/hRlTSTH7QC+k3J5zQ8WdG78vxEAAuPMC7JU7Nm0GKw0xmp/nq0nwvmF+psAw+uNNtbIn1o3uUjqdt27Sv7rS512mKX8S1Mj7y2jtn45lLv9VeYDsSL0O2k2wZqaQKBgQChYhq+XbTDTu4oQPQPKybJbpIE6Jmd1u/vFrP467TeUx1YvnrsRQjicnXMTGwHnAV4+fTjE4LvYA34+YusuH7ytGv7Q3fUCezgAfnQRQeTmZRVaH5Exlvf0bc229x2x935CDbzOOdvDwKaKfbzfVNO0gC7ffZ1gQoGPw1gemwZXQKBgQDEED+tpxX45kevsuoPueGzmhxW/3VmygvfYBa4AxchgeJKCnq5nDdJt931JTC2ZzBHcDIFw1Xx8yRZPjEAlnxnZdH2/SuJIroJPwUnyjjEX/nRVy/yQoj+LE5ydnqaunQL9d9Fifl6qpiT9B7Jef1B3VkYhTiztLxwYAPIcoWFuQKBgFulQsCQYESMeT59Vf9cSUTlVs/Hen2TsKeHhaqhx3SyRsHLvDnTz5JxL/5GZS8dCb+VZdCxcMYtfaFfY2wzVBDRj7wOFLf6RuQ5mSwHpapRv2XV4pImVRAjktFQsHAyN7fflhpVy/cGWw7tovJPjmIDNGIq8hdiIdo6CfepGoTB",
"publicKey": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyq92YW/2jOwx54U9DzdhW+4Z0MQn48JLq7UTklTWYQ72sQIjYbgv8FCFA3fbQBkEoCIBGoOiEmhTHESDEwA6TekCcolHFctjOC5kTueVaLZjAfYOjlAZrAY7t0y+fCJaMtOY9cf09DxvTdCnoCg2yMBvPxULCl5uZHwzleaYjz0Xr3ospH7+pIfGagm0gbSFaYHTww+a9dwo+PkNMRmm85xl7+YAtMYSsma92shjxnFLwdnltxQBZ+qjizCr22sJ4Q2T5IjXRlPsg+lM9u0eRcsvLNcBit1JPsjwNt5sVV7GLtfWvvCSDUVdWnJmBhl7GCZgIuOOZDbIBVbDtvEy3QIDAQAB"
}
二、RSA加密算法+AES加密算法
2.1 AES概念
高级加密标准(Advanced Encryption Standard,缩写:AES),在密码学中又称Rijndael加密法,是美国联邦政府采用的一种区块加密标准。这个标准用来替代原先的DES。
2.2 数据传送的两个模式
2.2.1 客户端传输重要信息给服务端,服务端返回的信息不需加密的情况
客户端传输重要信息给服务端,服务端返回的信息不需加密,例如绑定银行卡的时候,需要传递用户的银行卡号,手机号等重要信息,客户端这边就需要对这些重要信息进行加密,使用RSA公钥加密,服务端使用RSA解密,然后返回一些普通信息,比如状态码code,提示信息msg,提示操作是成功还是失败。这种场景下,仅仅使用RSA加密是可以的。
2.2.2 客户端传输重要信息给服务端,服务端返回的信息需加密的情况
客户端传输重要信息给服务端,服务端返回的信息需加密,例如客户端登录的时候,传递用户名和密码等资料,需要进行加密,服务端验证登录信息后,返回令牌token需要进行加密,客户端解密后保存。此时就需要结合这两种算法了。
客户端使用公钥加密,服务端使用私钥解密的过程。也许你会这么想,既然可以如此,那服务端那边信息也可以通过RSA加密后,传递加密信息过来,客户端进行解密。但是,这样做,显示是不安全的。原因是,由于客户端并没有保存私钥,只有公钥,只可以服务端进行私钥加密,客户端进行公钥解密,但由于公钥是公开,别人也可以获取到公钥,如果信息被他们截取,他们同样可以通过公钥进行解密,那么这样子加密,就毫无意义了,所以这个时候,就要结合对称算法,实现客户端与服务端之前的安全通信了。另外一个重要原因是上篇文章提到的:“作为加密使用的 RSA 有着随密钥长度增加,性能急剧下降的问题”,所以使用AES加密业务数据,仅仅使用RSA来加解密AES的对称密钥。
2.3 AES加解密过程
于AES属于对称算法,加密和解密需要使用同一把密钥,所以,服务端要解密传递过来的内容,就需要密钥 + 密文。
客户端使用AES进行加密,服务端要进行解密的话,需要用到产生的密钥,那密钥必须从客户端传输到服务端,如果不对密钥进行加密,那加密就没有意义了。所以这里终于谈到了重点,RSA算法+AES算法结合使用。
2.3.1 客户端使用RSA + AES对重要信息进行加密步骤
1.客户端随机产生AES的密钥;
2.对重要信息进行AES加密;
3.通过使用服务端RSA公钥对AES密钥进行加密。
这样在传输的过程中,即时加密后的AES密钥被别人截取,对其也无济于事,因为他并不知道服务端RSA的私钥,无法解密得到原本的AES密钥,就无法解密用AES加密后的重要信息。
2.3.2 服务端使用RSA + AES对重要信息进行解密步骤
1.对加密后的AES密钥进行服务端RSA私钥解密,拿到密钥原文;
2.对加密后的重要信息进行AES解密,拿到原始内容。
现实开发中,服务端有时也需要向客户端传递重要信息,比如登录的时候,返回token给客户端,作为令牌,这个令牌就需要进行加密,原理也是差不多的,比上面多一个步骤而已,就是将解密后的AES密钥,对将要传递给客户端的数据token进行AES加密,返回给客户端,由于客户端和服务端都已经拿到同一把AES钥匙,所以客户端可以解密服务端返回的加密后的数据。如果客户端想要将令牌进行保存,则需要使用自己定义的默认的AES密钥进行加密后保存,需要使用的时候传入默认密钥和密文,解密后得到原token。
2.4 代码:
/**
* 发送端逻辑:
* 1.客户端随机产生AES的密钥;
* 2.对重要信息进行AES加密;
* 3.通过使用服务端RSA公钥对AES密钥进行加密。
* 接收端逻辑:
* 1.对加密后的AES密钥进行服务端RSA私钥解密,拿到密钥原文;
* 2.对加密后的重要信息进行AES解密,拿到原始内容。
*/
@GetMapping("/encryptDecryptSign")
public void encryptDecryptSign() throws Exception {
//发送方公私钥
String sendPrivateKey = "MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDKr3Zhb/aM7DHnhT0PN2Fb7hnQxCfjwkurtROSVNZhDvaxAiNhuC/wUIUDd9tAGQSgIgEag6ISaFMcRIMTADpN6QJyiUcVy2M4LmRO55VotmMB9g6OUBmsBju3TL58Iloy05j1x/T0PG9N0KegKDbIwG8/FQsKXm5kfDOV5piPPReveiykfv6kh8ZqCbSBtIVpgdPDD5r13Cj4+Q0xGabznGXv5gC0xhKyZr3ayGPGcUvB2eW3FAFn6qOLMKvbawnhDZPkiNdGU+yD6Uz27R5Fyy8s1wGK3Uk+yPA23mxVXsYu19a+8JINRV1acmYGGXsYJmAi445kNsgFVsO28TLdAgMBAAECggEAC6plqd4D1sCRbr3gccvCMsRVgAqKMTWxnURix/1SCWwPDskMuEcdmztHLJftapcGCSFr5tbEsUKH5gybbrCIqotKtMTp7nsyTr180H3Lv6cfs7ExzUcW8yu4rCginoprnplHKH5FvvjrfxMPUsx9urg4ruzLIeGlgOsVHP+UsEm9oqGSA1O7fzWQfJszuLRSVAu8GFYYzpGMm5FMVfgPHpZZUrZRcnJnMwQVNzwNh121zDmbx7Win/LKAD4C6s6g/KDKs7L1QYhiEBvmI0AkCcaLtdl3YhiZfNSbxRnJ364nnLFZSPvyvLl0zxFyPWIX61jYKSqu6uM3nogT5JnvgQKBgQDrxhDKwLCYmOd2QZ5EPK9/d+y1SOgwKxW243SAZobRIVTUkJW3NzJe5lxisbMEDsxRyUKHm0aL49bNkQeklBmnMAmtSG5MMgTz/fqRjN7o9+bWukdOwFVRNfDJ/dbHKCDr+dwCiHDyu/qkcCfjIBUR+mS1z2x3LWUIxwb0MQkuVQKBgQDcErrH5zeJ86obWDhhEstD0JgYf/pGXpGQadEcuOall0Uhc/hRlTSTH7QC+k3J5zQ8WdG78vxEAAuPMC7JU7Nm0GKw0xmp/nq0nwvmF+psAw+uNNtbIn1o3uUjqdt27Sv7rS512mKX8S1Mj7y2jtn45lLv9VeYDsSL0O2k2wZqaQKBgQChYhq+XbTDTu4oQPQPKybJbpIE6Jmd1u/vFrP467TeUx1YvnrsRQjicnXMTGwHnAV4+fTjE4LvYA34+YusuH7ytGv7Q3fUCezgAfnQRQeTmZRVaH5Exlvf0bc229x2x935CDbzOOdvDwKaKfbzfVNO0gC7ffZ1gQoGPw1gemwZXQKBgQDEED+tpxX45kevsuoPueGzmhxW/3VmygvfYBa4AxchgeJKCnq5nDdJt931JTC2ZzBHcDIFw1Xx8yRZPjEAlnxnZdH2/SuJIroJPwUnyjjEX/nRVy/yQoj+LE5ydnqaunQL9d9Fifl6qpiT9B7Jef1B3VkYhTiztLxwYAPIcoWFuQKBgFulQsCQYESMeT59Vf9cSUTlVs/Hen2TsKeHhaqhx3SyRsHLvDnTz5JxL/5GZS8dCb+VZdCxcMYtfaFfY2wzVBDRj7wOFLf6RuQ5mSwHpapRv2XV4pImVRAjktFQsHAyN7fflhpVy/cGWw7tovJPjmIDNGIq8hdiIdo6CfepGoTB";
String sendPublicKey = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyq92YW/2jOwx54U9DzdhW+4Z0MQn48JLq7UTklTWYQ72sQIjYbgv8FCFA3fbQBkEoCIBGoOiEmhTHESDEwA6TekCcolHFctjOC5kTueVaLZjAfYOjlAZrAY7t0y+fCJaMtOY9cf09DxvTdCnoCg2yMBvPxULCl5uZHwzleaYjz0Xr3ospH7+pIfGagm0gbSFaYHTww+a9dwo+PkNMRmm85xl7+YAtMYSsma92shjxnFLwdnltxQBZ+qjizCr22sJ4Q2T5IjXRlPsg+lM9u0eRcsvLNcBit1JPsjwNt5sVV7GLtfWvvCSDUVdWnJmBhl7GCZgIuOOZDbIBVbDtvEy3QIDAQAB";
//接收方公私钥
String recPrivateKey = "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCP3no8bYUMsdW1sSsIaUxaWDxxOBlDYNCuoVn7BlyptCnIOkqu86dVCB0dRxG90KlZoCtXhfYQgZkIkzUYdAeQsIHJr2PZn+z8XuqQllqDXDbBi0VXIx0JS8pjNP9pe/3iyLOWK7UPZkew1+OaomKnjpjFAYUxBlakFloP02mmaOfCHxkHzLWQRw/uBD7WCmCtKP5Mism78s5QuZxyfbMopECloVU8YzYc6tm5nygPOvudIxZtCJDNRTmgXtDra/WnvWE/+hVBjvCO1uPxLI5krPwdlYuBzlrylmuCoVZr9K5mJcMLt4SH2ApWLx/hyWJbyB1FZlhM4DzGIw2oRnnVAgMBAAECggEAfmAPP8V0ehI8h71474qPZ0zayxlcF7OTm9JgGAEepHN9wER0Ffoxop/d8znae8IvAGuRpvAllZpBsyacHT7O5moll+RY8XFp2sYFhbyNBZabAqgz4LcXanMI9Nw4/4/LFMr39ZGvGjfeAZmidNLvlf/MckFDnizTLo/zzLMIuwNW7ZNAYCokNa+/MwmTly6d1fuoazYNvv+4u7GVkEU9pNDwqeQP0JRY1O5uN+7RLfH7IZ3ObuKIvoJOIZCzEgSqA0DTvqgRkNHm3L5oRkVXyv85rO55n3EFwZ6boe1KaHRGX0WHTpkGWcto9Sz3tmKIexBCeveV+XjwRya5RJrcAQKBgQDdikiTSYmXMREdQfab4+BXrK4Wv5c0l+DdfGczdn4/wl5TMV7nEXh9zOSgET1Wxt6yULFQalofHQzZym5VENsEe2kjSEWf39m4Y+wRHjhsG89wSDEGL+dYVsS74Np0doREVzOssXSltfV2iOAwBgublMQJ41hLcC7ypMZugtKz+QKBgQCmP1WtgFB5uugEKaqQul9N/17g1PZ1vbIRAoYhinbsBrlhc+H6YonFXPECIcc84Hds7dhxkcLUlUawybTo/JzpvQBTB823TH1U/VuA+uE3ajQv2lZC8hYWu4hpwndsdqePrmmXVJFT2aH12E9y5yaoVesvFPQ350x/NVLxfQUzvQKBgQCsW+XTEaeGhZo3FRb0efoUvDhFYpIVTQSZzSvNkibvHB2exA59383Ksho9nqwGU3r3aGhLlDLBeiyBVUk5zX9YoVtPI+9nTxVoq/UB7G0hTxG43bGmiqaGyBsPwQS1D3Aga2e8t+N0+Xgb3KnvMwTc6oUK3GHZb1JXXXM0j3u2oQKBgCHeDy88T6is2e1XK6c2QIocNxDocZkE3wy2DesxUQ6+Q+/FcsjWYCizyWlcxkDxnYK0ZX6laiJykqcbQF6ib7jyRumjUlZAH9w7jPOWqGDoot8IxL/4n2VcKOsasceH2JTdvCcXFFAXqvXxbiYDTw3GCxZZV3M4DI5xp4cIqBGlAoGAUHdGKaCauzdUrXB8wV9symBQvrEbmAXlHI713CpDPBHjYZ+STvYbMncSxI2IXELZkE0UHYQYqDgc+h+1n+r5lgi+ljBcFAiZBlU4cdrdzkEvJ6/OMqyAlko5+0hOr1tgkuPU1jQe33JuOaKvzqtGjmV8hkxh2wH0FSGBfLLd4IE=";
String recPublicKey = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAj956PG2FDLHVtbErCGlMWlg8cTgZQ2DQrqFZ+wZcqbQpyDpKrvOnVQgdHUcRvdCpWaArV4X2EIGZCJM1GHQHkLCBya9j2Z/s/F7qkJZag1w2wYtFVyMdCUvKYzT/aXv94sizliu1D2ZHsNfjmqJip46YxQGFMQZWpBZaD9Nppmjnwh8ZB8y1kEcP7gQ+1gpgrSj+TIrJu/LOULmccn2zKKRApaFVPGM2HOrZuZ8oDzr7nSMWbQiQzUU5oF7Q62v1p71hP/oVQY7wjtbj8SyOZKz8HZWLgc5a8pZrgqFWa/SuZiXDC7eEh9gKVi8f4cliW8gdRWZYTOA8xiMNqEZ51QIDAQAB";
// String AesKey =RandomStringUtils.randomAlphanumeric(16);
//输入的明文参数
String inputJson = "{\"name\": \"guo\",\"age\": 40}";
/**
* 发送端业务开始
*/
byte[] aesKey = AesUtils.generateAesKey(128);
// AES 对称加密业务数据
String encryptData = AesUtils.encrypt(inputJson, aesKey);//注意key为16位
System.out.println("encryptData = " + encryptData);
// RSA 私钥签名数据
String signature = RsaUtils.sign(inputJson, sendPrivateKey);
// RSA 公钥加密AES密钥传送给接收方
String encryptAesKey = RsaUtils.encrypt(aesKey, recPublicKey);
/**
* 接收端业务开始
*/
byte[] decryptedData;
boolean signChecked;
// 使用"接收端RSA私钥"进行【非对称解密操作】发送端的“AES对称加密Key”
byte[] encryptKeyByte = RsaUtils.decrypt(encryptAesKey, recPrivateKey);
System.out.println("encryptAesKey = " + Base64.encodeBase64String(encryptKeyByte));
// 使用对称加密AES Key对发送端对称加密后的业务数据内容,进行【对称解密操作】,获取解密后的数据内容。
decryptedData = AesUtils.decrypt(encryptData, encryptKeyByte);
System.out.println("decryptedData解密后的业务数据 = " + new String(decryptedData));
// 使用“发送端RSA公钥”对发送端使用“RSA私钥进”行非对称加密签名后的数据进行【非对称解密验签操作】,判断数据完整性。
signChecked = RsaUtils.checkSign(decryptedData, signature, sendPublicKey);
System.out.println("signChecked = " + signChecked);
}
2.5 测试
访问URLhttp://localhost:8080/rsa/encryptDecryptSign
。
输出日志:
encryptData = gB6NMdYoL/CiKVuxPx7+b39KE4BEowmALf3Z98P8yas=
encryptAesKey = CdgT8AViaDMl9R000drYmg==
decryptedData解密后的业务数据 = {"name": "guo","age": 40}
signChecked = true
底线
本文源代码使用 Apache License 2.0开源许可协议,这里是本文源码Gitee地址,可通过命令git clone+地址
下载代码到本地,也可直接点击链接通过浏览器方式查看源代码。