需求&实现思路
工作中遇到一个需求,需要将接口数据加密发送给后台,项目中采用RSA+AES方式,记录一下思路和实现。
一、加密
1、随机生成AES 32位密钥
2、通过AES对传递数据加密
3、通过RSA的公钥Publickey对AES的密钥进行加密
4、通过RSA的私钥Privatekey对数据进行签名
二、解密
1、得到数据拿到sign值,先做验签
2、使用RSA的私钥private_key解密拿到AES的aesKey
3、使用AES解密得到所需数据
下面是具体实现步骤
1、通过opensll生成加解密所需要的公钥和私钥,生成步骤自行百度,这里 不在介绍
2、AES加解密
public static class AESUtils {
private static final String AES = "AES";
//偏移量
private static final String IV_STRING = "";
/**
* 密钥长度32字节,256位
*/
private static final int AES_KEY_LENGTH = 32;
/**
* 随机生成32位AES密钥
*/
public static String getRandomString() {
//生成规则自己定义
String base = "";
Random random = new Random();
StringBuffer sb = new StringBuffer();
for (int i = 0; i < AES_KEY_LENGTH; i++) {
int number = random.nextInt(base.length());
sb.append(base.charAt(number));
}
return sb.toString();
}
/**
* 加密
*
* @param content 加密内容
* @return 密文
* @throws Exception e
*/
@RequiresApi(api = Build.VERSION_CODES.O)
public static String encrypt(String key, String content) throws Exception {
byte[] encryptedBytes = new byte[0];
try {
byte[] byteContent = content.getBytes();
// 注意,为了能与 iOS 统一
// 这里的 key 不可以使用 KeyGenerator、SecureRandom、SecretKey 生成
byte[] enCodeFormat = key.getBytes(StandardCharsets.UTF_8);
SecretKeySpec secretKeySpec = new SecretKeySpec(enCodeFormat, AES);
byte[] initParam = IV_STRING.getBytes();
IvParameterSpec ivParameterSpec = new IvParameterSpec(initParam);
// 指定加密的算法、工作模式和填充方式
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec);
encryptedBytes = cipher.doFinal(byteContent);
// 同样对加密后数据进行 base64 编码
} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | InvalidAlgorithmParameterException | IllegalBlockSizeException | BadPaddingException e) {
LogUtil.e("AES decrypt Exception,content = {},Exception = {}" + content + Arrays.toString(e.getStackTrace()));
}
// return Base64.getUrlEncoder().encodeToString(encryptedBytes);
return Base64Util.encode(encryptedBytes);
}
/**
* 解密
*
* @param content 密文
* @return 明文
* @throws Exception e
*/
@RequiresApi(api = Build.VERSION_CODES.O)
public static String decrypt(String content, String aesKey) {
// base64 解码
try {
byte[] encryptedBytes = Base64Util.decode(content);
byte[] enCodeFormat = aesKey.getBytes();
SecretKeySpec secretKey = new SecretKeySpec(enCodeFormat, AES);
byte[] initParam = IV_STRING.getBytes();
IvParameterSpec ivParameterSpec = new IvParameterSpec(initParam);
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, secretKey, ivParameterSpec);
byte[] result = cipher.doFinal(encryptedBytes);
return new String(result, "UTF-8");
} catch (IOException | NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | InvalidAlgorithmParameterException | IllegalBlockSizeException | BadPaddingException e) {
LogUtil.e("AES decrypt Exception,content = {},Exception = {}" + content + Arrays.toString(e.getStackTrace()));
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
2、RSA加解密,opensll生成公钥和私钥的pem文件放在项目assets下
public static class RSAUtils {
/**
* 算法名字
*/
private static final String RSA_ALGORITHM = "RSA";
/**
* 消息摘要算法
*/
private static final String MD_ALGORITHM = "SHA256withRSA";
/**
* RSA最大加密明文大小
*/
private static final int MAX_ENCRYPT_BLOCK = 117;
/**
* RSA最大解密密文大小
*/
private static final int MAX_DECRYPT_BLOCK = 128;
/**
* 制作公钥
*
* @return
*/
@RequiresApi(api = Build.VERSION_CODES.O)
public static PublicKey getPublicKey() {
PublicKey public_Key = null;
try {
assert MyApplication.instance != null;
InputStream in = MyApplication.instance.getResources().getAssets().open("public_key.pem");
BufferedReader br = new BufferedReader(new InputStreamReader(in));
String readLine = null;
StringBuilder sb = new StringBuilder();
while ((readLine = br.readLine()) != null) {
if (readLine.charAt(0) == '-') {
continue;
} else {
sb.append(readLine);
}
}
KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM);
byte[] buffer = Base64Util.decode(sb.toString());
EncodedKeySpec keySpec = new X509EncodedKeySpec(buffer);
public_Key = keyFactory.generatePublic(keySpec);
return public_Key;
} catch (Exception e) {
}
return null;
}
/**
* 制作私钥
*
* @return
*/
@RequiresApi(api = Build.VERSION_CODES.O)
public static PrivateKey getPrivateKey() {
PrivateKey privateKey = null;
try {
assert MyApplication.instance != null;
InputStream in = MyApplication.instance.getResources().getAssets().open("private_key.pem");
BufferedReader br = new BufferedReader(new InputStreamReader(in));
String readLine = null;
StringBuilder sb = new StringBuilder();
while ((readLine = br.readLine()) != null) {
if (readLine.charAt(0) == '-') {
continue;
} else {
sb.append(readLine);
}
}
in.close();
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
byte[] buffer = Base64Util.decode(sb.toString());
EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec(buffer);
privateKey = keyFactory.generatePrivate(privateKeySpec);
return privateKey;
} catch (Exception e) {
}
return null;
}
/**
* RSA加密
* <p>
* // * @param data 待加密数据
*
* @param publicKey 公钥
* @return
*/
@RequiresApi(api = Build.VERSION_CODES.O)
public static String encrypt(String aesKey, PublicKey publicKey) {
//RSA加密
try {
if (publicKey == null) {
return null;
}
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
int inputLen = aesKey.getBytes().length;
ByteArrayOutputStream out = new ByteArrayOutputStream();
int offset = 0;
byte[] cache;
int i = 0;
// 对数据分段加密
while (inputLen - offset > 0) {
if (inputLen - offset > MAX_ENCRYPT_BLOCK) {
cache = cipher.doFinal(aesKey.getBytes(), offset, MAX_ENCRYPT_BLOCK);
} else {
cache = cipher.doFinal(aesKey.getBytes(), offset, inputLen - offset);
}
out.write(cache, 0, cache.length);
i++;
offset = i * MAX_ENCRYPT_BLOCK;
}
byte[] encryptedData = out.toByteArray();
out.close();
// 获取加密内容使用base64进行编码,并以UTF-8为标准转化成字符串
// 加密后的字符串
return Base64Util.encode(encryptedData);
} catch (Exception e) {
// log.warn("encrypt error, ex:{}", e.getMessage());
}
return null;
}
/**
* RSA解密
*
* @param data 待解密数据
* @param privateKey 私钥
* @return
*/
@RequiresApi(api = Build.VERSION_CODES.O)
public static String decrypt(String data, PrivateKey privateKey) {
try {
if (privateKey == null) {
return null;
}
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.DECRYPT_MODE, privateKey);
byte[] dataBytes = Base64Util.decode(data);
int inputLen = dataBytes.length;
ByteArrayOutputStream out = new ByteArrayOutputStream();
int offset = 0;
byte[] cache;
int i = 0;
// 对数据分段解密
while (inputLen - offset > 0) {
if (inputLen - offset > MAX_DECRYPT_BLOCK) {
cache = cipher.doFinal(dataBytes, offset, MAX_DECRYPT_BLOCK);
} else {
cache = cipher.doFinal(dataBytes, offset, inputLen - offset);
}
out.write(cache, 0, cache.length);
i++;
offset = i * MAX_DECRYPT_BLOCK;
}
out.close();
// 解密后的内容
return out.toString("UTF-8");
} catch (Exception e) {
// log.warn("decrypt error, ex:{}", e.getMessage());
}
return null;
}
/**
* 签名
*
* @param data 待签名数据
* @param privateKey 私钥
* @return 签名
*/
@RequiresApi(api = Build.VERSION_CODES.O)
public static String sign(String data, PrivateKey privateKey) {
try {
byte[] keyBytes = privateKey.getEncoded();
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM);
PrivateKey key = keyFactory.generatePrivate(keySpec);
Signature signature = Signature.getInstance(MD_ALGORITHM);
signature.initSign(key);
signature.update(data.getBytes());
return Base64Util.encode(signature.sign());
} catch (Exception e) {
// log.warn("RSA sign error, ex:{}", e.getMessage());
}
return null;
}
/**
* 验签
*
* @param srcData 原始字符串
* @param publicKey 公钥
* @param sign 签名
* @return 是否验签通过
*/
@RequiresApi(api = Build.VERSION_CODES.O)
public static boolean verify(String srcData, PublicKey publicKey, String sign) {
try {
byte[] keyBytes = publicKey.getEncoded();
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM);
PublicKey key = keyFactory.generatePublic(keySpec);
Signature signature = Signature.getInstance(MD_ALGORITHM);
signature.initVerify(key);
signature.update(srcData.getBytes());
return signature.verify(Base64Util.decode(sign));
} catch (Exception e) {
}
return false;
}
}
3、Base64Util编解码工具类 ,需要依赖javabase64-1.3.1.jar文章来源:https://www.toymoban.com/news/detail-439014.html
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import it.sauronsoftware.base64.Base64;
public class Base64Util {
/** */
/**
* 文件读取缓冲区大小
*/
private static final int CACHE_SIZE = 1024;
/** */
/**
* <p>
* BASE64字符串解码为二进制数据
* </p>
*
* @param base64
* @return
* @throws Exception
*/
public static byte[] decode(String base64) throws Exception {
return Base64.decode(base64.getBytes());
}
/** */
/**
* <p>
* 二进制数据编码为BASE64字符串
* </p>
*
* @param bytes
* @return
* @throws Exception
*/
public static String encode(byte[] bytes) throws Exception {
return new String(Base64.encode(bytes));
}
/** */
/**
* <p>
* 将文件编码为BASE64字符串
* </p>
* <p>
* 大文件慎用,可能会导致内存溢出
* </p>
*
* @param filePath
* 文件绝对路径
* @return
* @throws Exception
*/
public static String encodeFile(String filePath) throws Exception {
byte[] bytes = fileToByte(filePath);
return encode(bytes);
}
/** */
/**
* <p>
* BASE64字符串转回文件
* </p>
*
* @param filePath
* 文件绝对路径
* @param base64
* 编码字符串
* @throws Exception
*/
public static void decodeToFile(String filePath, String base64)
throws Exception {
byte[] bytes = decode(base64);
byteArrayToFile(bytes, filePath);
}
/** */
/**
* <p>
* 文件转换为二进制数组
* </p>
*
* @param filePath
* 文件路径
* @return
* @throws Exception
*/
public static byte[] fileToByte(String filePath) throws Exception {
byte[] data = new byte[0];
File file = new File(filePath);
if (file.exists()) {
FileInputStream in = new FileInputStream(file);
ByteArrayOutputStream out = new ByteArrayOutputStream(2048);
byte[] cache = new byte[CACHE_SIZE];
int nRead = 0;
while ((nRead = in.read(cache)) != -1) {
out.write(cache, 0, nRead);
out.flush();
}
out.close();
in.close();
data = out.toByteArray();
}
return data;
}
/** */
/**
* <p>
* 二进制数据写文件
* </p>
*
* @param bytes
* 二进制数据
* @param filePath
* 文件生成目录
*/
public static void byteArrayToFile(byte[] bytes, String filePath)
throws Exception {
InputStream in = new ByteArrayInputStream(bytes);
File destFile = new File(filePath);
if (!destFile.getParentFile().exists()) {
destFile.getParentFile().mkdirs();
}
destFile.createNewFile();
OutputStream out = new FileOutputStream(destFile);
byte[] cache = new byte[CACHE_SIZE];
int nRead = 0;
while ((nRead = in.read(cache)) != -1) {
out.write(cache, 0, nRead);
out.flush();
}
out.close();
in.close();
}
}
4、下面是项目中世界运用文章来源地址https://www.toymoban.com/news/detail-439014.html
一、加密
//获取AES签名
val aesKey = CipherUtils.AESUtils.getRandomString()
//加密数据
val data = CipherUtils.AESUtils.encrypt(aesKey, "需要加密的数据")
//RSA加密
val key = CipherUtils.RSAUtils.encrypt(aesKey, publicKey)
//RSA签名
val sign = CipherUtils.RSAUtils.sign(data, privateKey)
二、解密
//RSA解密
val aesKey = CipherUtils.RSAUtils.decrypt("后台返回的AES的key值", privateKey)
//AES解密
val data = CipherUtils.AESUtils.decrypt("后台返回的加密数据", aesKey)
到了这里,关于RSA+AES加解密的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!