RSA加密长度限制及如何分段加密

    RSA加密长度限制问题

    RSA加解密中必须考虑到的密钥长度、明文长度和密文长度问题。明文长度需要小于密钥长度,而密文长度则等于密钥长度。因此当加密内容长度大于密钥长度时,有效的RSA加解密就需要对内容进行分段。

    这是因为,RSA算法本身要求加密内容也就是明文长度m必须0

    这样,对于1024长度的密钥。128字节(1024bits)-减去11字节正好是117字节,但对于RSA加密来讲,padding也是参与加密的,所以,依然按照1024bits去理解,但实际的明文只有117字节了。

    所以如果要对任意长度的数据进行加密,就需要将数据分段后进行逐一加密,并将结果进行拼接。同样,解码也需要分段解码,并将结果进行拼接。

    /***

    * @Descriptions rsa 加解密类

    * @Create 阿汤哥

    * @Date: 2020/6/10 14:03

    * @Company

    * @version V1.0.0

    **/

    public class RSAEncrypt {

    public static final String CHARSET = "UTF-8";

    public static final String RSA_ALGORITHM = "RSA"; //

    public static Map createKeys(int keySize) {

    // 为RSA算法创建一个KeyPairGenerator对象

    KeyPairGenerator kpg;

    try {

    kpg = KeyPairGenerator.getInstance(RSA_ALGORITHM);

    } catch (NoSuchAlgorithmException e) {

    throw new IllegalArgumentException("No such algorithm-->[" + RSA_ALGORITHM + "]");

    }

    // 初始化KeyPairGenerator对象,密钥长度

    kpg.initialize(keySize);

    // 生成密匙对

    KeyPair keyPair = kpg.generateKeyPair();

    // 得到公钥

    Key publicKey = keyPair.getPublic();

    String publicKeyStr = Base64.encodeBase64URLSafeString(publicKey.getEncoded());

    // 得到私钥

    Key privateKey = keyPair.getPrivate();

    String privateKeyStr = Base64.encodeBase64URLSafeString(privateKey.getEncoded());

    // map装载公钥和私钥

    Map keyPairMap = new HashMap();

    keyPairMap.put("publicKey", publicKeyStr);

    keyPairMap.put("privateKey", privateKeyStr);

    // 返回map

    return keyPairMap;

    }

    /**

    * 得到公钥

    * @param publicKey 密钥字符串(经过base64编码)

    * @throws Exception

    */

    public static RSAPublicKey getPublicKey(String publicKey) throws NoSuchAlgorithmException, InvalidKeySpecException {

    // 通过X509编码的Key指令获得公钥对象

    KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM);

    X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(Base64.decodeBase64(publicKey));

    RSAPublicKey key = (RSAPublicKey) keyFactory.generatePublic(x509KeySpec);

    return key;

    }

    /**

    * 得到私钥

    * @param privateKey 密钥字符串(经过base64编码)

    * @throws Exception

    */

    public static RSAPrivateKey getPrivateKey(String privateKey) throws NoSuchAlgorithmException, InvalidKeySpecException {

    // 通过PKCS#8编码的Key指令获得私钥对象

    KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM);

    PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(Base64.decodeBase64(privateKey));

    RSAPrivateKey key = (RSAPrivateKey) keyFactory.generatePrivate(pkcs8KeySpec);

    return key;

    }

    /**

    * 公钥加密

    * @param data

    * @param publicKey

    * @return

    */

    public static String publicEncrypt(String data, RSAPublicKey publicKey) {

    try {

    Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);

    cipher.init(Cipher.ENCRYPT_MODE, publicKey);

    return Base64.encodeBase64URLSafeString(rsaSplitCodec(cipher, Cipher.ENCRYPT_MODE, data.getBytes(CHARSET), publicKey.getModulus().bitLength()));

    } catch (Exception e) {

    throw new RuntimeException("加密字符串[" + data + "]时遇到异常", e);

    }

    }

    /**

    * 私钥解密

    * @param data

    * @param privateKey

    * @return

    */

    public static String privateDecrypt(String data, RSAPrivateKey privateKey) {

    try {

    Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);

    cipher.init(Cipher.DECRYPT_MODE, privateKey);

    return new String(rsaSplitCodec(cipher, Cipher.DECRYPT_MODE, Base64.decodeBase64(data), privateKey.getModulus().bitLength()), CHARSET);

    } catch (Exception e) {

    throw new RuntimeException("解密字符串[" + data + "]时遇到异常", e);

    }

    }

    /**

    * 私钥加密

    * @param data

    * @param privateKey

    * @return

    */

    public static String privateEncrypt(String data, RSAPrivateKey privateKey) {

    try {

    Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);

    //每个Cipher初始化方法使用一个模式参数opmod,并用此模式初始化Cipher对象。此外还有其他参数,包括密钥key、包含密钥的证书certificate、算法参数params和随机源random。

    cipher.init(Cipher.ENCRYPT_MODE, privateKey);

    return Base64.encodeBase64URLSafeString(rsaSplitCodec(cipher, Cipher.ENCRYPT_MODE, data.getBytes(CHARSET), privateKey.getModulus().bitLength()));

    } catch (Exception e) {

    throw new RuntimeException("加密字符串[" + data + "]时遇到异常", e);

    }

    }

    /**

    * 公钥解密

    * @param data

    * @param publicKey

    * @return

    */

    public static String publicDecrypt(String data, RSAPublicKey publicKey) {

    try {

    Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);

    cipher.init(Cipher.DECRYPT_MODE, publicKey);

    return new String(rsaSplitCodec(cipher, Cipher.DECRYPT_MODE, Base64.decodeBase64(data), publicKey.getModulus().bitLength()), CHARSET);

    } catch (Exception e) {

    throw new RuntimeException("解密字符串[" + data + "]时遇到异常", e);

    }

    }

    //rsa切割解码 , ENCRYPT_MODE,加密数据 ,DECRYPT_MODE,解密数据

    private static byte[] rsaSplitCodec(Cipher cipher, int opmode, byte[] datas, int keySize) {

    int maxBlock = 0; //最大块

    if (opmode == Cipher.DECRYPT_MODE) {

    maxBlock = keySize / 8;

    } else {

    maxBlock = keySize / 8 - 11;

    }

    ByteArrayOutputStream out = new ByteArrayOutputStream();

    int offSet = 0;

    byte[] buff;

    int i = 0;

    try {

    while (datas.length > offSet) {

    if (datas.length - offSet > maxBlock) {

    //可以调用以下的doFinal()方法完成加密或解密数据:

    buff = cipher.doFinal(datas, offSet, maxBlock);

    } else {

    buff = cipher.doFinal(datas, offSet, datas.length - offSet);

    }

    out.write(buff, 0, buff.length);

    i++;

    offSet = i * maxBlock;

    }

    } catch (Exception e) {

    throw new RuntimeException("加解密阀值为[" + maxBlock + "]的数据时发生异常", e);

    }

    byte[] resultDatas = out.toByteArray();

    IOUtils.closeQuietly(out);

    return resultDatas;

    }

    // 简单测试____________

    public static void main(String[] args) throws Exception {

    Map keyMap = createKeys(1024);

    String publicKey = keyMap.get("publicKey");

    String privateKey = keyMap.get("privateKey");

    System.out.println("公钥: \n\r" + publicKey);

    System.out.println("私钥: \n\r" + privateKey);

    System.out.println("公钥加密——私钥解密");

    String str = "这是一个明文测试字符串,如果明文太长才回分段加密";

    System.out.println("\r明文:\r\n" + str);

    System.out.println("\r明文大小:\r\n" + str.getBytes().length);

    String encodedData = publicEncrypt(str, getPublicKey(publicKey)); //传入明文和公钥加密,得到密文

    System.out.println("密文:\r\n" + encodedData);

    String decodedData = privateDecrypt(encodedData, getPrivateKey(privateKey)); //传入密文和私钥,得到明文

    System.out.println("解密后文字: \r\n" + decodedData);

    System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>");

    }

    }