【问题标题】:AES 256 Text Encryption returning different valuesAES 256 文本加密返回不同的值
【发布时间】:2019-03-21 23:38:32
【问题描述】:

我正在尝试根据我找到的另一个 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==

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

【问题讨论】:

  • 也许我看错了,但你不是在 c# 中使用 256 位密钥,在 Java 中使用 128 位密钥吗?
  • @JohnWu,哦,真的吗?在哪一部分?
  • 我建议您 debug.log 记录每个单独的加密调用的输入和输出,以便您可以更轻松地隔离问题。
  • 哇,您完全错过了第一次 SHA-256 密码的部分(出于某种未知原因)。你还在努力吗?投反对票;为什么不让 Java 代码至少看起来像 C# 代码?
  • 另一个问题是字节数​​组的数据(密码的哈希值)存储在字符数组中,因为PBEKeySpec 需要一个字符数组。在 PBKDF2-provider 内部,字符数组使用 UTF-8 编码转换回字节数组。原始(在 C# 代码中使用)和重新转换的字节数组在值 >= 0x80(大部分存在)方面会有所不同。这会导致两种代码中的密钥和 IV 不同,从而产生不同的加密数据。另一种方法是来自 Bouncy Castle 的 PKCS5S2ParametersGenerator,用于生成需要字节数组的密钥和 IV。

标签: java c# encryption aes sha256


【解决方案1】:

最后我决定使用 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】:

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

    阅读有关Rfc2898DeriveBytes的文档我看到该函数使用SHA1哈希,所以试试你可以尝试使用PBKDF2WithHmacSHA1

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

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

    我需要添加 Java Cryptography Extensions 以允许 256 个密钥。

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

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

    【讨论】:

      【解决方案3】:

      我有原始问题的答案。供将来参考,不带 bouncycastle。

      您遇到了一些问题。

      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 "";
              }
      
      
          }
      

      【讨论】:

        猜你喜欢
        • 2021-04-24
        • 1970-01-01
        • 2011-09-03
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-04-10
        • 2015-07-19
        相关资源
        最近更新 更多