2017-02-25 9 views
1

사용자가 제공 한 암호 (키)와 salt를 기반으로 문자열을 인코딩하고 디코딩해야하는 dotnet 코어에 대한 간단한 도우미 작업을하고 있습니다.dotnet 코어에서 문자열과 암호를 사용하여 문자열을 인 코드하고 디코드하십시오.

전체 .NET Framework와 달리 dotnet core에는 현재 RijndaelManaged 클래스에 대한 구현이 없습니다. 거의 모든 C#의 암호화/해독 샘플은이 클래스를 기반으로하므로 dotnet 코어에서 쓸모 없게 만듭니다. 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 AesManaged의 조합과 article by Troy Hunt으로 - - 리팩토링을 언급하지 않기 위하여 나는 다음과 같은 반 가공 예 맥주를 양조 할 수 있었다 작업 예를 의미합니다 ... 가끔. 그리고 그 문제가 있습니다. 테스트를 임의로 실행하면 대부분의 경우 성공합니다.

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 변경이 오류에 이르게 : 무작위로 4 회 중 약, 그것은 다음과 같은 메시지와 함께 실패

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

그래서 내 기본 질문은 입니다. 왜 내 imlementation (테스트)가 실패합니까?

나는 확실히 암호에 대한 전문가는 아니지만, 높은 수준에서 몇 가지 기본 개념을 알고 있습니다. 또한 유사한 질문 "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 ('-'문자 제외)를 사용하여 고정시켰다. 감사! –