AES 256文本加密返回不同的值

时间:2019-03-21 23:38:33

标签: java c# encryption aes sha256

我正在尝试基于发现的另一种C#方法来复制加密方法。

C#加密方法EncryptText(word, password)调用另一个方法AES_Encrypt(byte[] bytesToBeEncrypted, byte[] passwordBytes)来加密纯文本:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using System.Security.Cryptography;
using System.IO;
using System.Text;

namespace Rextester
{
    public class Program
    {
        public static void Main(string[] args)
        {
            var f = EncryptText("763059", "515t3ma5m15B4d35");//(word, password)
            Console.WriteLine(f);
        }

        public static byte[] AES_Encrypt(byte[] bytesToBeEncrypted, byte[] passwordBytes)
        {
            byte[] encryptedBytes = null;
            byte[] saltBytes = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 };

            using (MemoryStream ms = new MemoryStream())
            {
                using (RijndaelManaged AES = new RijndaelManaged())
                {
                    AES.KeySize = 256;
                    AES.BlockSize = 128;

                    var key = new Rfc2898DeriveBytes(passwordBytes, saltBytes, 1000);
                    AES.Key = key.GetBytes(AES.KeySize / 8);
                    AES.IV = key.GetBytes(AES.BlockSize / 8);

                    AES.Mode = CipherMode.CBC;

                    using (var cs = new CryptoStream(ms, AES.CreateEncryptor(), CryptoStreamMode.Write))
                    {
                        cs.Write(bytesToBeEncrypted, 0, bytesToBeEncrypted.Length);
                        cs.Close();
                    }
                    encryptedBytes = ms.ToArray();
                }
            }

            return encryptedBytes;
        }

        public static string EncryptText(string input, string password)
        {
            byte[] bytesToBeEncrypted = Encoding.UTF8.GetBytes(input);
            byte[] passwordBytes = Encoding.UTF8.GetBytes(password);

            passwordBytes = SHA256.Create().ComputeHash(passwordBytes);

            byte[] bytesEncrypted = AES_Encrypt(bytesToBeEncrypted, passwordBytes);
            string result = Convert.ToBase64String(bytesEncrypted);

            return result;
        }
    }
}

使用单词 763059 和密码 515t3ma5m15B4d35 ,输出如下:

  

3cHrXxxL1Djv0K2xW4HuCg ==

更新:

现在,我创建了一个Java Class main,试图在其中复制以前的代码:

public class main {

    final static String PASSWORD = "515t3ma5m15B4d35";
    final static byte[] SALT = new byte[]{1, 2, 3, 4, 5, 6, 7, 8};
    final static int KEY_SIZE = 256;
    final static int BLOCK_SIZE = 128;
    final static int ITERATIONS = 1000;

    public static void main(String[] args) {
        System.out.println(encryptText("763059", PASSWORD));
    }

    public static String encryptText(String word, String password) {
        try {
            MessageDigest md = MessageDigest.getInstance("SHA-256");
            md.update(password.getBytes("UTF-8"));
            password = new String(md.digest(), "UTF-8");

            SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
            KeySpec spec = new PBEKeySpec(password.toCharArray(), SALT, ITERATIONS, KEY_SIZE);
            SecretKey tmp = factory.generateSecret(spec);
            SecretKeySpec skey = new SecretKeySpec(tmp.getEncoded(), "AES");

            byte[] iv = new byte[BLOCK_SIZE / 8];
            IvParameterSpec ivspec = new IvParameterSpec(iv);
            Cipher ci = Cipher.getInstance("AES/CBC/PKCS5Padding");
            ci.init(Cipher.ENCRYPT_MODE, skey, ivspec);
            byte[] result = ci.doFinal(word.getBytes("UTF-8"));

            return DatatypeConverter.printBase64Binary(result);

        } catch (NoSuchAlgorithmException | UnsupportedEncodingException | IllegalBlockSizeException | BadPaddingException | InvalidKeyException | InvalidAlgorithmParameterException | NoSuchPaddingException | InvalidKeySpecException ex) {
            return null;
        }
    }

}

更新:

我读到有关在Java中使用256位密钥的信息,发现需要添加Java Cryptography Extensions以允许256个密钥(因为我正在使用JDK7 )。

然后我将库添加到项目中,并且更改了这一行:

KeySpec spec = new PBEKeySpec(password.toCharArray(), SALT, ITERATIONS, KEY_SIZE);

具有键值:

final static int KEY_SIZE = 256;

现在输出如下:

  

J1xbKOjIeXbQ9njH + 67RNw ==

我仍然无法实现我的目标。有什么建议吗?

3 个答案:

答案 0 :(得分:0)

我不是C#专家,但有几件事需要检查:

阅读有关Rfc2898DeriveBytes的文档,我看到该函数正在使用SHA1哈希,因此请尝试使用PBKDF2WithHmacSHA1

在两个实例(Rfc2898DeriveBytes,PBEKeySpec)上,您应确保密钥大小相同(256位),在Java代码中肯定是错误的

您可以尝试对密钥进行编码和打印,以确保它们相同。

  

我需要添加Java密码学扩展以允许256个密钥。

取决于您的JVM版本。我相信从1.8u162版开始,Oracle JDK默认包含Unlimited Strength JCE策略。如果您使用任何当前的JRE版本,则应该没问题

附加:您正在使用(静态)零数组IV,这是不安全的

答案 1 :(得分:0)

最后,我决定使用BouncyCastle API来使用RijndaelEngine的功能,并使用PKCS5S2ParametersGenerator生成256位密钥。

我创建了RijndaelEncryption类,以便能够按照C#代码进行加密:

public class RijndaelEncryption {

    public String encryptString(String word, String password, byte[] salt, int iterations, int keySize, int blockSize) {
        try {
            byte[] pswd = sha256String(password, "UTF-8");
            PKCS5S2ParametersGenerator key = keyGeneration(pswd, salt, iterations);
            ParametersWithIV iv = generateIV(key, keySize, blockSize);
            BufferedBlockCipher cipher = getCipher(true, iv);
            byte[] inputText = word.getBytes("UTF-8");
            byte[] newData = new byte[cipher.getOutputSize(inputText.length)];
            int l = cipher.processBytes(inputText, 0, inputText.length, newData, 0);
            cipher.doFinal(newData, l);
            return new String(Base64.encode(newData), "UTF-8");
        } catch (UnsupportedEncodingException | IllegalStateException | DataLengthException | InvalidCipherTextException e) {
            return null;
        }
    }

    public BufferedBlockCipher getCipher(boolean encrypt, ParametersWithIV iv) {
        RijndaelEngine rijndael = new RijndaelEngine();
        BufferedBlockCipher cipher = new PaddedBufferedBlockCipher(new CBCBlockCipher(rijndael));
        cipher.init(encrypt, iv);
        return cipher;
    }

    public ParametersWithIV generateIV(PKCS5S2ParametersGenerator key, int keySize, int blockSize) {
        try {
            ParametersWithIV iv = null;
            iv = ((ParametersWithIV) key.generateDerivedParameters(keySize, blockSize));
            return iv;
        } catch (Exception e) {
            return null;
        }
    }

    public PKCS5S2ParametersGenerator keyGeneration(byte[] password, byte[] salt, int iterations) {
        try {
            PKCS5S2ParametersGenerator key = new PKCS5S2ParametersGenerator();
            key.init(password, salt, iterations);
            return key;
        } catch (Exception e) {
            return null;
        }
    }

    public byte[] sha256String(String password, Charset charset) {
        try {
            MessageDigest md = MessageDigest.getInstance("SHA-256");
            md.update(password.getBytes(charset));
            return md.digest();
        } catch (NoSuchAlgorithmException ex) {
            return null;
        }
    }

    public byte[] sha256String(String password, String charset) {
        try {
            MessageDigest md = MessageDigest.getInstance("SHA-256");
            md.update(password.getBytes(charset));
            return md.digest();
        } catch (NoSuchAlgorithmException | UnsupportedEncodingException ex) {
            return null;
        }
    }
}

然后我用main方法进行了测试:

public static void main(String[] args) {
     RijndaelEncryption s = new RijndaelEncryption();
     byte[] salt = new byte[]{1, 2, 3, 4, 5, 6, 7, 8};
     String encryptStr = s.encryptString("763059", "515t3ma5m15B4d35", salt, 1000, 256, 128);
     System.out.println("Encryptation: " + encryptStr);
}

获得:

  

加密:3cHrXxxL1Djv0K2xW4HuCg ==

答案 2 :(得分:0)

我对原始问题有一个答案。供以后没有弹力堡的参考。

您遇到了一些问题。

  1. 密钥大小必须为256 + 128(以及块大小)
  2. C#和Java byte []的作用不同,因为Java字节始终是带符号的,这与密码的加密搞混了。

这两个代码段均作为输出提供:

xD4R / yvV2tHajUS9p4kqJg ==

