aes rsa双重加密java,RSA和AES双向加密(Android 和 Java)

专注于Android开发,分享经验总结,欢迎加入

QQ群

接口数据加密

方案:客户端请求时数据加密,服务端解密;服务端返回数据时加密,客户端再去解密;需要两对公钥和私钥

RSA非对称加密,公钥加密,私钥去解密,AES是对称加密,由于RSA加密数据有上限,需要做分段加解密处理,在解密时很容易报错,所以用RSA公钥去加密AES,AES去加解密内容

在做的过程中遇到一些问题,特此记录

Android端加解密都正常,Java端加解密也正常,当Android和Java交互时RSA解密报错,出现乱码,原因是RSA加解密的格式不同,转byte时加上编码方式string.getBytes("utf-8")

在Android端获取加解密的Cipher类时要使用Cipher.getInstance("RSA/ECB/PKCS1Padding");

在Java端使用Cipher.getInstance("RSA")来获取

RSA Android端加密解密方式,Java端使用Cipher.getInstance("RSA")

RSA2.jpg

RSA3.jpg

核心类代码

Base64编码解码工具类

public final class Base64 {

private static final int BASELENGTH = 128;

private static final int LOOKUPLENGTH = 64;

private static final int TWENTYFOURBITGROUP = 24;

private static final int EIGHTBIT = 8;

private static final int SIXTEENBIT = 16;

private static final int FOURBYTE = 4;

private static final int SIGN = -128;

private static char PAD = '=';

private static byte[] base64Alphabet = new byte[BASELENGTH];

private static char[] lookUpBase64Alphabet = new char[LOOKUPLENGTH];

static {

for (int i = 0; i < BASELENGTH; ++i) {

base64Alphabet[i] = -1;

}

for (int i = 'Z'; i >= 'A'; i--) {

base64Alphabet[i] = (byte) (i - 'A');

}

for (int i = 'z'; i >= 'a'; i--) {

base64Alphabet[i] = (byte) (i - 'a' + 26);

}

for (int i = '9'; i >= '0'; i--) {

base64Alphabet[i] = (byte) (i - '0' + 52);

}

base64Alphabet['+'] = 62;

base64Alphabet['/'] = 63;

for (int i = 0; i <= 25; i++) {

lookUpBase64Alphabet[i] = (char) ('A' + i);

}

for (int i = 26, j = 0; i <= 51; i++, j++) {

lookUpBase64Alphabet[i] = (char) ('a' + j);

}

for (int i = 52, j = 0; i <= 61; i++, j++) {

lookUpBase64Alphabet[i] = (char) ('0' + j);

}

lookUpBase64Alphabet[62] = (char) '+';

lookUpBase64Alphabet[63] = (char) '/';

}

private static boolean isWhiteSpace(char octect) {

return (octect == 0x20 || octect == 0xd || octect == 0xa || octect == 0x9);

}

private static boolean isPad(char octect) {

return (octect == PAD);

}

private static boolean isData(char octect) {

return (octect < BASELENGTH && base64Alphabet[octect] != -1);

}

/**

* Encodes hex octects into Base64

*

* @param binaryData Array containing binaryData

* @return Encoded Base64 array

*/

public static String encode(byte[] binaryData) {

if (binaryData == null) {

return null;

}

int lengthDataBits = binaryData.length * EIGHTBIT;

if (lengthDataBits == 0) {

return "";

}

int fewerThan24bits = lengthDataBits % TWENTYFOURBITGROUP;

int numberTriplets = lengthDataBits / TWENTYFOURBITGROUP;

int numberQuartet = fewerThan24bits != 0 ? numberTriplets + 1

: numberTriplets;

char encodedData[] = null;

encodedData = new char[numberQuartet * 4];

byte k = 0, l = 0, b1 = 0, b2 = 0, b3 = 0;

int encodedIndex = 0;

int dataIndex = 0;

for (int i = 0; i < numberTriplets; i++) {

b1 = binaryData[dataIndex++];

b2 = binaryData[dataIndex++];

b3 = binaryData[dataIndex++];

l = (byte) (b2 & 0x0f);

k = (byte) (b1 & 0x03);

byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2)

: (byte) ((b1) >> 2 ^ 0xc0);

byte val2 = ((b2 & SIGN) == 0) ? (byte) (b2 >> 4)

: (byte) ((b2) >> 4 ^ 0xf0);

byte val3 = ((b3 & SIGN) == 0) ? (byte) (b3 >> 6)

: (byte) ((b3) >> 6 ^ 0xfc);

encodedData[encodedIndex++] = lookUpBase64Alphabet[val1];

encodedData[encodedIndex++] = lookUpBase64Alphabet[val2 | (k << 4)];

encodedData[encodedIndex++] = lookUpBase64Alphabet[(l << 2) | val3];

encodedData[encodedIndex++] = lookUpBase64Alphabet[b3 & 0x3f];

}

// form integral number of 6-bit groups

if (fewerThan24bits == EIGHTBIT) {

b1 = binaryData[dataIndex];

k = (byte) (b1 & 0x03);

byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2)

: (byte) ((b1) >> 2 ^ 0xc0);

encodedData[encodedIndex++] = lookUpBase64Alphabet[val1];

encodedData[encodedIndex++] = lookUpBase64Alphabet[k << 4];

encodedData[encodedIndex++] = PAD;

encodedData[encodedIndex++] = PAD;

} else if (fewerThan24bits == SIXTEENBIT) {

b1 = binaryData[dataIndex];

b2 = binaryData[dataIndex + 1];

l = (byte) (b2 & 0x0f);

k = (byte) (b1 & 0x03);

byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2)

