簡體   English   中英

RSA解密期間的javax.crypto.BadPaddingException

[英]javax.crypto.BadPaddingException during RSA Decryption

在我的Java代碼中,我試圖使用帶有公開密鑰的RSA加密字符串。 該字符串是表示圖像的Base64編碼的字符串(圖像已轉換為字符串)。 它將使用私鑰解密。

在加密期間,我首先收到一個異常“ javax.crypto.IllegalBlockSizeException:數據不得超過190個字節”。 因此,我以189的塊形式處理了字符串(純文本),然后對其進行了解析。

在解密期間,我得到了另一個異常“ javax.crypto.IllegalBlockSizeException:數據不得超過256個字節”。 因此,我先將byte [](密文)以256塊的形式轉換為String,然后再將其解析,從而處理了它。

同樣,在解密過程中,我最終遇到了“ javax.crypto.BadPaddingException:解密錯誤”異常,但我無法解決。

根據該站點專家的建議,我使用了“ OAEPWithSHA-256AndMGF1Padding”。 在其他填充方法之后,我什至嘗試使用No Padding,以查看Exception是否會消失,但是它不起作用。 我做錯了什么?

我能夠確定在行上拋出了異常-解密的圖像部分= t.rsaDecrypt(cipherTextTrimmed.getBytes(),privateKey); -位於main方法的解密部分中。

如果我的編碼實踐不佳,請多多包涵。 我真的很想現在才找出異常背后的錯誤。

import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;

public class Tester
{

    public KeyPair buildKeyPair() throws NoSuchAlgorithmException 
    {
        final int keySize = 2048;
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
        keyPairGenerator.initialize(keySize);
        return keyPairGenerator.genKeyPair();   
    }

    public byte[] encrypt(PublicKey publicKey, String message) throws Exception
    {
        Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-256AndMGF1Padding");  
        cipher.init(Cipher.ENCRYPT_MODE, publicKey);  
        return cipher.doFinal(message.getBytes());  
    }

    public String decrypt(PrivateKey privateKey, byte [] encrypted) throws Exception
    { 
        Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-256AndMGF1Padding");  
        cipher.init(Cipher.DECRYPT_MODE, privateKey);
        return new String(cipher.doFinal(encrypted));
    }

    public byte[] rsaEncrypt(String watermarkMsg, PublicKey publicKey) throws Exception
    {
        byte[] cipherText = encrypt(publicKey, watermarkMsg);
        return cipherText;
    }

    public String rsaDecrypt(byte[] cipherText, PrivateKey privateKey) throws Exception
    {
        String plainText = decrypt(privateKey, cipherText);
        return plainText;
    }

    public static void main(String args[]) throws NoSuchAlgorithmException
    {
        Tester t = new Tester();

        String inputImageFilePath = "<file_path_here";
        String stringOfImage = null;
        byte[] encryptedImage = null;
        byte[] encryptedImagePartial = null;
        KeyPair keyPair = t.buildKeyPair();
        PublicKey pubKey = keyPair.getPublic();
        PrivateKey privateKey = keyPair.getPrivate()

        //-----------IMAGE TO STRING CONVERSION----------------
        //The imagetostring() function retrieves the image at the file path and converts it into a Base64 encoded String  
        try
        {
            stringOfImage = t.imagetostring(inputImageFilePath);
        }
        catch(Exception e)
        {
            System.out.println(e.toString());
        }

        //-----------ENCRYPTION OF STRING----------------
        //The encryption is done in blocks of 189, because earlier I got an exception - "javax.crypto.IllegalBlockSizeException: Data must not be longer than 190 bytes"
        try
        {
            String plaintext = stringOfImage;
            String plaintextTrimmed = "";
            System.out.println(stringOfImage);
            encryptedImage = new byte[15512];    //The size is given as 15512 because the length of the particular string was found to be 15512
            while(plaintext!="")
            {
                if(plaintext.length()>189)
                {
                    plaintextTrimmed = plaintext.substring(0, 189);
                    plaintext = plaintext.substring(189);
                }
                else
                {
                    plaintextTrimmed = plaintext;
                    plaintext = "";
                }
                encryptedImagePartial = t.rsaEncrypt(plaintextTrimmed, pubKey);
                encryptedImage = t.concatenate(encryptedImage, encryptedImagePartial);
                System.out.println(encryptedImage.length);
            }

        }
        catch(Exception e)
        {
            System.out.println(e.toString());
        }
        t.byteDigest(encryptedImage);

        //-----------DECRYPTION OF STRING--------------
        //The decryption is done in blocks of 189, because earlier I got an exception - "javax.crypto.IllegalBlockSizeException: Data must not be longer than 256 bytes"
        try
        {
            // The ciphertext is located in the variable encryptedImage which is a byte[]
            String stringRepOfCipherText = new String(encryptedImage);              String cipherTextTrimmed = "";
            String decryptedImagePartial;
            String decryptedImage = "";
            while(stringRepOfCipherText!="")
            {
                if(stringRepOfCipherText.length()>189)
                {
                    cipherTextTrimmed = stringRepOfCipherText.substring(0, 189);
                    stringRepOfCipherText = stringRepOfCipherText.substring(189);
                }
                else
                {
                    cipherTextTrimmed = stringRepOfCipherText;
                    stringRepOfCipherText = "";
                }
                decryptedImagePartial = t.rsaDecrypt(cipherTextTrimmed.getBytes(), privateKey);
                decryptedImage = decryptedImage + decryptedImagePartial;
            }
        }
        catch(BadPaddingException e)
        {
            System.out.println(e.toString());
        }
        catch(Exception e)
        {
            System.out.println(e.toString());
        }
    }
}

