当前位置: 首页 > 知识库问答 >
问题:

Java Bouncy Castle的块式RSA加密

殷学
2023-03-14

在C#中,我正在通过如下操作加密文本数据(请注意,我正在以块(块)形式加密数据):

        public string EncryptData(string publicKey, string data)
        {
            try
            {
                var bytesToEncrypt = Encoding.UTF8.GetBytes(data);
                int srclen = bytesToEncrypt.Length;

                //Prepare encryption engine
                var encryptEngine = new Pkcs1Encoding(new RsaEngine());

                //Initialize Key
                using (var txtreader = new StringReader(publicKey))
                {
                    var keyParameter = (AsymmetricKeyParameter)new PemReader(txtreader).ReadObject();
                    encryptEngine.Init(true, keyParameter);
                }

                //Encrypt in loop
                byte[] complete = new byte[0];
                int src_block_size = encryptEngine.GetInputBlockSize();
                for (int idx = 0; idx < srclen; idx += src_block_size)
                {
                    int data_len = srclen - idx;
                    if (data_len > src_block_size)
                    {
                        data_len = src_block_size;
                    }

                    var encryptedChunk = encryptEngine.ProcessBlock(bytesToEncrypt, idx, data_len);
                    complete = CombineByteArrays(complete, encryptedChunk);
                }

                var finalString = Convert.ToBase64String(complete);
                return finalString;
            }
            catch (InvalidCipherTextException)
            {
                //catch exception
            }
        }

现在,我需要提供相同的加密逻辑给一些Java的家伙(我根本不熟悉Java)。现在他们是这样做的:

public static byte[] encrypt(byte[] text, PublicKey key) throws Exception
    {
        byte[] cipherText = null;
        //
        // get an RSA cipher object and print the provider
        Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");

        // encrypt the plain text using the public key
        cipher.init(Cipher.ENCRYPT_MODE, key);
        cipherText = cipher.doFinal(text);
        return cipherText;
    }

因此,有时解密(发生在基于.NET的服务器上)会失败,并出现错误“输入太大而不是RSA密码”。所以我怀疑这可能是因为加密和解密数据的逻辑不同(加密发生在基于Java的客户端上,逻辑如下所示,解密发生在基于.NET的客户端上,逻辑如下):

public string DecryptData(string privateKey, string base64Data)
        {
            try
            {
                var bytesToDecrypt = Convert.FromBase64String(base64Data);
                AsymmetricCipherKeyPair keyPair;
                //var internalEngine = new RsaEngine();
                var decryptEngine = new RsaEngine(); //No paddind. We'll hunt the data inside packets
                using (var txtreader = new StringReader(privateKey))
                {
                    keyPair = (AsymmetricCipherKeyPair)new PemReader(txtreader).ReadObject();
                    decryptEngine.Init(false, keyPair.Private);
                }

                //Loop por todo el bloque y saca data
                byte[] complete = new byte[0];
                int blockSize = decryptEngine.GetInputBlockSize();
                for (int chunkPosition = 0; chunkPosition < bytesToDecrypt.Length; chunkPosition += blockSize)
                {
                    //int chunkSize = Math.Min(blockSize, bytesToDecrypt.Length - ((chunkPosition / blockSize) * blockSize));
                    int chunkSize = bytesToDecrypt.Length - chunkPosition;
                    if (chunkSize > blockSize)
                    {
                        chunkSize = blockSize;
                    }

                    var decryptedChunk = decryptEngine.ProcessBlock(bytesToDecrypt, chunkPosition, chunkSize);

                    //the actual decrypted data is in the middle, locate it!
                    int idxFirstZero = -1;
                    int outlen = decryptedChunk.Length;
                    int idxNextZero = (int)outlen;
                    for (int i = 0; i < outlen; i++)
                    {
                        if (decryptedChunk[i] == 0)
                        {
                            if (idxFirstZero < 0)
                            {
                                idxFirstZero = i;
                            }
                            else
                            {
                                idxNextZero = i;
                                break;
                            }
                        }
                    }

                    var totalSizeToCopy = idxNextZero - idxFirstZero - 1;
                    Array.Resize(ref complete, complete.Length + totalSizeToCopy);
                    int dstOffset = complete.Length - totalSizeToCopy > 0 ? complete.Length - totalSizeToCopy : 0;
                    Buffer.BlockCopy(decryptedChunk, idxFirstZero + 1, complete, dstOffset, totalSizeToCopy);
                }

                var finalString = Encoding.UTF8.GetString(complete).Trim('\0');
                return finalString;
            }
            catch (InvalidCipherTextException)
            {

            }
        }

如你所见,我正在对数据进行块解密。所以我的问题是,我们如何在Java(使用bouncy castle)中进行块式加密(就像我在文章的第一段代码中在.NET中所做的那样)?

共有1个答案

邵伟泽
2023-03-14

这不是一个常见的操作,因为对于大数据(甚至更小的数据),通常使用混合加密,其中使用对称密码的操作模式(例如CBC)来加密较大的数据对象。

据我所知,没有直接的方法从cipher实例请求RSA/PKCS#1的最大输入大小。

然而,这并不是一个大问题,因为您可以在给定RSA密钥大小的情况下自己计算这个值。并且该密钥大小与模数的大小(以字节为单位)相同:

  1. 以字节为单位计算模数大小:(keysize-1)/byte.size+1或者,如果keysize是8的倍数(像往常一样),当然只需keysize/byte.size
  2. 从结果中减去11个字节(对于PKCS#1 v1.5填充);

要获得keysize,只需将密钥强制转换为RSAPublickey,然后调用GetModulus(),然后在生成的BigInteger上调用BitLengts()

注意,发送没有完整性保护(例如签名)的密文不被认为是非常安全的。PKCS#1也可能成为填充oracle攻击的牺牲品,特别是在传输模式安全中使用时。RSA/OAEP将是一个更好的选择,用于混合加密(因为RSA/OAEP将存储更少)。

 类似资料:
  • 我在C#中有一个传递公钥字符串的方法。 我传递的公钥字符串是 我得到一个错误组织。BouncyCastle。安全InvalidKeyException:“不是RSA密钥”。我传递给该方法的公钥格式是否不正确?

  • 在本章中,我们将重点介绍RSA密码加密的不同实现及其所涉及的功能。 您可以引用或包含此python文件以实现RSA密码算法实现。 加密算法包含的模块如下 - from Crypto.PublicKey import RSA from Crypto.Cipher import PKCS1_OAEP from Crypto.Signature import PKCS1_v1_5 from Crypto

  • 我正在检查一个用Python编写的代码,它用来生成一个RSA公钥对。 它会生成keypair,但是在代码的末尾,它会再次运行ssh-keygen。我不知道为什么这样做。因为,rsa.generate()本身将生成keypair,我们将其导出到两个单独的文件中。为什么需要再次运行ssh-keygen? 下面是代码中的几行: 文件pub将存储公钥,id_rsa将存储来自rsa.generate()函数

  • 所以,接下来我需要的是: 创建用于开发的证书,分别为客户端和服务器获取一个证书 通过从客户端编码并在服务器上解码的API检索密码 现在,我按照这个链接创建了certifiactes。那里的女孩一步一步地给出了如何获得自己签名的证书、将其存放在商店等方面的指导。现在,我有问题的部分是: 真的会有人帮我。

  • 问题内容: 我试图来回编码一个简单的String“ test”。 但是,尽管加密工作得很好(ALGORITHM是“ RSA”),但是当尝试解密刚刚从加密“ test”中获得的字符串时,出现以下异常: javax.crypto.IllegalBlockSizeException:数据不得超过256个字节 我是否应该将加密的字节分成256个块才能解密? 问题答案: 您无法可靠地将随机字节转换为。结果将

  • 我们封装了一个RSA 加解密的工具放在 extends 中。首先看看它的文件结构 rsa |-- RSACrypt 加解密主程序 |-- RSACryptBigData 大数据加解密 |-- SignUtil 签名类 |-- rsa_public_key.pem 公钥 |-- rsa_private_key.pem 私钥 RSACrypt API RSAC