: (byte) ((b1) >> 2 ^ 0xc0);

byte val2 = ((b2 & SIGN) == 0) ? (byte) (b2 >> 4)

: (byte) ((b2) >> 4 ^ 0xf0);

encodedData[encodedIndex++] = lookUpBase64Alphabet[val1];

encodedData[encodedIndex++] = lookUpBase64Alphabet[val2 | (k << 4)];

encodedData[encodedIndex++] = lookUpBase64Alphabet[l << 2];

encodedData[encodedIndex++] = PAD;

}

return new String(encodedData);

}

/**

* Decodes Base64 data into octects

*

* @param encoded string containing Base64 data

* @return Array containind decoded data.

*/

public static byte[] decode(String encoded) {

if (encoded == null) {

return null;

}

char[] base64Data = encoded.toCharArray();

// remove white spaces

int len = removeWhiteSpace(base64Data);

if (len % FOURBYTE != 0) {

return null;// should be divisible by four

}

int numberQuadruple = (len / FOURBYTE);

if (numberQuadruple == 0) {

return new byte[0];

}

byte decodedData[] = null;

byte b1 = 0, b2 = 0, b3 = 0, b4 = 0;

char d1 = 0, d2 = 0, d3 = 0, d4 = 0;

int i = 0;

int encodedIndex = 0;

int dataIndex = 0;

decodedData = new byte[(numberQuadruple) * 3];

for (; i < numberQuadruple - 1; i++) {

if (!isData((d1 = base64Data[dataIndex++]))

|| !isData((d2 = base64Data[dataIndex++]))

|| !isData((d3 = base64Data[dataIndex++]))

|| !isData((d4 = base64Data[dataIndex++]))) {

return null;

}// if found "no data" just return null

b1 = base64Alphabet[d1];

b2 = base64Alphabet[d2];

b3 = base64Alphabet[d3];

b4 = base64Alphabet[d4];

decodedData[encodedIndex++] = (byte) (b1 << 2 | b2 >> 4);

decodedData[encodedIndex++] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf));

decodedData[encodedIndex++] = (byte) (b3 << 6 | b4);

}

if (!isData((d1 = base64Data[dataIndex++]))

|| !isData((d2 = base64Data[dataIndex++]))) {

return null;// if found "no data" just return null

}

b1 = base64Alphabet[d1];

b2 = base64Alphabet[d2];

d3 = base64Data[dataIndex++];

d4 = base64Data[dataIndex++];

if (!isData((d3)) || !isData((d4))) {// Check if they are PAD characters

if (isPad(d3) && isPad(d4)) {

if ((b2 & 0xf) != 0)// last 4 bits should be zero

{

return null;

}

byte[] tmp = new byte[i * 3 + 1];

System.arraycopy(decodedData, 0, tmp, 0, i * 3);

tmp[encodedIndex] = (byte) (b1 << 2 | b2 >> 4);

return tmp;

} else if (!isPad(d3) && isPad(d4)) {

b3 = base64Alphabet[d3];

if ((b3 & 0x3) != 0)// last 2 bits should be zero

{

return null;

}

byte[] tmp = new byte[i * 3 + 2];

System.arraycopy(decodedData, 0, tmp, 0, i * 3);

tmp[encodedIndex++] = (byte) (b1 << 2 | b2 >> 4);

tmp[encodedIndex] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf));

return tmp;

} else {

return null;

}

} else { // No PAD e.g 3cQl

b3 = base64Alphabet[d3];

b4 = base64Alphabet[d4];

decodedData[encodedIndex++] = (byte) (b1 << 2 | b2 >> 4);

decodedData[encodedIndex++] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf));

decodedData[encodedIndex++] = (byte) (b3 << 6 | b4);

}

return decodedData;

}

/**

* remove WhiteSpace from MIME containing encoded Base64 data.

*

* @param data the byte array of base64 data (with WS)

* @return the new length

*/

