RSA解密期间的javax.crypto.BadPaddingException

时间:2018-04-09 06:12:48

标签: java encryption cryptography rsa padding

在我的Java代码中,我尝试使用RSA使用公钥加密字符串。 String是Base64编码的String,表示Image(Image已转换为String)。它将使用私钥解密。

在加密期间,我首先遇到异常" javax.crypto.IllegalBlockSizeException:数据不得超过190个字节"。所以,我以189块为单位处理了String(明文),然后解析了它。

在解密期间,我得到另一个例外" javax.crypto.IllegalBlockSizeException:数据不得超过256个字节"。因此,我处理了byte [](密文),首先将其转换为String,然后将其解析为256.

同样,在我的解密过程中,我最终得到了一个" javax.crypto.BadPaddingException:解密错误"例外,我一直无法解决。

根据本网站专家的推荐,我使用了" OAEPWithSHA-256AndMGF1Padding"。我甚至尝试使用No Padding,在其他填充方法之后,看看Exception是否会消失,但它不起作用。我做错了什么?

我能够识别出该行引发了异常 - decryptedImagePartial = t.rsaDecrypt(cipherTextTrimmed.getBytes(),privateKey); - 这是主方法的解密部分。

如果我的编码习惯很差,请耐心等待。我现在真的更愿意找出异常背后的错误。

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和我用过的东西之间的区别?

2 个答案:

答案 0 :(得分:2)

无法将密文剪切成任意块!

由于你特别要求没有涉及对称算法的普通RSA(我强烈建议不要这样做!),这就是你需要做的:

  1. 找出RSA配置的maximum payload size
  2. 将您的明文拆分为此大小的块
  3. 单独加密每个块,不要简单地连接它们并丢弃块边界!
  4. 解密期间:

    1. 使用加密后的原始大小将每个密文块传递给解密函数。 请勿附加任何数据,也不要创建&#34;子串&#34;
    2. 连接生成的明文。
    3. 理想情况下,您应该使用混合加密方案:

      1. 生成加密密钥(encKey
      2. 使用encKey
      3. 的对称算法加密您的图片
      4. 使用encKey使用RSA
      5. 加密pubKey

        对称密码可以在不同的操作模式下使用,避免这种长度限制。

答案 1 :(得分:1)

首先,首先将图像编码为基数64是完全没有意义的。现代密码的输入由字节组成,图像已经是字节。如果要存储字符串,则可能需要对密文进行64编码。

输入块大小确实是190字节。您可以看到RSA / OAEP here的表格(不要忘记upvote!)。我不确定你为什么要在这种情况下使用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));
    }
}