android之Rsa加密,分段公钥加密解密

使用RSA算法加密

老规矩先说流程:
公钥私钥,一般由服务器生成,有512位的,1024位,2048位的,前面两个已经有破解的方法,建议使用2048位的,记住这个位数。在下面分段加解密有用。公钥可以解密和加密,公钥加密私钥解密,私钥加密公钥解密,有点绕,哈哈哈,客服端放置公钥,私钥放在服务端。我们 第一步也是先加载公钥。如下是服务端的人员给的公钥, 很重要,去掉头和尾(-----BEGIN PUBLIC KEY-----)(-----END PUBLIC KEY-----),只要中间部分,并且去掉/r/n
-----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的分段加解密,网上很多误解,说最多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的文章赠送)

你可能感兴趣的:(android之Rsa加密,分段公钥加密解密)