错误的算法:C# 需要 AES 或 Rijndael

Wrong algorithm: AES or Rijndael required on c#

我有安卓代码,我试图将其转换为 C#。这是一个简单的加密 class。但是当我尝试用它解密数据时,我发现:Wrong algorithm: AES or Rijndael required.
这是我转换后的代码:

public static string decrypt(string data)
{
    byte[] dataBytes = Convert.FromBase64String(data);
    SecretKey secretKey = getSecretKey(hashTheKey("ABCD"));

    Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");

    cipher.init(2, secretKey, new IvParameterSpec(new byte[16]),
            SecureRandom.getInstance("SHA1PRNG"));
    var x = cipher.doFinal(dataBytes);
    return System.Text.Encoding.UTF8.GetString(x);
}
public static SecretKey getSecretKey(char[] key)
{
    var secretKeyType = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
    var secretkey = secretKeyType.generateSecret(new PBEKeySpec(key,
            System.Text.Encoding.UTF8
                .GetBytes("ABCD"),
            100, 128)).getEncoded();

    return new SecretKeySpec(secretkey, "AES/CBC/PKCS5Padding");
}
public static char[] hashTheKey(string key)
{
    MessageDigest messageDigest = MessageDigest.getInstance("SHA1");
    messageDigest.update(System.Text.Encoding.UTF8.GetBytes(key));
    return Convert.ToBase64String(messageDigest.digest()).ToCharArray();
}

这是我的原始 android 代码:

private char[] hashTheKey(String key) throws UnsupportedEncodingException, NoSuchAlgorithmException {
    MessageDigest messageDigest = MessageDigest.getInstance("SHA1");
    messageDigest.update(key.getBytes());
    return Base64.encodeToString(messageDigest.digest(),
                                 Base64.NO_PADDING).toCharArray();
}

private SecretKey getSecretKey(char[] key) throws NoSuchAlgorithmException, UnsupportedEncodingException, InvalidKeySpecException {
    return new SecretKeySpec(
        SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1")
        .generateSecret(new PBEKeySpec(key,
                       "ABCD".getBytes("UTF8"),
                       100, 128)).getEncoded(), "AES");
}

public String decrypt(String data) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException, UnsupportedEncodingException, InvalidKeySpecException {
    byte[] dataBytes = Base64.decode(data, Base64.DEFAULT);
    SecretKey secretKey = getSecretKey(hashTheKey("ABCD"));
    Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
    cipher.init(2, secretKey, new IvParameterSpec(new byte[16]),
            SecureRandom.getInstance("SHA1PRNG"));
    return new String(cipher.doFinal(dataBytes));
}

C# 不会将加密算法处理为 Android 或 java 您是否必须使用 AES 或 Rijndael 算法,因为您可以看到将简单文本转换为加密 Base64 的错误并且反之亦然,您可以在 C#