private static int removeWhiteSpace(char[] data) {

if (data == null) {

return 0;

}

// count characters that's not whitespace

int newSize = 0;

int len = data.length;

for (int i = 0; i < len; i++) {

if (!isWhiteSpace(data[i])) {

data[newSize++] = data[i];

}

}

return newSize;

}

}

Base64转换工具类

/**

* Base64转换工具类

*/

public class Base64Util {

/**

* 字节数组转Base64编码

*

* @param bytes 字节数组

* @return 字节数组

*/

public static String byte2Base64(byte[] bytes) {

return Base64.encode(bytes);

}

/**

* Base64编码转字节数组

*

* @param base64Key Base64编码

* @return Base64编码

* @throws IOException

*/

public static byte[] base642Byte(String base64Key) throws IOException {

return Base64.decode(base64Key);

}

}

AES加密工具类

/**

* AES加密工具类

*

*/

public class AESUtil {

/**

* 生成AES秘钥,然后Base64编码

*

* @return Base64编码

* @throws Exception

*/

public static String genKeyAES() throws Exception {

KeyGenerator keyGen = KeyGenerator.getInstance("AES");

keyGen.init(128);

SecretKey key = keyGen.generateKey();

return Base64Util.byte2Base64(key.getEncoded());

}

/**

* 将Base64编码后的AES秘钥转换成SecretKey对象

*

* @param base64Key

* @return SecretKey对象

* @throws Exception

*/

public static SecretKey loadKeyAES(String base64Key) throws Exception {

byte[] bytes = Base64Util.base642Byte(base64Key);

return new SecretKeySpec(bytes, "AES");

}

/**

* AES加密

*

* @param source 加密内容

* @param key SecretKey对象

* @return 加密后的字节数组

* @throws Exception

*/

public static byte[] encryptAES(byte[] source, SecretKey key) throws Exception {

Cipher cipher = Cipher.getInstance("AES");

cipher.init(Cipher.ENCRYPT_MODE, key);

return cipher.doFinal(source);

}

/**

* AES解密

*

* @param source 解密内容

* @param key SecretKey对象

* @return 解密后的字节数组

* @throws Exception

*/

public static byte[] decryptAES(byte[] source, SecretKey key) throws Exception {

Cipher cipher = Cipher.getInstance("AES");

cipher.init(Cipher.DECRYPT_MODE, key);

return cipher.doFinal(source);

}

}

RSA加密工具类

/**

* RSA加密工具类

* EC和RSA的优缺点:

* RSA的优点:JDK自己支持。不需要第三方库。同时支持RSA的开发库也很多(最典型的就是OpenSSL)

* EC的缺点: 需要第三方库,支持的广度比不上RSA

* EC的优点: 1.在达到相同加密程度下,EC需要的秘钥长度比RSA要短得多

* 2.bouncycastle实现的EC加密算法,对密文长度的限制比较松。在下面的测试程序中构造了一个长字符串加密,没有报错。

* RSA的加密则是有限制的,必须分片。不过我不知道是不是bouncycastle自己事先做了分片

* 明文长度(bytes) = (加密长度/8 -11)

* 片数=(明文长度(bytes)/(密钥长度(bytes)-11))的整数部分+1,就是不满一片的按一片算

* 密文长度=密钥长度*片数

*

* 在移动端获取解密的Cipher类时要使用Cipher.getInstance("RSA/ECB/PKCS1Padding");

* 在后端使用Cipher.getInstance("RSA");来获取

*

*

*/

public class RSAUtil {

/**

* 生成RSA秘钥

*

* @return 秘钥对

* @throws Exception

*/

public static KeyPair getKeyPair() throws Exception {

KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");//加密方式

keyPairGenerator.initialize(2048);//加密长度

return keyPairGenerator.generateKeyPair();

}

/**

* 获取RSA公钥并转为Base64编码

*

* @param keyPair 秘钥对

* @return RSA公钥

*/

public static String getPublicKey(KeyPair keyPair) {

PublicKey publicKey = keyPair.getPublic();

byte[] bytes = publicKey.getEncoded();

return Base64Util.byte2Base64(bytes);

}

/**

* 获取RSA私钥并转为Base64编码

*

* @param keyPair 秘钥对

* @return RSA私钥

*/

public static String getPrivateKey(KeyPair keyPair) {

PrivateKey privateKey = keyPair.getPrivate();

byte[] bytes = privateKey.getEncoded();

return Base64Util.byte2Base64(bytes);

}

/**

* 将Base64编码后的公钥转换成PublicKey对象

*

* @param pubStr Base64编码后的公钥

* @return PublicKey公钥对象

* @throws Exception

*/

public static PublicKey string2PublicKey(String pubStr) throws Exception {

byte[] keyBytes = Base64Util.base642Byte(pubStr);

X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);

KeyFactory keyFactory = KeyFactory.getInstance("RSA");

return keyFactory.generatePublic(keySpec);

}

