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