简体   繁体   English

RSA算法适用于Android 4.3,但适用于Android 2.3.3崩溃

[英]RSA algorithm works for Android 4.3 but crash for Android 2.3.3

I am trying to encrypt a file using the RSA algorithm with a public key (which I build using a given modulus and exponent). 我正在尝试使用带有公钥的RSA算法加密文件(我使用给定的模数和指数构建)。

My code works fine with Android 4.3, and I get all I need. 我的代码可以在Android 4.3上正常运行,并且可以满足我的所有需求。 But I am trying to make it work for other Android versions such (2.3.3) and there is no way. 但是我正在尝试使其适用于其他Android版本(如2.3.3),并且没有办法。 It complains about a "input too large for RSA cipher". 它抱怨“对于RSA密码输入太大”。 If I have understood the theory well the block cipher size is relative to the publicKey.size(), therefore if this don't change from one version of Android to another I should get the same result, should't I? 如果我已经很好地理解了该理论,则块密码的大小是相对于publicKey.size()的,因此,如果不从一个Android版本更改为另一个版本,我应该得到相同的结果,不是吗?

Here is my code: package com.example.rsa_ex; 这是我的代码:package com.example.rsa_ex;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigInteger;
import java.security.Key;
import java.security.KeyFactory;
import java.security.spec.RSAPublicKeySpec;
import java.util.Arrays;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;

import android.app.Activity;
import android.content.res.AssetManager;
import android.os.Bundle;
import android.util.Base64;
import android.util.Log;

public class MainActivity extends Activity 
{

    private byte[] mKeyModulus = {...};
    private byte[] mKeyExponent = {...};

    private String tag = "MainActivity";

    @Override
    protected void onCreate(Bundle savedInstanceState) 
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        String token = "";
        try
        {
            token = readTokenFromFile("token.base64");
        }
        catch (IOException e)
        {
            Log.d(tag, "Failed to open property file");
        }

        byte[] encodedBytes = Base64.encode(token.getBytes(), 0);

        Log.d(tag, "Encrypt Token"+ onEncrypt(encodedBytes));
    }

    public String readTokenFromFile(String fileName) throws IOException  
    {  
        String token = "empty";

        try
        {  
            AssetManager assetManager = getAssets();
            InputStream iS = assetManager.open(fileName);

            byte[] buffer = new byte[iS.available()];    
            iS.read(buffer);  

            ByteArrayOutputStream oS = new ByteArrayOutputStream();  

            oS.write(buffer);  
            token = oS.toString();
            oS.close();  
            iS.close(); 

            Log.d(tag, "token ==> "+token);
        }
        catch (IOException e)
        {
            Log.d(tag, "Failed to open property file");
        }

        return token; 

    } // readTokenFromFile end 
    public String onEncrypt(byte[] token)
    {   
        Log.d(tag,"onEncrypt entry");
        String encryptedTranspherable = "";//null;
        // get the publicKey
        try
        {
            BigInteger m = new BigInteger(mKeyModulus);
            BigInteger e = new BigInteger(mKeyExponent);

            KeyFactory fact = KeyFactory.getInstance("RSA");

            Key pubKey = fact.generatePublic(new RSAPublicKeySpec(m, e));

            Cipher cipher = Cipher.getInstance("RSA");
            cipher.init(Cipher.ENCRYPT_MODE, pubKey);

            byte[] encrypted = blockCipher(token,cipher);
            encryptedTranspherable = Base64.encodeToString(encrypted, Base64.DEFAULT);
        }
        catch (Exception e) 
        {
            throw new RuntimeException("serialisation error got issue here !! ==>"+ e.getMessage(), e);
        }

        return encryptedTranspherable;
    }// onEncrypt end

    private byte[] blockCipher(byte[] bytes,Cipher cipher) throws IllegalBlockSizeException, BadPaddingException
    {
        Log.d(tag,"ISecurityProvider blockCipher entry");
        Log.d(tag,"ISecurityProvider byteArray =>"+ Arrays.toString(bytes));
        byte[] scrambled = new byte[0];

        // toReturn will hold the total result
        byte[] toReturn = new byte[0];
        int length = 256; 

        // another buffer. this one will hold the bytes that have to be modified in this step
        byte[] buffer = new byte[Math.min(bytes.length, length)];//(bytes.length > length ? length : bytes.length)];
        for (int i=0; i< bytes.length; i++)
        {
            // if we filled our buffer array we have our block ready for de- or encryption
            if ((i > 0) && (i % length == 0))
            {
                Log.d(tag,"ISecurityProvider blockCipher processing block  i ="+i);
                scrambled = cipher.doFinal(buffer);
                toReturn = append(toReturn,scrambled);

                // here we calculate the length of the next buffer required
                int newlength = length;

                // if newlength would be longer than  remaining bytes in the bytes array we shorten it.
                if (i + length > bytes.length) 
                {
                    newlength = bytes.length - i;
                }
                // clean the buffer array
                buffer = new byte[newlength];
            }
            // copy byte into our buffer.
            buffer[i%length] = bytes[i];
        }

        // this step is needed if we had a trailing buffer. should only happen when encrypting.
        // example: we encrypt 110 bytes. 100 bytes per run means we "forgot" the last 10 bytes. they are in the buffer array
        scrambled = cipher.doFinal(buffer);
        // final step before we can return the modified data.
        toReturn = append(toReturn,scrambled);

        return toReturn;
    }

    private byte[] append(byte[] prefix, byte[] suffix)
    {
        byte[] toReturn = new byte[prefix.length + suffix.length];

        int prefixSize = prefix.length;
        int  suffixSize = suffix.length;

        for (int i=0; i< prefixSize; i++)
        {
            toReturn[i] = prefix[i];
        }

        for (int i=0; i< suffixSize; i++)
        {
            toReturn[i+prefixSize] = suffix[i];
        }

        return toReturn;
    }
}

Any suggestion or advise will be more than welcome. 任何建议或意见都将受到欢迎。 Thanks a lot. 非常感谢。

From Android 4.2 enhancements: 来自Android 4.2的增强功能:

Cryptography - Modified the default implementations of SecureRandom and Cipher.RSA to use OpenSSL. 加密-修改了SecureRandom和Cipher.RSA的默认实现以使用OpenSSL。 Added SSL Socket support for TLSv1.1 and TLSv1.2 using OpenSSL 1.0.1 使用OpenSSL 1.0.1为TLSv1.1和TLSv1.2添加了SSL套接字支持

This means that they are using another provider, and probably another default RSA padding mechanism. 这意味着他们正在使用另一个提供程序,并且可能正在使用另一个默认的 RSA填充机制。 You should always provide a complete algorithm string and not rely on provider defaults, eg try "RSA/ECB/OAEPWithSHA1AndMGF1Padding" for new applications and "RSA/ECB/PKCS1Padding" for compatibility with older libraries. 你应该总是提供完整的算法字符串,而不是依赖于供应商的默认设置,如尝试"RSA/ECB/OAEPWithSHA1AndMGF1Padding"为新的应用和"RSA/ECB/PKCS1Padding"以兼容旧的库。

Note that you usually do not encrypt a file directly using RSA. 请注意,您通常不直接使用RSA直接加密文件。 Usually you encrypt the file with a random data key using eg AES, a symmetric cipher. 通常,您使用AES(对称密码)使用随机数据密钥对文件进行加密。 Then you encrypt that AES key with the RSA public key and send the result with the ciphertext. 然后,您使用RSA公钥对该AES密钥进行加密,并使用密文发送结果。 This way you can encrypt an (almost) arbitrary number of bytes. 这样,您可以加密(几乎)任意数量的字节。 This is called hybrid cryptography . 这称为混合密码术

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM