【问题标题】:RSA algorithm works for Android 4.3 but crash for Android 2.3.3RSA 算法适用于 Android 4.3,但适用于 Android 2.3.3
【发布时间】:2014-08-12 12:49:48
【问题描述】:

我正在尝试使用带有公钥的 RSA 算法加密文件(我使用给定的模数和指数构建)。

我的代码在 Android 4.3 上运行良好,我得到了我需要的一切。但我试图让它适用于其他 Android 版本,如(2.3.3),但没有办法。它抱怨“对于 RSA 密码而言输入太大”。 如果我已经很好地理解了这个理论,那么分组密码的大小是相对于 publicKey.size() 的,因此如果这不会从一个版本的 Android 更改为另一个版本,我应该得到相同的结果,不是吗?

这是我的代码: 包 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;
    }
}

任何建议或建议都将受到欢迎。 非常感谢。

【问题讨论】:

  • 不是直接在主题上,但我建议发布您认为可能会发出错误的一小部分代码
  • 好吧,在 Android 2.3.3 中,它第一次抱怨尝试执行 cipher.doFinal(buffer),因为“显然”缓冲区对于该密码来说太大了(输入数据也是对于 RSA 密码来说很大)。但这对我来说毫无意义,因为在 Android 4.3 中它可以完美运行。我想知道是否还有其他东西。
  • 他们是否可能使用不同版本的安全提供程序 BC?
  • 我建议您阅读developer.com/ws/android/… 并尝试那里的实现,这对于所有 API 级别都应该是正确的。
  • @IVlad 那个写了应该被枪杀和龙骨牵引的白痴,然后再次射击以确保。

标签: java android algorithm encryption rsa


【解决方案1】:

来自 Android 4.2 的增强功能:

密码学 - 修改 SecureRandom 和 Cipher.RSA 的默认实现以使用 OpenSSL。使用 OpenSSL 1.0.1 添加了对 TLSv1.1 和 TLSv1.2 的 SSL Socket 支持

这意味着他们正在使用另一个提供程序,并且可能是另一个默认 RSA 填充机制。您应该始终提供完整的算法字符串,而不是依赖于提供者默认值,例如尝试"RSA/ECB/OAEPWithSHA1AndMGF1Padding" 用于新应用程序,"RSA/ECB/PKCS1Padding" 用于与旧库的兼容性。

请注意,您通常不会直接使用 RSA 加密文件。通常,您使用随机数据密钥加密文件,例如AES,一种对称密码。然后,您使用 RSA 公钥加密该 AES 密钥,并使用密文发送结果。通过这种方式,您可以加密(几乎)任意数量的字节。这称为hybrid cryptography

【讨论】:

  • 感谢您的评论,但这不适用于我的情况。添加更多信息,我意识到 Android 2.3.3 正在使用 BouncyCastle Security Provider v1.45,而 Android 4.3 BouncyCastle Security Provider v1.48。当我要求他们实现 RSA 算法时。有什么方法可以在我的项目中使用 .jar 添加我想要的 BC 版本,并使用该版本而不是设备中的版本。
  • 仅适用于较新版本的 Android。如果我理解正确,他们现在已将 Bouncy 移出其默认命名空间(包),因此您应该能够在(圆形)Android 版本旁边使用它。否则,使用 Spongy Castle 作为第一个提供者(在不同命名空间中实现的 Bouncy Castle 提供者)。请注意,这并不会使我上面所说的无效,如果 Bouncy/Spongy 使用不同的默认值......并且 PKCS1Padding 也应该始终与默认提供程序一起使用。
  • 我也尝试过导入 SpongyCastle,但它也不起作用。如果我使用 (RSA/ECB/NoPadding) 设置我的密码,它在 Android 4.3 中工作,因为它调用 AndroidOpenSSL,但如果在 Android 2.3.3 中失败,因为它一直调用 BC。有什么方法可以将新的安全提供程序与旧版本的 android.我在这里很累,我真的不知道我还能尝试什么。
  • 填充是 RSA 安全性不可或缺的一部分。如果你把它放在外面,你就会暴露自己。将混合方法与(至少)PKCS1Padding 一起使用,您的问题就会消失。
猜你喜欢
  • 1970-01-01
  • 2012-06-21
  • 2013-01-11
  • 2012-01-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-02-07
  • 1970-01-01
相关资源
最近更新 更多