2017-02-25 49 views
1

我一直在为dotnet核心开发一个简单的帮助程序,该程序应该根据用户提供的密码(密钥)和盐对字符串进行编码和解码。在dotnet核心中使用密码和salt对字符串进行编码和解码

与完整的.NET Framework相反,dotnet核心目前没有RijndaelManaged类的实现。几乎所有适用于C#的正确加密/解密示例都基于此类,使其对dotnet核心无用。在CoreFX repo on GitHub上有很多辩论。

然而,随着双方的(旧) MSDN article on AesManaged组合和article by Troy Hunt - 更不用说一些重构 - 我能酿造以下半工作示例

internal class CryptographyHelpers 
{ 
    internal static string Decrypt(string password, string salt, string encrypted_value) 
    { 
     string decrypted; 

     using (var aes = Aes.Create()) 
     { 
      var keys = GetAesKeyAndIV(password, salt, aes); 
      aes.Key = keys.Item1; 
      aes.IV = keys.Item2; 

      // create a decryptor to perform the stream transform. 
      var decryptor = aes.CreateDecryptor(aes.Key, aes.IV); 

      // create the streams used for encryption. 
      var encrypted_bytes = ToByteArray(encrypted_value); 
      using (var memory_stream = new MemoryStream(encrypted_bytes)) 
      { 
       using (var crypto_stream = new CryptoStream(memory_stream, decryptor, CryptoStreamMode.Read)) 
       { 
        using (var reader = new StreamReader(crypto_stream)) 
        { 
         decrypted = reader.ReadToEnd(); 
        } 
       } 
      } 
     } 

     return decrypted; 
    } 

    internal static string Encrypt(string password, string salt, string plain_text) 
    { 
     string encrypted; 

     using (var aes = Aes.Create()) 
     { 
      var keys = GetAesKeyAndIV(password, salt, aes); 
      aes.Key = keys.Item1; 
      aes.IV = keys.Item2; 

      var encryptor = aes.CreateEncryptor(aes.Key, aes.IV); 

      using (var memory_stream = new MemoryStream()) 
      { 
       using (var crypto_stream = new CryptoStream(memory_stream, encryptor, CryptoStreamMode.Write)) 
       { 
        using (var writer = new StreamWriter(crypto_stream)) 
        { 
         writer.Write(plain_text); 
        } 

        var encrypted_bytes = memory_stream.ToArray(); 
        encrypted = ToString(encrypted_bytes); 
       } 
      } 
     } 

     return encrypted; 
    } 

    private static byte[] ToByteArray(string input) 
    { 
     return Encoding.Unicode.GetBytes(input); 
    } 

    private static string ToString(byte[] input) 
    { 
     return Encoding.Unicode.GetString(input); 
    } 

    private static Tuple<byte[], byte[]> GetAesKeyAndIV(string password, string salt, SymmetricAlgorithm symmetricAlgorithm) 
    { 
     const int bits = 8; 
     var key = new byte[16]; 
     var iv = new byte[16]; 

     var derive_bytes = new Rfc2898DeriveBytes(password, ToByteArray(salt)); 
     key = derive_bytes.GetBytes(symmetricAlgorithm.KeySize/bits); 
     iv = derive_bytes.GetBytes(symmetricAlgorithm.BlockSize/bits); 

     return new Tuple<byte[], byte[]>(key, iv); 
    } 

} 

我说半工作的例子,这意味着它的工作... 有时。这就是问题所在。当我随意运行我的测试时,大部分时间都会成功。随机,约四分之一的时候,它失败,出现以下消息:

Message: Expected string to be "lorem ipsum dom dolor sit amet", but " �R��k6��o��do�Lr sit amet" differs near ">�R" (index 0).

它看起来像一个统一的问题,但将助手Encoding.UTF8的Encoding.Unicode,导致错误:

System.Security.Cryptography.CryptographicException : The input data is not a complete block.

更改编码Encoding.ASCII导致以下错误:

System.Security.Cryptography.CryptographicException : Specified padding mode is not valid for this algorithm.

我使用的测试是:

[Fact] 
public void Decrypt_Should_Decode_An_Encrypted_String() 
{ 
    // arrange 
    var key = Guid.NewGuid().ToString(); 
    var salt = Guid.NewGuid().ToString(); 
    var original_value = "lorem ipsum dom dolor sit amet"; 
    var encrypted_value = CryptographyHelpers.Encrypt(key, salt, original_value); 

    // act 
    var target = CryptographyHelpers.Decrypt(key, salt, encrypted_value); 

    // assert 
    target.Should().NotBeNullOrEmpty(); 
    target.Should().Be(original_value); 
} 

所以,我的主要问题是:为什么我的执行(测试)有时会失败?

我绝对不是密码学方面的专家,但在高层面上我意识到一些基本概念。此外,还发布了类似的问题"How to use Rijndael encryption with a .Net Core class library?",但唯一的答案停留在概念层面,缺乏具体的实施。

任何有关这是否足够好的实现的提示和论据也将非常感谢。

谢谢!

+0

如果你来到这个答案后,但是不知道为什么你的块大小错误,尝试与_salt_为使用新Rfc2898DeriveBytes一个字节数组,作为一个int它不会给IV一致的结果。 –

回答

2

您的问题无关,与加密,而是由这对功能

private static byte[] ToByteArray(string input) 
{ 
    return Encoding.Unicode.GetBytes(input); 
} 

private static string ToString(byte[] input) 
{ 
    return Encoding.Unicode.GetString(input); 
} 

你不准叫GetString上任意字节数组引起的,那么会造成信息丢失,如果你做。你需要使用一个编码,允许任意字节数组,例如Base64编码:

private static byte[] ToByteArray(string input) 
{ 
    return Convert.FromBase64String(input); 
} 

private static string ToString(byte[] input) 
{ 
    return Convert.ToBase64String(input); 
} 
+0

真棒,这似乎是。我仍然对它的“随机性”感到困惑,但是使用Base64(没有' - '字符)修复了它。谢谢! –

相关问题