/**

* 将Base64编码后的私钥转换成PrivateKey对象

*

* @param priStr Base64编码后的私钥

* @return PrivateKey私钥对象

* @throws Exception

*/

public static PrivateKey string2PrivateKey(String priStr) throws Exception {

byte[] keyBytes = Base64Util.base642Byte(priStr);

PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);

KeyFactory keyFactory = KeyFactory.getInstance("RSA");

return keyFactory.generatePrivate(keySpec);

}

/**

* 公钥加密

* 在移动端获取解密的Cipher类时要使用Cipher.getInstance("RSA/ECB/PKCS1Padding");

* 在后端使用Cipher.getInstance("RSA");来获取

*

* @param content 加密的内容

* @param publicKey PublicKey公钥对象

* @return 加密后的字节数组

* @throws Exception

*/

public static byte[] publicEncrypt(byte[] content, PublicKey publicKey) throws Exception {

Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");

cipher.init(Cipher.ENCRYPT_MODE, publicKey);

return cipher.doFinal(content);

}

/**

* 私钥去解密

* 在移动端获取解密的Cipher类时要使用Cipher.getInstance("RSA/ECB/PKCS1Padding");

* 在后端使用Cipher.getInstance("RSA");来获取

*

* @param content 需要解密的内容

* @param privateKey PrivateKey私钥对象

* @return 解密后的字节数组

* @throws Exception

*/

public static byte[] privateDecrypt(byte[] content, PrivateKey privateKey) throws Exception {

Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");

cipher.init(Cipher.DECRYPT_MODE, privateKey);

return cipher.doFinal(content);

}

}

双向RSA + AES 工具类

/**

* 双向RSA + AES 工具类

*/

public class HttpEncryptUtil {

/**

* APP端加密请求内容

*

* @param action 需要加密的路径地址

* @param content 需要加密的内容

* @return 返回map

* @throws Exception

*/

public static Map appEncrypt(String action, String content) throws Exception {

LinkedHashMap map = new LinkedHashMap<>();

//将Base64编码后的(server端)公钥转换成PublicKey对象

PublicKey serverPublicKey = RSAUtil.string2PublicKey(KeyUtil.SERVER_PUBLIC_KEY.replaceAll("\n", ""));

//每次都随机生成AES秘钥

String aesKeyStr = AESUtil.genKeyAES();

SecretKey aesKey = AESUtil.loadKeyAES(aesKeyStr);

//用(server端)公钥加密AES秘钥

byte[] encryptAesKey = RSAUtil.publicEncrypt(aesKeyStr.getBytes("utf-8"), serverPublicKey);

//用AES秘钥加密路径地址

byte[] encryptActionRequest = AESUtil.encryptAES(action.getBytes("utf-8"), aesKey);

//用AES秘钥加密请求内容

byte[] encryptContentRequest = AESUtil.encryptAES(content.getBytes("utf-8"), aesKey);

map.put("ak", Base64Util.byte2Base64(encryptAesKey));//加密aesKey

map.put("act", Base64Util.byte2Base64(encryptActionRequest));//加密路径

map.put("data", Base64Util.byte2Base64(encryptContentRequest));//加密内容

LogUtils.e("----AES加密秘钥 ----" + aesKeyStr);

LogUtils.e("----AES加密数据 ---- ak==" + Base64Util.byte2Base64(encryptAesKey));

LogUtils.e("----AES加密数据 ---- act==" + Base64Util.byte2Base64(encryptActionRequest));

LogUtils.e("----AES加密数据 ---- data==" + Base64Util.byte2Base64(encryptContentRequest));

return map;

}

/**

* APP解密服务器的响应内容

*

* @param content 内容

* @return

* @throws Exception

*/

public static String appDecrypt(String content) throws Exception {

JSONObject result = new JSONObject(content);

String decryptAesKey = (String) result.get("ak");//加密后的aesKey

String decryptContent = (String) result.get("data");//内容

//将Base64编码后的APP私钥转换成PrivateKey对象

PrivateKey appPrivateKey = RSAUtil.string2PrivateKey(KeyUtil.APP_PRIVATE_KEY.replace("\n", ""));

//用APP私钥解密AES秘钥

byte[] aesKeyBytes = RSAUtil.privateDecrypt(Base64Util.base642Byte(decryptAesKey), appPrivateKey);

//用AES秘钥解密请求内容

String base64Key = new String(aesKeyBytes);

SecretKey aesKey = AESUtil.loadKeyAES(base64Key);

byte[] response = AESUtil.decryptAES(Base64Util.base642Byte(decryptContent), aesKey);

LogUtils.e("----AES解密秘钥 ----" + base64Key);

return new String(response);

}

}

代码已经上传----->>> 快速通道

你可能感兴趣的:(aes,rsa双重加密java)