RSA加解密的java实现(及中途的各种异常情况分析)


RSA加密算法是一种非对称的加密算法,主要包括以下知识点:

1、密钥对的获取:主要涉及到两个参数:秘钥格式、秘钥长度

  (1)常见秘钥格式:PKCS8(java适用)、PKCS1(其他),如果不是java平台生成的秘钥,有可能报以下错误: 

java.security.InvalidKeyException: invalid key format //秘钥格式不合法

(2)秘钥长度:1024、2048(越长越安全,效率越低),详细介绍可以参考 :https://blog.csdn.net/luoluo_onion/article/details/78354799

  注意:分段加解密时:1024的秘钥每次加密最大长度为117(128-11),长度为2048秘钥每次加解密长度为256,如果加密时设置的长度不匹配,可能会报以下错误:

javax.crypto.BadPaddingException : Decryption error //解码失败

  秘钥获取的代码实现:java.security.KeyPairGenerator提供了生成秘钥的API,只需要调用即可。

public static Map<String, Object> genKeyPair() throws Exception {
        KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(KEY_ALGORITHM);
        keyPairGen.initialize(1024);//2048
        KeyPair keyPair = keyPairGen.generateKeyPair();
        RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
        RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
        Map<String, Object> keyMap = new HashMap<String, Object>(2);
        keyMap.put(PUBLIC_KEY, publicKey);
        keyMap.put(PRIVATE_KEY, privateKey);
        return keyMap;

    }
2、keyMap总的秘钥都是 java. security.Key类型的,如要传递或者显示,可以转码为Base64,
public static String getPrivateKey(Map<String, Object> keyMap) throws Exception {
        Key key = (Key) keyMap.get(PRIVATE_KEY);
        return Base64.encodeBase64String(key.getEncoded());
    }

3、加密实现(公钥加密)

  (1)获取Cipher需要的java.security.Key类型公钥;

  (2)调用getInstance()方法获取cipher实例,参数为填充方式(加密和解密的填充方式要统一,否则解密失败!)

  (3)使用公钥初始化cipher实例,同时设置加解密类型

  (4)对数据进行分段加密,得到字节数组。

private static byte[] encryptByPublicKey(byte[] data, String publicKey) throws Exception {
        byte[] keyBytes = Base64.decodeBase64(publicKey);
        X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        Key publicK = keyFactory.generatePublic(x509KeySpec);
        // 对数据加密
        Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
        cipher.init(Cipher.ENCRYPT_MODE, publicK);
        int inputLen = data.length;
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        int offSet = 0;
        byte[] cache;
        int i = 0;
        // 对数据分段加密
        while (inputLen - offSet > 0) {
            if (inputLen - offSet > 256) {
                cache = cipher.doFinal(data, offSet, 256);
            } else {
                cache = cipher.doFinal(data, offSet, inputLen - offSet);
            }
            out.write(cache, 0, cache.length);
            i++;
            offSet = i * 256;
        }
        byte[] encryptedData = out.toByteArray();
        out.close();
        return encryptedData;

    }

 

4、解密实现

  (1)获取Cipher需要的java.security.Key类型私钥;

  (2)调用getInstance()方法获取cipher实例,参数为填充方式(加密和解密的填充方式要统一,否则解密失败!)

  (3)使用私钥初始化cipher实例,同时设置类型为解密类型

  (4)分段解密,得到字节数组。

private static byte[] decryptByPrivateKey(byte[] data, String privateKey) throws Exception {
        // 得到私钥  
        byte[] keyBytes = Base64.decodeBase64(privateKey.getBytes());
        PKCS8EncodedKeySpec pKCS8EncodedKeySpec = new PKCS8EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        Key key = keyFactory.generatePrivate(pKCS8EncodedKeySpec);
        // 解密数据
        Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
        cipher.init(Cipher.DECRYPT_MODE, key);
       // SealedObject obj = new SealedObject(data, cipher);
        int inputLen = data.length;
        System.out.println(data);
        System.out.println(inputLen);
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        int offSet = 0;
        byte[] cache;
        int i = 0;
        // 对数据分段解�?
        while (inputLen - offSet > 0) {
            if (inputLen - offSet > MAX_DECRYPT_BLOCK) {
                cache = cipher.doFinal(data, offSet, MAX_DECRYPT_BLOCK);
            } else {
                cache = cipher.doFinal(data, offSet, inputLen - offSet);
            }
            out.write(cache, 0, cache.length);
            i++;
            offSet = i * MAX_DECRYPT_BLOCK;
        }
        byte[] decryptedData = out.toByteArray();
        out.close();
        //byte[] messageBytes = (byte[]) obj.getObject(cipher);
        return decryptedData;
    }

5由于本人是首次接触RSA,并且对加密算法一片空白,所以过程中几乎把能遇见的错误碰了个遍,总结以下几个常见错误,引以为戒:

  (1)java.security.InvalidKeyException: invalid key format

    秘钥格式不合法,或者是秘钥被修改了。

  (2)java.security.spec.InvalidKeySpecException: java.security.InvalidKeyException: IOException : DerInputStream.getLength(): lengthTag=111, too big. 

    秘钥字符串主体前后有空格,或者是换行,或者有多余字段,如(-----BEGIN RSA PRIVATE KEY-----)(秘钥格式不正确)

  (3)java.security.spec.InvalidKeySpecException: java.security.InvalidKeyException: IOException : algid parse error, not a sequence

    原因可能是没有使用 PKCS8(java适用)

 

   (4)javax.crypto.BadPaddingException : Decryption error

    解密时报这个错误,很可能是填充格式不匹配,请确认加密时使用的填充格式,解密时保持统一即可,我这里使用功能的是RSA/ECB/PKCS1Padding

  (5)javax.crypto.BadPaddingException: Message is larger than modulus

    解密时出现这个错误(这个错误我是羞于启齿的),可能是因为要解密的字符串中混入了什么奇怪的东西,你可以在解密前打印下要解密的字符串,再打印下转为base64后的字节数组长度(正常应该为128的倍数),否则应该是手残多加了什么东西呢。

备注:以上内容是个人见解,疏漏之处,望各位不吝赐教!


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM