为什么Java中的RSA加密会产生与C#不同长度的密文

时间:2019-07-11 02:30:49

标签: java c# encryption rsa

我正在AES使用随机生成的密钥对某些文本进行加密,然后RSA使用私钥对该密钥进行加密,以便可以将其上传到数据库。

RSA键是使用KeyPairGenerator中的Java生成的,并保存为文件。使用File.ReadAllBytes()读取密钥。

当我在Java中执行此操作时,一切正常,加密密钥始终为172 bytes,但是当我在C#中进行加密时,加密密钥始终为844 bytes。我很确定使用AES已正确加密了文本,但是RSA加密却出了问题。

我已经检查了Java和C#中的密钥大小,它们始终匹配。从字面上看,我唯一看到的区别是RSA加密密文长度,这使数据无法使用。我相信它与填充有关,但我不知道如何解决。

Java

public String encryptText(String msg, PrivateKey key) 
            throws NoSuchAlgorithmException, NoSuchPaddingException,
            UnsupportedEncodingException, IllegalBlockSizeException, 
            BadPaddingException, InvalidKeyException {

        KeyGenerator generator;   
        this.cipher.init(Cipher.ENCRYPT_MODE, key); //cipher is initialized earlier with this.cipher = Cipher.getInstance("RSA");

            try {
                generator = KeyGenerator.getInstance(AES);
                generator.init(128); // The AES key size in number of bits
                SecretKey secKey = generator.generateKey();

                Cipher aesCipher = Cipher.getInstance(AES);
                aesCipher.init(Cipher.ENCRYPT_MODE, secKey);

                String encText = Base64.getEncoder().encodeToString(aesCipher.doFinal(msg.getBytes("UTF-8")));
                String encKey = Base64.getEncoder().encodeToString(cipher.doFinal(secKey.getEncoded()));

                return "(" + encText + ")" + encKey;                
            } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | IllegalBlockSizeException | BadPaddingException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }

        return null;
    }

C#

public String EncryptText(byte[] privateKeyBytes, string msg)
        {
            try
            {
                RSACryptoServiceProvider RSA = new RSACryptoServiceProvider();
                RSAParameters RSAKeyInfo = RSA.ExportParameters(false);

                RSAKeyInfo.Modulus = privateKeyBytes;
                RSA.ImportParameters(RSAKeyInfo);

                RijndaelManaged aes = new RijndaelManaged();
                aes.BlockSize = 128;
                aes.KeySize = 128;
                aes.Mode = CipherMode.ECB;
                byte[] keyGenerated = aes.Key;

                string keyStr = Convert.ToBase64String(keyGenerated);
                byte[] keyArr = Convert.FromBase64String(keyStr);
                byte[] KeyArrBytes16Value = new byte[16];
                Array.Copy(keyArr, KeyArrBytes16Value, 16);

                aes.Key = KeyArrBytes16Value;

                ICryptoTransform encrypto = aes.CreateEncryptor();

                byte[] plainTextByte = ASCIIEncoding.UTF8.GetBytes(msg);
                byte[] CipherText = encrypto.TransformFinalBlock(plainTextByte, 0, plainTextByte.Length);

                string encText = Convert.ToBase64String(CipherText);
                string encKey = Convert.ToBase64String(RSA.Encrypt(aes.Key, true));

                return "(" + encText + ")" + encKey;

            }
            catch (CryptographicException e)
            {
                Console.WriteLine("FAILED: " + e.Message);
            }

            return null;
        }

UPDATE
感谢Henno指出问题出在我如何阅读钥匙上。我最终使用Bouncy Castle处理C#中的RSA加密。我还更改了Java代码,以使用公钥而不是私钥进行加密。

新C#

public String EncryptText(byte[] keyBytes, string msg)
        {
            try
            {
                AsymmetricKeyParameter asymmetricKeyParameter = PublicKeyFactory.CreateKey(keyBytes);
                RsaKeyParameters rsaKeyParameters = (RsaKeyParameters)asymmetricKeyParameter;
                RSAParameters rsaParameters = new RSAParameters();
                rsaParameters.Modulus = rsaKeyParameters.Modulus.ToByteArrayUnsigned();
                rsaParameters.Exponent = rsaKeyParameters.Exponent.ToByteArrayUnsigned();
                RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
                rsa.ImportParameters(rsaParameters);

                RijndaelManaged aes = new RijndaelManaged();
                aes.BlockSize = 128;
                aes.KeySize = 128;
                aes.Mode = CipherMode.ECB;
                byte[] keyGenerated = aes.Key;

                string keyStr = Convert.ToBase64String(keyGenerated);
                byte[] keyArr = Convert.FromBase64String(keyStr);
                byte[] KeyArrBytes16Value = new byte[16];
                Array.Copy(keyArr, KeyArrBytes16Value, 16);

                aes.Key = KeyArrBytes16Value;

                ICryptoTransform encrypto = aes.CreateEncryptor();

                byte[] plainTextByte = ASCIIEncoding.UTF8.GetBytes(msg);
                byte[] CipherText = encrypto.TransformFinalBlock(plainTextByte, 0, plainTextByte.Length);

                string encText = Convert.ToBase64String(CipherText);
                string encKey = Convert.ToBase64String(rsa.Encrypt(aes.Key, false));

                return "(" + encText + ")" + encKey;

            }
            catch (CryptographicException e)
            {
                Console.WriteLine("FAILED: " + e.Message);
            }

            return null;
        }

1 个答案:

答案 0 :(得分:1)

似乎出错的是,您在C#中读取了保存的“私钥文件”,大概是在变量privateKeyBytes中读取了(但是您的代码不完整,所以我猜是这样),然后执行{{ 1}},这很奇怪,而且在密码学上也不可行。您还应该根据读入的字节在C#中实例化某种RSA类,这是我认为您在C#代码开头(前四行)尝试执行的操作。我认为应该在文档中环顾四周:

RSAKeyInfo.Modulus = privateKeyBytes,然后从这些字节中设置RSAKeyInfo,但这不是模数。读取的字节应该是PKCS1格式或类似格式,可以是base64编码为文件格式,也可以是原始格式等。您必须研究一下Java用于将完整密钥导出到磁盘的格式。

您将从文件中读取的原始字节用作模数,这肯定会带来麻烦,并给出无效且太大的“密钥”。