【问题标题】:Wrong algorithm: AES or Rijndael required on c#错误的算法:c# 需要 AES 或 Rijndael
【发布时间】:2017-09-17 07:23:52
【问题描述】:

我有安卓代码,我尝试将其转换为 c#。这是一个简单的加密类。但是当我尝试用它解密数据时,我发现: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();
}

这是我原来的安卓代码:

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#,发布 C# 代码
  • 我不明白。它写在那里!转换后的代码。
  • this.mBuilder.getAlgorithm() 在您的 Android 代码中返回什么?
  • 我更新了安卓代码。现在您可以看到实际值了。
  • 不推荐。这些都是标准化的算法。因此,尽管调试可能需要一些时间,但您应该能够获得相同的结果。将 Java 和 C# 混合使用会让您在未来遥遥领先。

标签: c# algorithm encryption aes encryption-symmetric


【解决方案1】:

使用相同的完善的加密算法,但调用它们的方法不同。不过还是可以转换代码的。

一个关键点是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)));
    }
  }
}

【讨论】:

    【解决方案2】:

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

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

    【讨论】:

    • 谢谢。但我怎样才能创建密码短语?我想它应该从 ENCRYPTION_KEY、salt 和 iv 生成。我说的对吗?
    • 从哪里创建它并不重要,它只是唯一的字符串,以便确保只有您的应用程序/代码才能解密它。 . .
    • 但我想解密字符串。不加密它。实际上字符串已经被android客户端加密了,我必须解密它!
    • 你的字符串是用哪种算法加密的?使用 android 中提供的密钥尝试其解密部分进行加密并告诉我输出/错误。
    • 一般来说,这类问题不应收到显示通用加密/解密方式的答案。请保持手头的问题。块大小为 256 的 Rijndael 不是 AES,这意味着您可以在其他平台(包括 Java 和 Android)上遇到任何类型的兼容性问题。
    猜你喜欢
    • 2014-04-16
    • 1970-01-01
    • 1970-01-01
    • 2023-03-23
    • 1970-01-01
    • 2014-10-25
    • 1970-01-01
    • 2014-06-09
    • 2017-08-28
    相关资源
    最近更新 更多