Android java.lang.ArrayIndexOutOfBoundsException:RSA块的数据太多

Android java.lang.ArrayIndexOutOfBoundsException:RSA块的数据太多,android,rsa,Android,Rsa,我正在使用RSA加密文本和解密文本。公钥和私钥是使用openssl工具生成的。 我在解密数据时遇到了“java.lang.ArrayIndexOutOfBoundsException:RSA块的数据太多”异常 以下是RSA util类: package studio.uphie.app; import android.util.Base64; import java.security.KeyFactory; import java.security.NoSuchAlgorithmExcept

我正在使用RSA加密文本和解密文本。公钥和私钥是使用openssl工具生成的。 我在解密数据时遇到了“java.lang.ArrayIndexOutOfBoundsException:RSA块的数据太多”异常

以下是RSA util类:

package studio.uphie.app;

import android.util.Base64;

import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;

import javax.crypto.Cipher;

/**
 * Created by Uphie on 2016/4/11.
 */
public class RSA {

    private static String RSA = "RSA";

    /**
     *
     * @param text    text to be encrypted
     * @param pub_key rsa public key
     * @return encrypted data in byte-array form
     */
    public static byte[] encryptData(String text, String pub_key) {
        try {
            byte[] data = text.getBytes();
            PublicKey publicKey = getPublicKey(Base64.decode(pub_key.getBytes(), Base64.DEFAULT));

            Cipher cipher = Cipher.getInstance(RSA);
            cipher.init(Cipher.ENCRYPT_MODE, publicKey);
            return cipher.doFinal(data);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     *
     * @param text    text to be decrypted
     * @param pri_key rsa private key
     * @return
     */
    public static byte[] decryptData(String text, String pri_key) {
        try {
            byte[] data = text.getBytes();
            PrivateKey privateKey = getPrivateKey(Base64.decode(pri_key.getBytes(),Base64.DEFAULT));

            Cipher cipher = Cipher.getInstance(RSA);
            cipher.init(Cipher.DECRYPT_MODE, privateKey);
            return cipher.doFinal(data);
        } catch (Exception e) {
            //"java.lang.ArrayIndexOutOfBoundsException: too much data for RSA block" exception occurs here.
            return null;
        }
    }

    /**
     *
     * @param keyBytes
     * @return
     * @throws NoSuchAlgorithmException
     * @throws InvalidKeySpecException
     */
    public static PublicKey getPublicKey(byte[] keyBytes) throws NoSuchAlgorithmException, InvalidKeySpecException {
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(RSA);
        return keyFactory.generatePublic(keySpec);
    }

    /**
     *
     * @param keyBytes
     * @return
     * @throws NoSuchAlgorithmException
     * @throws InvalidKeySpecException
     */
    public static PrivateKey getPrivateKey(byte[] keyBytes) throws NoSuchAlgorithmException,
            InvalidKeySpecException {
        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(RSA);
        return keyFactory.generatePrivate(keySpec);
    }
}
以及加密和解密数据的代码段:

 //encrypt
 byte[] e = RSA.encryptData(text, PUBLIC_KEY);
 String result = Base64.encodeToString(e, Base64.DEFAULT);
 tv_encrypted.setText(result);

 //decrypt
 byte[] d = RSA.decryptData(text, PRIVATE_KEY);
 String result = Base64.encodeToString(d, Base64.DEFAULT);
 tv_decrypted.setText("Decrypted result:\n" + result);
我知道原因可能是要解密的文本太长,但我只是对“abc”进行加密,然后对加密的“abc”进行解密。如果要加密或解密的文本应该比rsa私钥少11字节,那么如何处理加密长文本?我怎样才能解决它?我是RSA的新手


提前谢谢

通常,您会为对称密码(如AES)生成一个随机密钥,并使用它来加密您的付费负载

然后,RSA仅用于加密此随机密钥。这不仅解决了长度问题,还具有其他一些优点:

  • 对称密码通常要快得多
  • 如果消息发送给多个接收者,只需为每个接收者专门添加加密密钥,则主要内容可以相同

    • 您的代码中缺少一些步骤,因此无法进行检查。然而,有一些线索表明存在问题。您的
      decryptData
      方法接受一个字符串参数,然后调用
      String.getBytes()
      获取数据,然后对数据进行解密。但是,加密的结果是一个字节序列,它不是任何有效字符串的编码。也许您想对输入进行base64解码,而不是调用
      getBytes()
      。一般来说,要执行解密和解码,必须反转加密和编码期间执行的步骤。因此,如果明文是字节[],则步骤如下:

      字节[]→ 加密→ 字节[]→ Base64编码→ 绳子

      然后,在以Base64字符串开始的解密方向上,必须按照以下顺序:

      串→ Base64解码→ 字节[]→ 解密→ 字节[]

      另外,另一个问题是默认值的使用,这是一个糟糕的做法,也是许多可移植性错误的根源。您在两个地方使用默认值,它们都很麻烦。首先,您使用的是默认的no-args方法,可能与one-arg构造函数相匹配。这将使用平台默认字符集,但在不同平台上可能会有所不同。因此,请始终指定一个字符集。对于大多数应用,“UTF-8”是理想的选择。其次,您正在调用
      Cipher.getInstance('RSA')
      ,而不指定填充。Oracle的Java和Android的Java将为您提供不同的填充,因此您的代码将无法在平台之间移植。始终指定完整的填充字符串。在这里,如果您需要可移植到较旧的Java实现,那么选择就不那么困难了。OAEP填充应该是您的首选,因此
      Cipher.getInstance(“RSA/ECB/OAEPWithSHA-256和mgf1padding”)可能是正确的选择。有关进一步讨论,请参阅


      至于如何加密较长的文本,请参见。

      最后,我这样修改了我的代码,它们工作得很好:

          public static String encryptData(String text, String pub_key) {
              try {
                  byte[] data = text.getBytes("utf-8");
                  PublicKey publicKey = getPublicKey(Base64.decode(pub_key.getBytes("utf-8"), Base64.DEFAULT));
                  Cipher cipher = Cipher.getInstance(RSA);
                  cipher.init(Cipher.ENCRYPT_MODE, publicKey);
                  return Base64.encodeToString(cipher.doFinal(data),Base64.DEFAULT);
              } catch (Exception e) {
                  e.printStackTrace();
                  return null;
              }
          }
          public static String decryptData(String text, String pri_key) {
              try {
                  byte[] data =Base64.decode(text,Base64.DEFAULT);
                  PrivateKey privateKey = getPrivateKey(Base64.decode(pri_key.getBytes("utf-8"),Base64.DEFAULT));
      
                  Cipher cipher = Cipher.getInstance(RSA);
                  cipher.init(Cipher.DECRYPT_MODE, privateKey);
                  return new String(cipher.doFinal(data),"utf-8");
              } catch (Exception e) {
                  return null;
              }
          }
      

      如果有什么不对劲,你还是可以提醒我。谢谢詹姆斯和亨利的回答。

      是的,是的。RSA不能加密长文本,而长文本是一种密码,通常与系统计量密码(AES或DES)一起使用,以加密系统计量密码的secrect密钥。系统计量密码加密任何文本。谢谢谢谢你的帮助!也许我从这个问题中得到了很多。Openssl生成不可读的字节序列:公钥和私钥,并将它们转换为可读的base64编码文本。加密或解密时,密码需要文本和密钥的字节码。我接受了你的建议,修改了我的rsa-util类,它工作得很好。我正在为Python服务器开发Java客户端,确切的格式化示例帮助了我很多!我花了大约2小时才找到这个,请为其他像我一样的人投票:]