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 // 为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.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 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(">>>>>>>>>>>>>>>>>>>>>>>>>>"); } }