2012-12-29 64 views
3

很抱歉,要问您关于多次提问的技巧。 我有一个关于RSA crypthography的问题。 我已经检查了有关此问题的其他主题,但我没有找到任何有用的答案。 我希望你能帮助我。RSA加密解密:BadPaddingException:数据必须以零开头

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

我其实可以: - 获取文件 的字节 - 地穴它

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

这里是我的代码:

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(); 
      } 
     } 
    } 

,我读了问题,当错误的专用密钥用于解密或Cypher支架被滥用常来。 但我不认为这是这种情况,或者我没有看到它... 我不知道我的代码有什么问题。 要运行此代码,您需要一个名为“filetocrypt”的文件。 我使用bytes [],我将它们分解为crypt和解密文件,以减轻RSA字节大小的问题。这种拆分工作正常(我试图用字符串文件,而不使用RSA算法)

感谢您的帮助!

+0

NIO总是让我困惑......为什么在你的代码结尾处有一个'byteBuffer.flip()'? – martijno

+0

我必须使用Nio,我别无选择。我不允许使用java.io.File。 我使用flip()来准备通道写入的顺序。 – user1936969

回答

6

您解密的代码需要与您的加密代码不同。有许多问题,但导致错误的原因如下。由于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); 
    } 
} 

因此,在你的解密方法中,将出现在3中的200改为?地方为256.你真的不应该硬编码这些常量。

+0

我想我明白你的意思,但我不知道如何修复它。 当我写入文件和读取文件时,是否必须使用缓冲区? 由于RSA填充和Cypher使用,我不能使用更多的245字节块...所以我怎么能达到256字节? 感谢您的帮助 – user1936969

+0

@ user1936969:不,您必须了解解密和加密方向是完全不同的。在加密时,*输入*块最多(256 - 11),*输出*总是256个字节。解密时,*输入*总是256字节,*输出*是原始明文的长度。 –

+0

对不起,我不太明白。 解密的输出长度与加密的输入相同吗? 我还是没有真正看到解密的输入可以是256字节,因为它是当我使用 返回cipher.doFinal(crypteddata); 抛出异常... 而我没有看到我必须应用这个......对不起 – user1936969