-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAw6xG80XOSDJzDynpG+GoKTA2wJK4PDT64VSm7chHJRStyRYJpUPS+VagLhPb3pu7Sc6yjmzdd2GGjlAiJ/yCbSlqqTrsKUC/hdomYBPP9BG7xFgjzJzCBygU/sUgzOEMGWpl8V3ePSO6cGyvU8wgcchS6T/0goHC8gZz/kaFp0msfM0xHB+NmlmZZLF3cZowjyPhW+SmU5O5AVWyMJ29Bm/Cglt6SJ+Ex7/WGPEz8q16tPXbusJCIhrjDs7cKmWXISFfPoxgvtyOE7gbyQeHFbClqjZroAke7pMGFCmUP9/+mLFFkpVjWr3yiS6UyZr0ASxdFYhcO7w1foms/Qzg8QIDAQAB
-----END PUBLIC KEY-----
然后是使用代码加载
private static PublicKey getPublicKey(String publicKeyString) throws Exception{
String a = publicKeyString.replaceAll("\r\n", "").replaceAll("\r", "").replaceAll("\n", "");
byte[] keyBytes = Base64.decode(a .getBytes(), Base64.DEFAULT);
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
return keyFactory.generatePublic(keySpec);
}
2.加载完钥匙后,则要开始解密了,然后用base64将数据转成byte数组,再解密即可。注意,这里的填充方式是"RSA/ECB/PKCS1Padding",android里边默认的填充方式是"RSA/None/NoPadding",而服务器默认是前者,这也是导致为什么服务端加密后,客户端无法解出来的一个容易出现的问题
private static byte[] decrypt(byte[] content, PublicKey privateKey) throws Exception{
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.DECRYPT_MODE, privateKey);
return cipher.doFinal(content);
}
ok,至此就已经完成服务端用私钥加密的数据,客户端用公钥将其解开的流程,接下来看,客户端用公钥加密数据,给服务的代码。加密完后用base64将其转为string,注意编码NO_WRAP,不然又会出现服务器无法解析的情况
private static String encrypted(byte[] content, PublicKey publicKey) throws Exception {
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
btye[] enContent=cipher.doFinal(content);
return Base64.encodeToString(enContent, Base64.NO_WRAP)
}
到这里,数据不是很长的情况下呢,已经可以完美的加密解密了,但是一但数据长一些,则需要做分段加解密了
对于RSA的分段加解密,网上很多误解,说最多RSA最多可以加密多少,解密多少,这都是错误的说法。
还记得刚开始说的2048位的钥匙吗,RSA可以加解密的多大长度取决于密钥的位数
最大解密:钥匙位数 / 8 如果是2048位,则是256
最大加密:钥匙位数 / 8 - 11 如果是2048位,则是245
比如2048位的,解密的内容超过了256字节就会报下面的错误
javax.crypto.IllegalBlockSizeException: input must be under 256 bytes
下面给上完整的代码(还可以优化)
public class RSAUtils {
/**
* RSA最大解密密文大小,2048位的公钥,2048/8=256
*/
private static final int MAX_DECRYPT_BLOCK = 256;
/**
* RSA最大加密明文大小,2048位的公钥,2048/8-11=245
*/
private static final int MAX_ENCRYPT_BLOCK = 245;
//公钥自行替换掉
private static String PublicKey= "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAw6xG80XOSDJzDynpG+GoKTA2wJK4PDT64VSm7chHJRStyRYJpUPS+VagLhPb3pu7Sc6yjmzdd2GGjlAiJ/yCbSlqqTrsKUC/hdomYBPP9BG7xFgjzJzCBygU/sUgzOEMGWpl8V3ePSO6cGyvU8wgcchS6T/0goHC8gZz/kaFp0msfM0xHB+NmlmZZLF3cZowjyPhW+SmU5O5AVWyMJ29Bm/Cglt6SJ+Ex7/WGPEz8q16tPXbusJCIhrjDs7cKmWXISFfPoxgvtyOE7gbyQeHFbClqjZroAke7pMGFCmUP9/+mLFFkpVjWr3yiS6UyZr0ASxdFYhcO7w1foms/Qzg8QIDAQAB" ;
/**
* 加载公钥
*/
private static PublicKey getPublicKey() throws NoSuchAlgorithmException, InvalidKeySpecException {
String key = PublicKey.replaceAll("\r\n", "").replaceAll("\r", "").replaceAll("\n", "");
byte[] keyBytes = Base64.decode(key.getBytes(), Base64.DEFAULT);
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
return keyFactory.generatePublic(keySpec);
}
/**
* 用RSA公钥解密
*
* @param data 要解密的数据
* @return 解密数据
*/
public static byte[] decryptByRSA1(byte[] data) throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchPaddingException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException, IOException {
PublicKey pubKey = getPublicKey();
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.DECRYPT_MODE, pubKey);
int blockCount = (data.length / MAX_DECRYPT_BLOCK);
if ((data.length % MAX_DECRYPT_BLOCK) != 0) {
blockCount += 1;
}
ByteArrayOutputStream bos = new ByteArrayOutputStream(blockCount * MAX_DECRYPT_BLOCK);
for (int offset = 0; offset < data.length; offset += MAX_DECRYPT_BLOCK) {
int inputLen = (data.length - offset);
if (inputLen > MAX_DECRYPT_BLOCK) {
inputLen = MAX_DECRYPT_BLOCK;
}
byte[] decryptedBlock = cipher.doFinal(data, offset, inputLen);
bos.write(decryptedBlock);
}
bos.close();
return bos.toByteArray();
}
/**
* 此方法为解密入口
* @param data 要解密的json字符串
* @return 解密后的字符串
*/
public static String base64Decrypt(String data) {
byte[] encryptedBytes = {};
try {
encryptedBytes = decryptByRSA1(Base64.decode(data.getBytes(), Base64.DEFAULT));
} catch (Exception e) {
e.printStackTrace();
}
return new String(encryptedBytes);
}
/**
* 此方法为加密入口
* @param data 要加密的json字符串
* @return 加密后的字符串
*/
public static String base64Encrypted(String data) {
byte[] encryptedBytes = {};
try {
encryptedBytes = encrypted(data.getBytes());
} catch (Exception e) {
e.printStackTrace();
}
return Base64.encodeToString(encryptedBytes, Base64.NO_WRAP);
}
private static byte[] encrypted(byte[] data) throws Exception {
PublicKey publicKey = getPublicKey();
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
//分段加密
int blockCount = (data.length / MAX_ENCRYPT_BLOCK);
if ((data.length % MAX_ENCRYPT_BLOCK) != 0) {
blockCount += 1;
}
ByteArrayOutputStream bos = new ByteArrayOutputStream(blockCount * MAX_ENCRYPT_BLOCK);
for (int offset = 0; offset < data.length; offset += MAX_ENCRYPT_BLOCK) {
int inputLen = (data.length - offset);
if (inputLen > MAX_ENCRYPT_BLOCK) {
inputLen = MAX_ENCRYPT_BLOCK;
}
byte[] encryptedBlock = cipher.doFinal(data, offset, inputLen);
bos.write(encryptedBlock);
}
bos.close();
return bos.toByteArray();
}
}
最后还要感谢以下文章,真的帮了很多,希望后来的人们也可以更少踩坑
https://www.cnblogs.com/yulibostu/articles/9859033.html
https://www.jianshu.com/p/44b3f5f03c78
https://blog.csdn.net/leedaning/article/details/51780511(如果你的后台不明白,还有php的文章赠送)