java:使用从PEM文件读取的RSA公钥解密数据时,如何避免IllegalBlockSizeException?

时间:2018-08-05 11:31:16

标签: java encryption

我使用以下openssl命令生成私钥/公钥对,并使用openssl加密(短)消息:

    openssl genrsa -out /root/priv.pem
    openssl rsa -in /root/priv.pem -out /root/pub.pem -pubout
    echo "hello" | openssl rsautl -encrypt -pubin -inkey /root/pub.pem | base64 > cipher.txt

然后,我尝试使用以下Java代码解密加密的消息:

import java.io.*;
import java.nio.charset.StandardCharsets;

import java.util.Scanner;
import org.apache.commons.cli.*;
import org.apache.commons.cli.Option;

import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
//import org.bouncycastle.util.io.pem.PemReader;
import org.bouncycastle.openssl.PEMParser;
import org.bouncycastle.openssl.PEMKeyPair;
//import org.bouncycastle.openssl.PasswordFinder;
import javax.crypto.Cipher;

import java.security.Security;
import java.security.KeyPair;
import java.security.Key;
import java.security.KeyFactory;
import java.security.spec.RSAPrivateCrtKeySpec;

public class DecryptRSA {

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

      String priv_key_path = System.getProperty( "user.home" ) + "/priv.pem";
      String cypher_file_path = null;
      String cypher = "";
      String plain = null;

      /*
      * Parse the command line
      */

      Options options = new Options();

      Option cypher_file = new Option( "c", "cypher_file_path", true, "path to cypher file" );
      options.addOption( cypher_file );

      Option  priv_key = new Option( "k", "priv_key_path", true, "path to private key file" );

      options.addOption( priv_key );

      CommandLineParser clp = new DefaultParser();
      HelpFormatter helpformatter = new HelpFormatter();
      CommandLine cmd = null;

      try {

         cmd = clp.parse( options, args );

      } catch ( ParseException e ) {

         System.out.println( e.getMessage() );
         helpformatter.printHelp( "DecryptRSA", options );

         System.exit(1);

      }

      cypher_file_path = cmd.getOptionValue("c");

      /*
      * Read cypher text
      */ 

      if ( cypher_file_path == null ) {

         BufferedReader input = new BufferedReader(new InputStreamReader( System.in ) );
         String input_line = null;

     while( ( input.readLine() ) != null ) {    

        cypher += input_line;

         }

      }
      else {

         cypher = new Scanner( new File( cypher_file_path ) ).useDelimiter("\\Z").next();

      }

      System.out.println( cypher );

      String priv_key_path_tmp = cmd.getOptionValue("k");

      if ( priv_key_path_tmp != null ) {

         priv_key_path = priv_key_path_tmp;

      }

      /*
       * Decrypt the cipher text
       */

      //Security.addProvider( new BouncyCastleProvider() );
      Security.addProvider( new org.bouncycastle.jce.provider.BouncyCastleProvider() );

      File key_file = new File( priv_key_path );
      KeyPair kp = getKeyPairFromFile( key_file ); 

      byte[] cypher_bytes = cypher.getBytes( StandardCharsets.UTF_8 );

      plain = decrypt( kp.getPrivate(), cypher_bytes ); 

      System.out.println( plain );

   }



   private static KeyPair getKeyPairFromFile( File key_file ) throws IOException {

      FileReader fileReader = new FileReader( key_file );

      PEMParser pemParser = new PEMParser(new FileReader( key_file ));

      Object object = pemParser.readObject();
      JcaPEMKeyConverter converter = new JcaPEMKeyConverter().setProvider("BC");
      KeyPair kp;

      kp = converter.getKeyPair((PEMKeyPair) object);

      return kp;  

   }

   private static String decrypt( Key decrypt_key, byte[] buf ) {

    try {

       Cipher crypt_algo;
       crypt_algo = Cipher.getInstance("RSA");
       crypt_algo.init( Cipher.DECRYPT_MODE, decrypt_key );
       byte[] utf8 = crypt_algo.doFinal( buf );

       return new String( utf8, "UTF8" );

    } catch ( Exception e ) {

        e.printStackTrace();

    }

    return null;

   }
}

使用

运行时
   java DecryptRSA -c cipher.txt -k /root/priv.pem

我明白了

psOI9G6Y9oDnIJ5ru3myk18eoS6KKROzED3JYa3cwJVjfDY2q/MRSWgsaLEcAXX8Ngc9PSYXtCAS
oQkWf9hXbAbsFGnvHcbDEcB5GBssN7jfP+gtmv0meYpzk/mBAHXBV76ShA+oVor/Jw7NJPAJp32Q
NhAiT9RC/oOjSut+z94xHpRq1CfYiIit8sIvhMh8BLV/W3nJdyOsS2WlYAS3kmx9oaIsFkIIK6DP
ybLOVwRMV7Pit65o2Vts678fbis3ca+SaY9o/ZOZhu2j0YiF9DFVkVUJtFaiI3QlUzJVticTQd5p
zvrP2uFYAsEnBxX1zpOcUXI5XdvYH4UTpx4DYQ==
javax.crypto.IllegalBlockSizeException: Data must not be longer than 256 bytes
    at com.sun.crypto.provider.RSACipher.doFinal(RSACipher.java:344)
    at com.sun.crypto.provider.RSACipher.engineDoFinal(RSACipher.java:389)
    at javax.crypto.Cipher.doFinal(Cipher.java:2164)
    at DecryptRSA.decrypt(DecryptRSA.java:148)
    at DecryptRSA.main(DecryptRSA.java:117)
null

我总是会遇到这种异常,无论密钥(用长度为8192字节的密钥进行实验)还是用openssl加密的原始文本(用一个字符尝试过)多久。

我的主要问题是:

  1. 此错误消息是否真的是由于众所周知的RSA消息大小限制引起的,还是我在这里做错了什么,所以引发了相同的异常?

  2. 是否有一种方法可以轻松修改代码,以便能够使用,例如应该克服消息大小限制的AES加密RSA密钥,如何实现?

1 个答案:

答案 0 :(得分:0)

您不对base 64编码的加密消息进行解码。在解密cypher_bytes之前,请先解码。

更改

byte[] cypher_bytes = cypher.getBytes( StandardCharsets.UTF_8 );

byte[] cypher_bytes = Base64.getDecoder().decode(cypher);