java RSA 加密解密:BadPaddingException:数据必须从零开始

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/14085333/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-31 15:00:35  来源:igfitidea点击:

RSA Encryption-Decryption : BadPaddingException : Data must start with zero

javaencryptioncryptographyrsa

提问by user1936969

I'm sorry to ask your skills about a question asked so many times. I have an issue about RSA crypthography. I've already checked the other topics about this issue but i didn't find any helpful answer. I hope you'll be able to help me.

我很抱歉就一个被问过很多次的问题问你的技能。我有一个关于 RSA 密码学的问题。我已经检查了有关此问题的其他主题,但没有找到任何有用的答案。我希望你能帮助我。

I want to read a file, crypt its contain and then decrypt it and put these decrypted bytes in a new file.

我想读取一个文件,加密它的包含,然后解密它并将这些解密的字节放在一个新文件中。

I actually can : - get the bytes of a file - crypt it

我实际上可以: - 获取文件的字节 - 将其加密

I have the exception : javax.crypto.BadPaddingException: Data must start with zero.

我有一个例外:javax.crypto.BadPaddingException:数据必须从零开始。

Here is my code :

这是我的代码:

    package com.bodom.ghosty;

    import javax.crypto.BadPaddingException;
    import javax.crypto.Cipher;
    import javax.crypto.IllegalBlockSizeException;
    import javax.crypto.NoSuchPaddingException;
    import java.io.*;
    import java.math.BigInteger;
    import java.nio.ByteBuffer;
    import java.nio.channels.FileChannel;
    import java.nio.file.Files;
    import java.nio.file.Path;
    import java.nio.file.Paths;
    import java.security.*;
    import java.security.spec.InvalidKeySpecException;
    import java.security.spec.RSAPrivateKeySpec;
    import java.security.spec.RSAPublicKeySpec;
    import java.util.Scanner;

    public class EncryptionUtil {
        private final PrivateKey privateKey;
        private final PublicKey publicKey;

        /**
         * Build an EncryptionUtil object
         *
         * @param keyPair The KeyPair used for Ghosty
         */
        public EncryptionUtil (KeyPair keyPair) {
            this.privateKey = keyPair.getPrivate();
            this.publicKey = keyPair.getPublic();
        }


        /**
         * Generate a pair of RSA keys
         *
         * @return A keypair of RSA keys
         * @throws NoSuchAlgorithmException
         */
        private static KeyPair keyGenerate() throws NoSuchAlgorithmException {
            KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
            keyGen.initialize(2048);
            return keyGen.genKeyPair();
        }


        /**
         * Crypt data
         *
         * @param data      Data to encrypt
         * @return          The crypted data
         * @throws NoSuchAlgorithmException
         * @throws NoSuchPaddingException
         * @throws InvalidKeyException
         * @throws IllegalBlockSizeException
         * @throws BadPaddingException
         */
        private static byte[] rsaEncryption(byte[] data, EncryptionUtil encryptionUtil) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
            Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
            cipher.init(Cipher.ENCRYPT_MODE, encryptionUtil.publicKey);
            return cipher.doFinal(data);
        }


        /**
         * Read the bytes of a file
         *
         * @param file is the file to read
         * @return     the bytes of the file
         * @throws IOException
         */
        private static byte[] readBytesInFile (Path file) throws IOException {
            byte[] result = new byte[(int)Files.size(file)];
            try {
                try (InputStream inputStream = new BufferedInputStream(new FileInputStream(file.getFileName().toString()))) {
                    int bytesRead = 0;
                    while (bytesRead < result.length) {
                        int bytesLeft = result.length - bytesRead;
                        int bytesGet = inputStream.read(result, bytesRead, bytesLeft);
                        if (bytesGet > 0) {
                            bytesRead += bytesGet;
                        }
                    }
                }
            } catch (IOException e) {
                System.out.println(e);
            }
            return result;
        }


        /**
         * Encrypt a file
         *
         * @param file      Path of the file to crypt
         * @return          A byte array which contains the crypted lines of the file
         * @throws java.io.IOException
         * @throws javax.crypto.IllegalBlockSizeException
         * @throws java.security.InvalidKeyException
         * @throws javax.crypto.BadPaddingException
         * @throws java.security.NoSuchAlgorithmException
         * @throws javax.crypto.NoSuchPaddingException
         */
        public static byte[] encryption (Path file, EncryptionUtil encryptionUtil) throws IOException, IllegalBlockSizeException, InvalidKeyException, BadPaddingException, NoSuchAlgorithmException, NoSuchPaddingException {
            byte[] datatocrypt = readBytesInFile(file);
            int offset = 0;
            byte[] cryptedfile = null;

            while (offset < datatocrypt.length) {
                byte[] outputBytes;
                byte[] tmp;

                if(datatocrypt.length - offset < 200 ) {
                    outputBytes = new byte[datatocrypt.length - offset];
                    System.arraycopy(datatocrypt, offset, outputBytes, 0, datatocrypt.length - offset);

                    byte[] crypt = rsaEncryption(outputBytes, encryptionUtil);

                    tmp = cryptedfile;
                    if (tmp != null) {
                        cryptedfile = new byte[tmp.length + crypt.length];
                    }
                    else cryptedfile = new byte[crypt.length];

                    if (tmp != null) {
                        System.arraycopy(tmp, 0, cryptedfile, 0, tmp.length);
                        System.arraycopy(crypt, 0, cryptedfile, tmp.length, crypt.length);
                    }
                    else {
                        System.arraycopy(crypt, 0, cryptedfile, 0, crypt.length);
                    }
                    break;
                }

                outputBytes = new byte[200];
                System.arraycopy(datatocrypt, offset, outputBytes, 0, 200);

                byte[] crypt = rsaEncryption(outputBytes, encryptionUtil);

                tmp = cryptedfile;
                if (tmp != null) {
                    cryptedfile = new byte[tmp.length + crypt.length];
                }
                else cryptedfile = new byte[crypt.length];

                if (tmp != null) {
                    System.arraycopy(tmp, 0, cryptedfile, 0, tmp.length);
                    System.arraycopy(crypt, 0, cryptedfile, tmp.length, crypt.length);
                }
                else {
                    System.arraycopy(crypt, 0, cryptedfile, 0, crypt.length);
                }

                offset += 200 ;
            }
            return cryptedfile;
        }


        /**
         * Decrypt data
         *
         * @param crypteddata Crypted data to decrypt
         * @return            The decrypted data
         * @throws NoSuchAlgorithmException
         * @throws NoSuchPaddingException
         * @throws InvalidKeyException
         * @throws IllegalBlockSizeException
         * @throws BadPaddingException
         */
        private static byte[] rsaDecryption(byte[] crypteddata, EncryptionUtil encryptionUtil) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
            Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
            cipher.init(Cipher.DECRYPT_MODE, encryptionUtil.privateKey);
            return cipher.doFinal(crypteddata);
        }


        /**
         * Decrypt a file
         *
         * @param file       Path of the file to decrypt
         * @return           A byte array which contains the decrypted lines of the file
         * @throws IOException
         * @throws IllegalBlockSizeException
         * @throws InvalidKeyException
         * @throws BadPaddingException
         * @throws NoSuchAlgorithmException
         * @throws NoSuchPaddingException
         */
        public static byte[] decryption(Path file, EncryptionUtil encryptionUtil) throws IllegalBlockSizeException, InvalidKeyException, BadPaddingException, NoSuchAlgorithmException, NoSuchPaddingException, IOException {
            byte[] crypteddata = readBytesInFile(file);
            int offset = 0;
            byte[] decryptedfile = null;

            while (offset < crypteddata.length) {
                byte[] outputBytes;
                byte[] tmp;

                if(crypteddata.length - offset < 200 ) {
                    outputBytes = new byte[crypteddata.length - offset];
                    System.arraycopy(crypteddata, offset, outputBytes, 0, crypteddata.length - offset);

                    byte[] decrypt = rsaDecryption(outputBytes, encryptionUtil);

                    tmp = decryptedfile;
                    if (tmp != null) {
                        decryptedfile = new byte[tmp.length + decrypt.length];
                    }
                    else decryptedfile = new byte[decrypt.length];

                    if (tmp != null) {
                        System.arraycopy(tmp, 0, decryptedfile, 0, tmp.length);
                        System.arraycopy(decrypt, 0, decryptedfile, tmp.length, decrypt.length);
                    }
                    else {
                        System.arraycopy(decrypt, 0, decryptedfile, 0, decrypt.length);
                    }
                    break;
                }

                outputBytes = new byte[200];
                System.arraycopy(crypteddata, offset, outputBytes, 0, 200);

                byte[] decrypt = rsaDecryption(outputBytes, encryptionUtil);

                tmp = decryptedfile;
                if (tmp != null) {
                    decryptedfile = new byte[decrypt.length + tmp.length];
                }
                else decryptedfile = new byte[decrypt.length];

                if (tmp != null) {
                    System.arraycopy(tmp, 0, decryptedfile, 0, tmp.length);
                    System.arraycopy(decrypt, 0, decryptedfile, tmp.length, decrypt.length);
                }
                else {
                    System.arraycopy(decrypt, 0, decryptedfile, 0, decrypt.length);
                }
                offset +=200 ;
            }
            return decryptedfile;
        }

        /**
         * Save a key in a file
         *
         * @param modulus  Modulus of the key to save
         * @param exponent Exponent of the key to save
         * @param filename File used to save the keys
         * @throws IOException
         * @throws NoSuchAlgorithmException
         */
        private static void saveKeyToFile (BigInteger modulus, BigInteger exponent, String filename) throws IOException, NoSuchAlgorithmException {
            Path path = Paths.get(filename);
            if(!Files.exists(path)) {
                Files.createFile(path);
            }

            try (ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream(filename))) {
                objectOutputStream.writeObject(modulus);
                objectOutputStream.writeObject(exponent);
            }
        }

        /**
         * Save a KeyPair in two files
         *
         * @throws NoSuchAlgorithmException
         * @throws InvalidKeySpecException
         * @throws FileNotFoundException
         * @throws IOException
         */
        public static void saveKeyPair(EncryptionUtil encryptionUtil, String directorypath) throws NoSuchAlgorithmException, InvalidKeySpecException, IOException {
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");

            RSAPublicKeySpec rsaPublicKeySpec = keyFactory.getKeySpec(encryptionUtil.publicKey, RSAPublicKeySpec.class);
            saveKeyToFile(rsaPublicKeySpec.getModulus(), rsaPublicKeySpec.getPublicExponent(), directorypath + "/public.key");

            RSAPrivateKeySpec rsaPrivateKeySpec = keyFactory.getKeySpec(encryptionUtil.privateKey, RSAPrivateKeySpec.class);
            saveKeyToFile(rsaPrivateKeySpec.getModulus(), rsaPrivateKeySpec.getPrivateExponent(), directorypath + "/private.key");
        }

        /**
         * Get a PublicKey from a file
         *
         * @param filename File where the PublicKey is saved
         * @return         The PublicKey get in the file
         * @throws IOException
         * @throws ClassNotFoundException
         * @throws NoSuchAlgorithmException
         * @throws InvalidKeySpecException
         */
        private static PublicKey getPublicKeyFromFile (String filename) throws IOException, ClassNotFoundException, NoSuchAlgorithmException, InvalidKeySpecException {
            try (ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(filename))) {
                BigInteger modulus = (BigInteger) objectInputStream.readObject();
                BigInteger exponent = (BigInteger) objectInputStream.readObject();

                RSAPublicKeySpec rsaPublicKeySpec = new RSAPublicKeySpec(modulus, exponent);
                KeyFactory keyFactory = KeyFactory.getInstance("RSA");
                return keyFactory.generatePublic(rsaPublicKeySpec);
            }
        }

        /**
         * Get a PrivateKey from a file
         *
         * @param filename File where the PrivateKey is saved
         * @return         The PrivateKey get in the file
         * @throws IOException
         * @throws ClassNotFoundException
         * @throws NoSuchAlgorithmException
         * @throws InvalidKeySpecException
         */
        private static PrivateKey getPrivateKeyFromFile (String filename) throws IOException, ClassNotFoundException, NoSuchAlgorithmException, InvalidKeySpecException {
            try (ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(filename))) {
                BigInteger modulus = (BigInteger) objectInputStream.readObject();
                BigInteger exponent = (BigInteger) objectInputStream.readObject();

                RSAPrivateKeySpec rsaPrivateKeySpec = new RSAPrivateKeySpec(modulus, exponent);
                KeyFactory keyFactory = KeyFactory.getInstance("RSA");
                return keyFactory.generatePrivate(rsaPrivateKeySpec);
            }
        }

        /**
         * Get the RSA keypair from the files
         *
         * @return The Keypair which contains the public and the private key
         * @throws IOException
         * @throws ClassNotFoundException
         * @throws NoSuchAlgorithmException
         * @throws InvalidKeySpecException
         */
        public static KeyPair getKeysFromFiles (String directorypath) throws IOException, ClassNotFoundException, NoSuchAlgorithmException, InvalidKeySpecException{
            PublicKey publicKey = getPublicKeyFromFile(directorypath + "/public.key");
            PrivateKey privateKey = getPrivateKeyFromFile(directorypath + "/private.key");
            return new KeyPair(publicKey, privateKey);
        }


        public static void main(String[] args) {
            EncryptionUtil encryptionUtil = null;

            Scanner scanner = new Scanner(System.in);
            System.out.println("Path of the keys :");
            String path = scanner.nextLine();

            try {
                encryptionUtil = new EncryptionUtil(EncryptionUtil.keyGenerate());
            } catch (NoSuchAlgorithmException e) {
                e.printStackTrace();
            }

            Path directorypath = Paths.get(path);
            try {
                Files.createDirectories(directorypath);
            } catch (IOException e) {
                e.printStackTrace();
            }

            try {
                saveKeyPair(encryptionUtil, path);
            } catch (NoSuchAlgorithmException | InvalidKeySpecException| IOException e) {
                System.out.println("Error during the storage of the keys : " + e);
            }

            // Crypt part
            byte[] uncrypt;
            try {
                if (encryptionUtil != null) {
                    if (encryptionUtil.publicKey != null && encryptionUtil.privateKey != null) {
                        //Cryptage
                        byte[] crypt = encryption(Paths.get("filetocrypt"), encryptionUtil);

                        FileOutputStream fileOutputStream = new FileOutputStream("cryptedfile");
                        FileChannel channel = fileOutputStream.getChannel();
                        ByteBuffer byteBuffer = ByteBuffer.allocate(crypt.length * 2);
                        byteBuffer.put(crypt);
                        byteBuffer.flip();
                        channel.write(byteBuffer);
                        channel.close();

                        //Decryptage
                        uncrypt = decryption(Paths.get("cryptedfile"), encryptionUtil);

                        String v = new String(uncrypt);
                        System.out.println("END " + v);
                    }
                }
            } catch (InvalidKeyException | NoSuchAlgorithmException
                    | NoSuchPaddingException | IllegalBlockSizeException
                    | BadPaddingException | IOException e) {
                e.printStackTrace();
            }
        }
    }

I read that issue often comes when the wrong privatekey is used to decrypt or the Cypher is misused. But i don't think it's the case here, or i didn't see it... I have no clue about what is wrong with my code. To run this code, you need a file called "filetocrypt". I use bytes[] and I split them to crypt and decrypt the files, to dodge the size of bytes issue of RSA. This splitting works fine (I tried on files with Strings without using RSA algorithm)

我读到当使用错误的私钥解密或滥用密码时,经常会出现这个问题。但我不认为这是这里的情况,或者我没有看到......我不知道我的代码有什么问题。要运行此代码,您需要一个名为“filetocrypt”的文件。我使用 bytes[] 并将它们拆分以加密和解密文件,以避开 RSA 的字节大小问题。这种拆分工作正常(我尝试使用字符串而不使用 RSA 算法)

Thanks for your help!

谢谢你的帮助!

回答by President James K. Polk

You're decrypt code needs to be different from your encrypt code. There are a number of problems, but the one causing your error is as follows. Because of RSA padding, your input blocks are 200 bytes and your output blocks are 256 bytes. So, upon decryption, you need to read 256 bytes at a time and expect to get 200 bytes out. Instead, you are only reading 200 bytes at a time.

您的解密代码需要与您的加密代码不同。有很多问题,但导致您出错的问题如下。由于 RSA 填充,您的输入块为 200 字节,而您的输出块为 256 字节。因此,在解密时,您需要一次读取 256 个字节,并期望获得 200 个字节。相反,您一次只能读取 200 个字节。

import java.security.*;
import java.security.interfaces.*;
import javax.crypto.Cipher;

public class RsaToy {

    public static void main(String[] args) throws Exception {
        KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
        kpg.initialize(1024);
        KeyPair kp = kpg.generateKeyPair();
        RSAPublicKey pub = (RSAPublicKey) kp.getPublic();
        RSAPrivateKey priv = (RSAPrivateKey) kp.getPrivate();


        Cipher c = Cipher.getInstance("RSA/ECB/PKCS1PADDING");
        c.init(Cipher.ENCRYPT_MODE, pub);

        byte [] plain = new byte[100]; // initialize to all zero bytes

        // First encrypt: length of input (plain) is 100

        byte [] cipher = c.doFinal(plain);

        System.out.println("length of cipher is " + cipher.length);

        // Now decrypt: length of input(cipher) is 128;

        c.init(Cipher.DECRYPT_MODE, priv);

        byte [] decrypted_cipher = c.doFinal(cipher);

        System.out.println("length of decrypted cipher is " + decrypted_cipher.length);
    }
}

So, in your decryption method, change the 200 that appears in 3?? places to 256. You really shouldn't be hardcoding these constants.

所以,在你的解密方法中,把3中出现的200改掉??放置到 256。您真的不应该对这些常量进行硬编码。