RSA 利用分段加解密 解决IOS、Android、JAVA后台服务器 加解密数据交互安全问题

之前用的对称加密,安全方面欠妥。后来换成了RSA非对称加密,走了很多弯路。比如:
1、利用JAVA代码生成出来的秘钥对不能正确解密和加密;
2、密文加解密长度限制等等;


网上找了好多资料后终于调通了,现总结如下:
1、运用SSL工具生成IOS和JAVA能识别的证书,不懂的可以问问度娘;

2、Windows环境搭建SSL麻烦点,但是mac自带有这个,可以让有mac的兄dei生成哈就可以了;
3、生成好之后,有两种方式加载公钥和私钥 有两种方式,一是通过读取文件,二是直接写成字符串(文件里面也是字符串)。
4、调节过程遇到的坑,我已经改好了。现弄成了一个Util,代码如下:
 

需要用到的JAR:maven配置 


<dependency>
    <groupId>org.apache.xmlbeans</groupId>
    <artifactId>xmlbeans</artifactId>
    <version>2.5.0</version>
</dependency>

 

package com.common;

import java.math.BigInteger;
import java.io.ByteArrayOutputStream; 
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.RSAPrivateKeySpec;
import java.security.spec.RSAPublicKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.HashMap;
import java.util.Map;

import javax.crypto.Cipher;

import org.bouncycastle.util.encoders.Base64;

import net.sf.json.JSONObject;

public class RSAUtil {

    
    private static final String RSA = "RSA";

    private static final String PUBLIC_KEY = "public";

    private static final String PRIVATE_KEY = "private";
    
    /** 
     * RSA最大加密明文大小 
     */  
    private static final int MAX_ENCRYPT_BLOCK = 117;
    /** 
     * RSA最大解密密文大小 
     */  
    private static final int MAX_DECRYPT_BLOCK = 128; 