C#代码:

    using System;
    using System.IO;
    using System.Security.Cryptography;
    using System.Text;
    using System.Threading.Tasks;

    namespace tryencryption
    {
        class Program
        {


        static void Main(string[] args)
        {
            var f = EncryptText("yme", "515t3ma5m15B4d35");//(word, password)
            Console.WriteLine(f);
            Console.ReadKey();
        }

        public static byte[] AES_Encrypt(byte[] bytesToBeEncrypted, string passwordString)
        {
            byte[] encryptedBytes = null;
            byte[] salt = new byte[] { (byte)0x49, (byte)0x64, (byte)0x76, (byte)0x65, (byte)0x64, (byte)0x65, (byte)0x76, (byte)0x61, (byte)0x6e, (byte)0x20, (byte)0x4d, (byte)0x65, (byte)0x76 };

            using (MemoryStream ms = new MemoryStream())
            {
                using (RijndaelManaged AES = new RijndaelManaged())
                {
                    AES.KeySize = 256;
                    AES.BlockSize = 128;

                    var key = new Rfc2898DeriveBytes(passwordString, salt, 1000);
                    AES.Key = key.GetBytes(AES.KeySize / 8);
                    AES.IV = key.GetBytes(AES.BlockSize / 8);

                    AES.Mode = CipherMode.CBC;

                    using (var cs = new CryptoStream(ms, AES.CreateEncryptor(), CryptoStreamMode.Write))
                    {
                        cs.Write(bytesToBeEncrypted, 0, bytesToBeEncrypted.Length);
                        cs.Close();
                    }
                    encryptedBytes = ms.ToArray();
                }
            }

            return encryptedBytes;
        }

        public static string EncryptText(string input, string password)
        {
            byte[] bytesToBeEncrypted = Encoding.Unicode.GetBytes(input);

            byte[] bytesEncrypted = AES_Encrypt(bytesToBeEncrypted, password);
            string result = Convert.ToBase64String(bytesEncrypted);

            return result;
        }


    }
}

Java代码(这是我的用例,来自一个Android项目bcs,但应该在任何地方都可以使用):

        package com.example.myapplication;

    import androidx.appcompat.app.AppCompatActivity;

    import android.os.Bundle;
    import android.util.Base64;

    import java.nio.charset.StandardCharsets;
    import java.security.InvalidAlgorithmParameterException;
    import java.security.InvalidKeyException;
    import java.security.Key;
    import java.security.MessageDigest;
    import java.security.NoSuchAlgorithmException;
    import java.security.spec.AlgorithmParameterSpec;
    import java.security.spec.InvalidKeySpecException;

    import javax.crypto.BadPaddingException;
    import javax.crypto.Cipher;
    import javax.crypto.IllegalBlockSizeException;
    import javax.crypto.NoSuchPaddingException;
    import javax.crypto.SecretKeyFactory;
    import javax.crypto.spec.IvParameterSpec;
    import javax.crypto.spec.PBEKeySpec;
    import javax.crypto.spec.SecretKeySpec;

    public class MainActivity extends AppCompatActivity {

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

            String result = encrypt("yme", "515t3ma5m15B4d35");
        }


        private static String encrypt(String word, String password) {

            byte[] salt = new byte[] { (byte)0x49, (byte)0x64, (byte)0x76, (byte)0x65, (byte)0x64, (byte)0x65, (byte)0x76, (byte)0x61, (byte)0x6e, (byte)0x20, (byte)0x4d, (byte)0x65, (byte)0x76};

            try {
                SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
                PBEKeySpec pbeKeySpec = new PBEKeySpec(password.toCharArray(), salt, 1000, 256 + 128);
                Key secretKey = factory.generateSecret(pbeKeySpec);
                byte[] test = secretKey.getEncoded();
                byte[] key = new byte[32];
                byte[] iv = new byte[16];
                System.arraycopy(secretKey.getEncoded(), 0, key, 0, 32);
                System.arraycopy(secretKey.getEncoded(), 32, iv, 0, 16);


                SecretKeySpec secret = new SecretKeySpec(key, "AES");
                AlgorithmParameterSpec ivSpec = new IvParameterSpec(iv);
                Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
                cipher.init(Cipher.ENCRYPT_MODE, secret, ivSpec);
   //Realise Im using UTF16 here! Maybe you need UTF8
                byte[] plaintextintobytes  =word.getBytes(StandardCharsets.UTF_16LE);
                byte[] encrypted = cipher.doFinal(plaintextintobytes);
                String encryptedInformation = Base64.encodeToString(encrypted, Base64.NO_WRAP);
                return encryptedInformation;

            } catch (NoSuchAlgorithmException e) {
                e.printStackTrace();
            } catch (InvalidKeySpecException e) {
                e.printStackTrace();
            } catch (NoSuchPaddingException e) {
                e.printStackTrace();
            } catch (InvalidKeyException e) {
                e.printStackTrace();
            } catch (InvalidAlgorithmParameterException e) {
                e.printStackTrace();
            } catch (IllegalBlockSizeException e) {
                e.printStackTrace();
            } catch (BadPaddingException e) {
                e.printStackTrace();
            }

            return "";
        }


    }