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

Uph*_*hie 1 android rsa

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

这是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);
    }
}
Run Code Online (Sandbox Code Playgroud)

以及加密和解密数据的代码段:

 //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);
Run Code Online (Sandbox Code Playgroud)

我知道原因可能是要解密的文本太长,但我只是加密"abc"然后解密加密的"abc".如果要加密或解密的文本应该比rsa私钥少11个字节,如何处理加密长文本?我该怎么做才能解决它?我是RSA的新手.

提前致谢!

Jam*_*olk 5

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

byte []→加密→byte []→Base64编码→字符串.

然后,在解密方向上,您以Base64字符串开头,您必须按顺序:

字符串→Base64解码→byte []→解密→byte []

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

至于如何加密较长的文本,请参阅亨利答案.