    /**
     * 生成公钥和私钥
     */
    public static Map<String, Object> getKeys() {
        try {
            KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(RSA);
            keyPairGen.initialize(1024);
            KeyPair keyPair = keyPairGen.generateKeyPair();
            RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
            RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
            Map<String, Object> map = new HashMap<String, Object>();
            map.put(PUBLIC_KEY, publicKey);
            map.put(PRIVATE_KEY, privateKey);
            return map;
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 使用模和指数生成RSA公钥 注意:【此代码用了默认补位方式,为RSA/None/PKCS1Padding,不同JDK默认的补位方式可能不同,如Android默认是RSA/None/NoPadding】
     * @param modulus 模
     * @param exponent 指数
     * @return
     */
    public static RSAPublicKey getPublicKey(String modulus, String exponent) {
        try {
            BigInteger b1 = new BigInteger(modulus);
            BigInteger b2 = new BigInteger(exponent);
            KeyFactory keyFactory = KeyFactory.getInstance(RSA);
            RSAPublicKeySpec keySpec = new RSAPublicKeySpec(b1, b2);
            return (RSAPublicKey) keyFactory.generatePublic(keySpec);
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (InvalidKeySpecException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 使用模和指数生成RSA私钥 注意:【此代码用了默认补位方式,为RSA/None/PKCS1Padding,不同JDK默认的补位方式可能不同,如Android默认是RSA/None/NoPadding】
     * @param modulus 模
     * @param exponent 指数
     * @return
     */
    public static RSAPrivateKey getPrivateKey(String modulus, String exponent) {
        try {
            BigInteger b1 = new BigInteger(modulus);
            BigInteger b2 = new BigInteger(exponent);
            KeyFactory keyFactory = KeyFactory.getInstance(RSA);
            RSAPrivateKeySpec keySpec = new RSAPrivateKeySpec(b1, b2);
            return (RSAPrivateKey) keyFactory.generatePrivate(keySpec);
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (InvalidKeySpecException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 公钥加密
     * @param data
     * @param publicKey
     * @return
     */
    public static String encryptByPublicKey(String data, RSAPublicKey publicKey) {
        try {
            Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
            cipher.init(Cipher.ENCRYPT_MODE, publicKey);
            // 模长
            int key_len = publicKey.getModulus().bitLength() / 8;
            // 加密数据长度 <= 模长-11
            String[] datas = splitString(data, key_len-11);
            String mi = "";
            // 如果明文长度大于模长-11则要分组加密
            for (String s : datas) {
                mi += bcd2Str(cipher.doFinal(s.getBytes()));
            }
            return mi;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
    
    /** 
     * @Title: RSAEncode 
     * @Description: 将字符串加密 
     * @param key 
     * @param data 
     * @return String 
     */  
    public static String RSAEncode(String data, RSAPublicKey publicKey) {  
        byte[] b = data.getBytes();  
        try {  
            int inputLen = b.length;  
            ByteArrayOutputStream out = new ByteArrayOutputStream();  
            int offSet = 0;  
            byte[] cache;  
            int i = 0;  
            Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");  
            cipher.init(Cipher.ENCRYPT_MODE, publicKey);  
            // 对数据分段解密  
            while (inputLen - offSet > 0) {  
                if (inputLen - offSet > MAX_ENCRYPT_BLOCK) {  
                    cache = cipher.doFinal(b, offSet, MAX_ENCRYPT_BLOCK);  
                } else {  
                    cache = cipher.doFinal(b, offSet, inputLen - offSet);  
                }  
                out.write(cache, 0, cache.length);  
                i++;  
                offSet = i * MAX_ENCRYPT_BLOCK;  
            }  
            byte[] decryptedData = out.toByteArray();  
            out.close();  
            return Base64Utils.encode(decryptedData);  
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;  
    }  
    /**
     * 私钥解密
     * @param data
     * @param privateKey
     * @return
     */
    public static String decryptByPrivateKey(String data, RSAPrivateKey privateKey) {
        try {
            Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
            cipher.init(Cipher.DECRYPT_MODE, privateKey);
            // 模长
            int key_len = privateKey.getModulus().bitLength() / 8;
            byte[] bytes = data.getBytes();
            byte[] bcd = ASCII_To_BCD(bytes, bytes.length);
            System.err.println(bcd.length);
            // 如果密文长度大于模长则要分组解密
            String ming = "";
            byte[][] arrays = splitArray(bcd, key_len);
            for (byte[] arr : arrays) {
                ming += new String(cipher.doFinal(arr));
            }
            return ming;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
    /** 
     * @Title: RSADecode 
     * @Description: 将字符串解密 
     * @param key 
     * @param encodedText 
     * @return String 
     */  
    public static String RSADecode(String data, RSAPrivateKey privateKey) {  
        try {
            byte[] b = Base64.decode(data);  
            int inputLen = b.length;  
            ByteArrayOutputStream out = new ByteArrayOutputStream();  
            int offSet = 0;  
            byte[] cache;  
            int i = 0;  
            Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");  
            cipher.init(Cipher.DECRYPT_MODE, privateKey);  
            // 对数据分段解密  
            while (inputLen - offSet > 0) {  
                if (inputLen - offSet > MAX_DECRYPT_BLOCK) {  
                    cache = cipher.doFinal(b, offSet, MAX_DECRYPT_BLOCK);  
                } else {  
                    cache = cipher.doFinal(b, offSet, inputLen - offSet);  
                }  
                out.write(cache, 0, cache.length);  
                i++;  
                offSet = i * MAX_DECRYPT_BLOCK;  
            }  
            byte[] decryptedData = out.toByteArray();  
            out.close();  
            return new String(decryptedData);  
        } catch (Exception e) {
            System.err.println("******分段解密失败!******");
            e.printStackTrace();
        }
        return null;
    }  
    /**
     * 从字符串中加载公钥
     * 
     * @param publicKeyStr
     *            公钥数据字符串
     * @throws Exception
     *             加载公钥时产生的异常
     */
    public static RSAPublicKey loadPublicKey(String publicKeyStr){
        try {
            byte[] buffer = Base64.decode(publicKeyStr);
            KeyFactory keyFactory = KeyFactory.getInstance(RSA);
            X509EncodedKeySpec keySpec = new X509EncodedKeySpec(buffer);
            return (RSAPublicKey) keyFactory.generatePublic(keySpec);
        }catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
    
    public static RSAPrivateKey loadPrivateKey(String privateKeyStr){
        try {
            byte[] buffer = Base64.decode(privateKeyStr);
            PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(buffer);
            KeyFactory keyFactory = KeyFactory.getInstance(RSA);
            return (RSAPrivateKey) keyFactory.generatePrivate(keySpec);
        }catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * ASCII码转BCD码
     */
    private static byte[] ASCII_To_BCD(byte[] ascii, int asc_len) {
        byte[] bcd = new byte[asc_len / 2];
        int j = 0;
        for (int i = 0; i < (asc_len + 1) / 2; i++) {
            bcd[i] = asc_to_bcd(ascii[j++]);
            bcd[i] = (byte) (((j >= asc_len) ? 0x00 : asc_to_bcd(ascii[j++])) + (bcd[i] << 4));
        }
        return bcd;
    }

    private static byte asc_to_bcd(byte asc) {
        byte bcd;

        if ((asc >= '0') && (asc <= '9')) bcd = (byte) (asc - '0');
        else if ((asc >= 'A') && (asc <= 'F')) bcd = (byte) (asc - 'A' + 10);
        else if ((asc >= 'a') && (asc <= 'f')) bcd = (byte) (asc - 'a' + 10);
        else bcd = (byte) (asc - 48);
        return bcd;
    }

    /**
     * BCD转字符串
     */
    private static String bcd2Str(byte[] bytes) {
        char temp[] = new char[bytes.length * 2], val;

        for (int i = 0; i < bytes.length; i++) {
            val = (char) (((bytes[i] & 0xf0) >> 4) & 0x0f);
            temp[i * 2] = (char) (val > 9 ? val + 'A' - 10 : val + '0');

            val = (char) (bytes[i] & 0x0f);
            temp[i * 2 + 1] = (char) (val > 9 ? val + 'A' - 10 : val + '0');
        }
        return new String(temp);
    }

    /**
     * 拆分字符串
     */
    private static String[] splitString(String string, int len) {
        int x = string.length() / len;
        int y = string.length() % len;
        int z = 0;
        if (y != 0) {
            z = 1;
        }
        String[] strings = new String[x + z];
        String str = "";
        for (int i = 0; i < x + z; i++) {
            if (i == x + z - 1 && y != 0) {
                str = string.substring(i * len, i * len + y);
            } else {
                str = string.substring(i * len, i * len + len);
            }
            strings[i] = str;
        }
        return strings;
    }

    /**
     * 拆分数组
     */
    private static byte[][] splitArray(byte[] data, int len) {
        int x = data.length / len;
        int y = data.length % len;
        int z = 0;
        if (y != 0) {
            z = 1;
        }
        byte[][] arrays = new byte[x + z][];
        byte[] arr;
        for (int i = 0; i < x + z; i++) {
            arr = new byte[len];
            if (i == x + z - 1 && y != 0) {
                System.arraycopy(data, i * len, arr, 0, y);
            } else {
                System.arraycopy(data, i * len, arr, 0, len);
            }
            arrays[i] = arr;
        }
        return arrays;
    }
    
    public static JSONObject getContext(String data) {
        JSONObject object=new JSONObject();
        //String publicKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCv7OU1DVIFIZU4H0+X8grUCrUeqw7tqAXQtR8BWBpStNhcyupLsqEfgQn1sBOATNuSkags4Lxa7TEdQIu1Mmgsbb4ESAsRzhhj57vcBmvvl2QWq+Mi/g+PVimdC/tV9Fbu3cN/Dy+tIs3lgdhEbzkLed40cs0qR1H7k+JYV/42pQIDAQAB";  
        String privateKey = "MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAK/s5TUNUgUhlTgfT5fyCtQKtR6rDu2oBdC1HwFYGlK02FzK6kuyoR+BCfWwE4BM25KRqCzgvFrtMR1Ai7UyaCxtvgRICxHOGGPnu9wGa++XZBar4yL+D49WKZ0L+1X0Vu7dw38PL60izeWB2ERvOQt53jRyzSpHUfuT4lhX/jalAgMBAAECgYBHFQDHnso1KIySUJRoYHPfmTP1YEh33hv7aDNO57o7ELcbRyHD1Dkl8Y2Ep9A5VtShNBkviN6SHqRpnVrVq7hLD82UmerfqhGNiC6HFa+aYeiRqVL+Pq3Hs/kH6ktc4S00+oixXRi3J8Q88wNFpRnh4ObOANkxV2/FGCox/OdIAQJBANRsxszNv3fltOvnx4JhhXJHmOwuG6jbTM1t2m2d+uvfxuMBrEZQrJZB/EcO4Sn0tmcg0zisuNsYt5C82USgHUUCQQDUA2RjlAGLZafGMQ00eBWE1NasU6j1zaS9oLsBy0QEz2un3qJQHv/s9VvvJQftQco37zK/jGP1kj3nJ9+HcdnhAkAwBD6TwNZM5dGVmZjRcfXWNTTt/iYmbxvXO8OqD5HOi5DLc29bnZTFijjaXxSqhTRN72+PWguXCulDgEykZIT5AkA45cAnSS0w79MayUc18ZQ01bnW/Yy2ir3Pdjc5wAc9Ez1dC86bmfYs7ZwCjfmWnrVaVYNVDKQ230xloLnIa7GBAkAFD14+ch9ewjr90tBeUZBncds2eTF7LF5nEU7fgvcxOCdWKQ3yeZiJJyESRBnK6Oufoz2sn7CpGp9ggvFZmkS1";
         String  context="";
         try {
             context = RsaUtil.RSADecode(data, RsaUtil.loadPrivateKey(privateKey));
        } catch (Exception e) {
            e.printStackTrace();
            context="";
            object=null;
        }finally{
            if(!"".equals(context)){
                object= JSONObject.fromObject(context);
            }else{
                object=null;
            }
        }
        return object;
    }

    public static void main(String[] args) {
        try {
            String publicKey = "自己生成秘钥对里的公钥";  
            String privateKey = "自己生成秘钥对里的私钥";
            // 明文
            //String ming = "{\"code\":\"396275\",\"time\":1542180738730,\"user\":\"lzwsnlh\"}";
            /*System.out.println("public key  :"+publicKey);
            System.out.println("private key :"+privateKey);
            // 加密后的密文
            String mi = RsaUtil.RSAEncode(ming, RsaUtil.loadPublicKey(publicKey));
            System.out.println("加密后密文:"+mi);*/
            // 解密后的明文
            String ms = "iUOwkSL9L9LFs1qi1rF2MdnAfiVCv7TUGLP6ZrakJ+OwgcP9Yvo2Hfzu5nrg2plz0cM7cM/4NLwnM+EiT7z1ttxp+An2tfSbnBb8N4h762/yQ1vowCHjX0MiLmgJPnv3snO/a81ZrirBlx9sq3u0vazd0GqaLejgmNhFEB73YSSh4NsSVwRxsyjp1fj4hO5wsRQ/LOCyK6DukeQZ8uXZZcuWaT5NWZtV0vMhMTyJ76drbKWU+hHr759uOi2jL7iNjM3tlw4EVVuDf9rOUu8yBxMFWFBhleRIyIoyKCIhx8u9woJyBvSATQ6kmlhacwOdiNbiB1/b/yUGlqf5o+00xQ==";
            String ming="";
            ming = RsaUtil.RSADecode(ms, RsaUtil.loadPrivateKey(privateKey));
            System.out.println(ming);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}

其中:Base64Utils类如下:
 

package com.common;


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 com.sun.org.apache.xml.internal.security.utils.Base64;


public class Base64Utils {
     /** *//** 
     * 文件读取缓冲区大小 
     */  
    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();  
    }  
}
 

 


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值