2017-02-25 18 views
1

私は、ユーザーが入力したパスワード(キー)とソルトに基づいて、文字列をエンコードしてデコードする簡単なヘルパーを開発しています。dotnetコアでパスワードと塩を使って文字列をエンコードしてデコードする

完全な.NET Frameworkとは対照的に、現在dotnet coreにはRijndaelManagedクラスの実装がありません。 C#用のかなりまともな暗号化/復号化サンプルは、このクラスに基づいており、ドットネットのために役に立たないものです。 CoreFX repo on GitHubには多くの議論があります。私は半言っ

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

} 

はしかし、(旧) MSDN article on AesManagedarticle by Troy Hunt両方の組み合わせで - - いくつかのリファクタリングはもちろんのこと、私は、次の半働い例を醸造することができましたそれは動作することを意味します... 時々。そしてそこに問題がある。テストを任意に実行すると、ほとんどの場合成功します。ランダムに、約4倍のうち、それが次のメッセージで失敗します。

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

それはUnicodeの問題のように見えますが、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

あなたの問題は、あなたが任意のバイト配列にGetStringを呼び出すことが許可されていません

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

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

機能のこのペアによって引き起こされる代わりに、暗号化とは何の関係もないと、あなたは情報の損失が発生します。あなたは一例にBase64のために、任意のバイト配列することができますエンコーディングを使用する必要があります。

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

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

すごく、そうだね。私はまだそれの 'ランダム性'について困惑していますが、Base64( ' - '文字なし)を使用することでそれを修正しました。ありがとう! –

関連する問題