中使用以下 class
public static class Stringcipher
    {
        // This constant is used to determine the keysize of the encryption algorithm in bits.
        // We divide this by 8 within the code below to get the equivalent number of bytes.
        private const int Keysize = 256;

        // This constant determines the number of iterations for the password bytes generation function.
        private const int DerivationIterations = 1000;

        public static string Encrypt(string plainText, string passPhrase)
        {
            // Salt and IV is randomly generated each time, but is preprended to encrypted cipher text
            // so that the same Salt and IV values can be used when decrypting.  
            var saltStringBytes = Generate256BitsOfRandomEntropy();
            var ivStringBytes = Generate256BitsOfRandomEntropy();
            var plainTextBytes = Encoding.UTF8.GetBytes(plainText);
            using (var password = new Rfc2898DeriveBytes(passPhrase, saltStringBytes, DerivationIterations))
            {
                var keyBytes = password.GetBytes(Keysize / 8);
                using (var symmetricKey = new RijndaelManaged())
                {
                    symmetricKey.BlockSize = 256;
                    symmetricKey.Mode = CipherMode.CBC;
                    symmetricKey.Padding = PaddingMode.PKCS7;
                    using (var encryptor = symmetricKey.CreateEncryptor(keyBytes, ivStringBytes))
                    {
                        using (var memoryStream = new MemoryStream())
                        {
                            using (var cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write))
                            {
                                cryptoStream.Write(plainTextBytes, 0, plainTextBytes.Length);
                                cryptoStream.FlushFinalBlock();
                                // Create the final bytes as a concatenation of the random salt bytes, the random iv bytes and the cipher bytes.
                                var cipherTextBytes = saltStringBytes;
                                cipherTextBytes = cipherTextBytes.Concat(ivStringBytes).ToArray();
                                cipherTextBytes = cipherTextBytes.Concat(memoryStream.ToArray()).ToArray();
                                memoryStream.Close();
                                cryptoStream.Close();
                                return Convert.ToBase64String(cipherTextBytes);
                            }
                        }
                    }
                }
            }
        }

        public static string Decrypt(string cipherText, string passPhrase)
        {
            // Get the complete stream of bytes that represent:
            // [32 bytes of Salt] + [32 bytes of IV] + [n bytes of CipherText]
            var cipherTextBytesWithSaltAndIv = Convert.FromBase64String(cipherText);
            // Get the saltbytes by extracting the first 32 bytes from the supplied cipherText bytes.
            var saltStringBytes = cipherTextBytesWithSaltAndIv.Take(Keysize / 8).ToArray();
            // Get the IV bytes by extracting the next 32 bytes from the supplied cipherText bytes.
            var ivStringBytes = cipherTextBytesWithSaltAndIv.Skip(Keysize / 8).Take(Keysize / 8).ToArray();
            // Get the actual cipher text bytes by removing the first 64 bytes from the cipherText string.
            var cipherTextBytes = cipherTextBytesWithSaltAndIv.Skip((Keysize / 8) * 2).Take(cipherTextBytesWithSaltAndIv.Length - ((Keysize / 8) * 2)).ToArray();

            using (var password = new Rfc2898DeriveBytes(passPhrase, saltStringBytes, DerivationIterations))
            {
                var keyBytes = password.GetBytes(Keysize / 8);
                using (var symmetricKey = new RijndaelManaged())
                {
                    symmetricKey.BlockSize = 256;
                    symmetricKey.Mode = CipherMode.CBC;
                    symmetricKey.Padding = PaddingMode.PKCS7;
                    using (var decryptor = symmetricKey.CreateDecryptor(keyBytes, ivStringBytes))
                    {
                        using (var memoryStream = new MemoryStream(cipherTextBytes))
                        {
                            using (var cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read))
                            {
                                var plainTextBytes = new byte[cipherTextBytes.Length];
                                var decryptedByteCount = cryptoStream.Read(plainTextBytes, 0, plainTextBytes.Length);
                                memoryStream.Close();
                                cryptoStream.Close();
                                return Encoding.UTF8.GetString(plainTextBytes, 0, decryptedByteCount);
                            }
                        }
                    }
                }
            }
        }

        private static byte[] Generate256BitsOfRandomEntropy()
        {
            var randomBytes = new byte[32]; // 32 Bytes will give us 256 bits.
            using (var rngCsp = new RNGCryptoServiceProvider())
            {
                // Fill the array with cryptographically secure random bytes.
                rngCsp.GetBytes(randomBytes);
            }
            return randomBytes;
        }
    }

and 正在使用相同的完善的加密算法,但调用它们的方法不同。不过仍然可以转换代码。

一个关键点是 base64 编码的不同 - C# 总是使用填充。

转换后的代码如下:

const int KeySize = 128;

static string HashTheKey(string key) {
  String hashKey;
  using (var sha = new SHA1Managed()) {
   hashKey = Convert.ToBase64String(sha.ComputeHash(Encoding.UTF8.GetBytes(key)));
  }
  // beware - you're on C# now so remove the padding and add the newline to match java
  return hashKey.Replace("=", "") + "\n";
}

static byte[] GetSecretKey(string password) {
  var salt = Encoding.UTF8.GetBytes("JVAaVhAiddKAaghraikhmaini");
  using (var pass = new Rfc2898DeriveBytes(password, salt, 65536)) {
    return pass.GetBytes(KeySize / 8);
  }
}

static void Main(string[] args) {
  string encrypted = "vtlkQHTz7/oz2weuAAkLz2Q5c2yj2LGukF7SHJjT+TA8oRLixTQSXQ7dG1O736hyT1HJxcz0P4DzzVaO5chWKKSJQ2uPEpDQJu/fZGguqDw=";
  byte[] encryptedBytes = Convert.FromBase64String(encrypted);
  using (var aes = new AesManaged()) {
    aes.KeySize = KeySize;
    aes.Padding = PaddingMode.PKCS7;
    aes.Key = GetSecretKey(HashTheKey("Android"));
    // you're using the same init vector in your android code
    aes.IV = new byte[16];
    using (var decryptor = aes.CreateDecryptor()) {
      // dumps {"barcode":"12345678","token":"cad603fc-1e53-4a95-9150-f1694baa07f9"}
      Console.Out.WriteLine(Encoding.UTF8.GetString(decryptor.TransformFinalBlock(encryptedBytes, 0, encryptedBytes.Length)));
    }
  }
}