此外,我注意到其他一些使用KeyFactory生成密鑰的示例。 誰能告訴我使用KeyFactory和我使用過的東西之間的區別嗎?

不能將密文切成任意的塊!

由於您專門要求不涉及對稱算法的普通RSA(我強烈建議您反對!),因此需要執行以下操作:

  1. 找出RSA配置的最大有效負載大小
  2. 將您的純文本分割成這種大小的塊
  3. 單獨加密每個塊, 不要簡單地將它們串聯起來並丟棄塊邊界!

解密期間:

  1. 使用加密后的原始大小將每個密文塊傳遞給解密函數。 不要追加任何數據,也不要創建“ substrings”
  2. 連接結果純文本。

理想情況下,您應該使用混合加密方案:

  1. 生成一個加密密鑰( encKey
  2. 使用帶有encKey的對稱算法加密圖像
  3. 使用帶有RSA的pubKey加密encKey

可以在不同的操作模式中使用對稱密碼,從而避免了這種長度限制。

首先,首先將圖像編碼為基數64是絕對沒有意義的。現代密碼的輸入由字節組成,圖像已經是字節。 如果您要存儲一個字符串,則可能需要對密文進行64位編碼。

輸入塊的大小確實為190字節。 您可以在此處看到RSA / OAEP的表格(別忘了投票!)。 我不確定在這種情況下為什么要使用189。 但是我的代碼是通用的。 輸出塊大小只是RSA的密鑰大小,因為它已顯式轉換為以字節為單位的密鑰大小(即使可能更小)。

在解密過程中,您將密文轉換為字符串。 但是,Java中的字符串解碼是有損的; 如果解碼器找到了一個不代表字符的字節,那么它會被靜默丟棄。 因此,這將不會(始終有效),例如會導致BadPaddingException 沒關系,我們可以保留二進制密文。

因此,事不宜遲,請看一些代碼。 請注意,以每塊66個字節的形式擴展密文,並且主要是解密的性能較差。 強烈建議在混合密碼系統中將AES與RSA結合使用(對於這個問題,這不是第一次)。

import java.io.ByteArrayOutputStream;
import java.nio.charset.StandardCharsets;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.util.Arrays;

import javax.crypto.Cipher;

public class Tester {

    private static final int KEY_SIZE = 2048;
    private static final int OAEP_MGF1_SHA256_OVERHEAD = 66;

    public static KeyPair buildKeyPair() throws NoSuchAlgorithmException {
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
        keyPairGenerator.initialize(KEY_SIZE);
        return keyPairGenerator.generateKeyPair();
    }

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

        KeyPair keyPair = Tester.buildKeyPair();
        RSAPublicKey pubKey = (RSAPublicKey) keyPair.getPublic();
        RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();

        // assumes the bitLength is a multiple of 8 (check first!)
        int keySizeBytes = pubKey.getModulus().bitLength() / Byte.SIZE;

        byte[] image = new byte[1000];
        Arrays.fill(image, (byte) 'm'); 

        // --- encryption

        final Cipher enc;
        try {
            enc = Cipher.getInstance("RSA/ECB/OAEPWithSHA-256AndMGF1Padding");
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException("OAEP with MGF-1 using SHA-256 not available in this runtime", e);
        }
        enc.init(Cipher.ENCRYPT_MODE, pubKey);

        int fragmentsize = keySizeBytes - OAEP_MGF1_SHA256_OVERHEAD;

        ByteArrayOutputStream ctStream = new ByteArrayOutputStream();
        int off = 0;
        while (off < image.length) {
            int toCrypt = Math.min(fragmentsize, image.length - off);
            byte[] partialCT = enc.doFinal(image, off, toCrypt);
            ctStream.write(partialCT);
            off += toCrypt;
        }

        byte[] ct = ctStream.toByteArray();

        // --- decryption

        Cipher dec = Cipher.getInstance("RSA/ECB/OAEPWithSHA-256AndMGF1Padding");
        dec.init(Cipher.DECRYPT_MODE, privateKey);

        ByteArrayOutputStream ptStream = new ByteArrayOutputStream();
        off = 0;
        while (off < ct.length) {
            int toCrypt = Math.min(keySizeBytes, ct.length - off);
            byte[] partialPT = dec.doFinal(ct, off, toCrypt);
            ptStream.write(partialPT);
            off += toCrypt;
        }

        byte[] pt = ptStream.toByteArray();

        // mmmm...
        System.out.println(new String(pt, StandardCharsets.US_ASCII));
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM