2010-09-10 9 views
42

Mam pewne dane, które chcę zaszyfrować w aplikacji ASP.NET MVC, aby uniemożliwić użytkownikom manipulowanie nimi. Mogę używać klas kryptograficznych do rzeczywistego szyfrowania/deszyfrowania, bez problemu. Głównym problemem jest ustalenie, gdzie przechowywać klucz szyfrowania i zarządzanie zmianami w nim.Wykorzystanie klucza maszynowego ASP.NET do szyfrowania własnych danych

Ponieważ ASP.NET już utrzymuje klucz MachineKey dla różnych rzeczy (szyfrowanie ViewData itp.), Zastanawiałem się, czy istnieją jakieś funkcje ASP.NET, które pozwalają mi szyfrować/odszyfrowywać moje własne dane za pomocą klucza maszynowego? W ten sposób nie musiałbym opracowywać własnego systemu zarządzania kluczami.

Odpowiedz

9

Chyba nie bezpośrednio. Nie pamiętam, skąd to wzięłam, prawdopodobnie połączenie Reflectora i niektórych blogów.

public abstract class MyAwesomeClass 
{ 
    private static byte[] cryptKey; 

    private static MachineKeySection machineKeyConfig = 
     (MachineKeySection)ConfigurationManager 
      .GetSection("system.web/machineKey"); 

    // ... snip ... 

    static MyAwesomeClass() 
    { 
     string configKey; 
     byte[] key; 

     configKey = machineKeyConfig.DecryptionKey; 
     if (configKey.Contains("AutoGenerate")) 
     { 
      throw new ConfigurationErrorsException(
       Resources.MyAwesomeClass_ExplicitAlgorithmRequired); 
     } 

     key = HexStringToByteArray(configKey); 

     cryptKey = key; 
    } 

    // ... snip ... 

    protected static byte[] Encrypt(byte[] inputBuffer) 
    { 
     SymmetricAlgorithm algorithm; 
     byte[] outputBuffer; 

     if (inputBuffer == null) 
     { 
      throw new ArgumentNullException("inputBuffer"); 
     } 

     algorithm = GetCryptAlgorithm(); 

     using (var ms = new MemoryStream()) 
     { 
      algorithm.GenerateIV(); 
      ms.Write(algorithm.IV, 0, algorithm.IV.Length); 

      using (var cs = new CryptoStream(
       ms, 
       algorithm.CreateEncryptor(), 
       CryptoStreamMode.Write)) 
      { 
       cs.Write(inputBuffer, 0, inputBuffer.Length); 
       cs.FlushFinalBlock(); 
      } 

      outputBuffer = ms.ToArray(); 
     } 

     return outputBuffer; 
    } 

    protected static byte[] Decrypt(string input) 
    { 
     SymmetricAlgorithm algorithm; 
     byte[] inputBuffer, inputVectorBuffer, outputBuffer; 

     if (input == null) 
     { 
      throw new ArgumentNullException("input"); 
     } 

     algorithm = GetCryptAlgorithm(); 
     outputBuffer = null; 

     try 
     { 
      inputBuffer = Convert.FromBase64String(input); 

      inputVectorBuffer = new byte[algorithm.IV.Length]; 
      Array.Copy(
       inputBuffer, 
       inputVectorBuffer, 
       inputVectorBuffer.Length); 
      algorithm.IV = inputVectorBuffer; 

      using (var ms = new MemoryStream()) 
      { 
       using (var cs = new CryptoStream(
        ms, 
        algorithm.CreateDecryptor(), 
        CryptoStreamMode.Write)) 
       { 
        cs.Write(
         inputBuffer, 
         inputVectorBuffer.Length, 
         inputBuffer.Length - inputVectorBuffer.Length); 
        cs.FlushFinalBlock(); 
       } 

       outputBuffer = ms.ToArray(); 
      } 
     } 
     catch (FormatException e) 
     { 
      throw new CryptographicException(
       "The string could not be decoded.", e); 
     } 

     return outputBuffer; 
    } 

    // ... snip ... 

    private static SymmetricAlgorithm GetCryptAlgorithm() 
    { 
     SymmetricAlgorithm algorithm; 
     string algorithmName; 

     algorithmName = machineKeyConfig.Decryption; 
     if (algorithmName == "Auto") 
     { 
      throw new ConfigurationErrorsException(
       Resources.MyAwesomeClass_ExplicitAlgorithmRequired); 
     } 

     switch (algorithmName) 
     { 
      case "AES": 
       algorithm = new RijndaelManaged(); 
       break; 
      case "3DES": 
       algorithm = new TripleDESCryptoServiceProvider(); 
       break; 
      case "DES": 
       algorithm = new DESCryptoServiceProvider(); 
       break; 
      default: 
       throw new ConfigurationErrorsException(
        string.Format(
         CultureInfo.InvariantCulture, 
         Resources.MyAwesomeClass_UnrecognizedAlgorithmName, 
         algorithmName)); 
     } 

     algorithm.Key = cryptKey; 

     return algorithm; 
    } 

    private static byte[] HexStringToByteArray(string str) 
    { 
     byte[] buffer; 

     if (str == null) 
     { 
      throw new ArgumentNullException("str"); 
     } 

     if (str.Length % 2 == 1) 
     { 
      str = '0' + str; 
     } 

     buffer = new byte[str.Length/2]; 

     for (int i = 0; i < buffer.Length; ++i) 
     { 
      buffer[i] = byte.Parse(
       str.Substring(i * 2, 2), 
       NumberStyles.HexNumber, 
       CultureInfo.InvariantCulture); 
     } 

     return buffer; 
    } 
} 

Ostrzegawczy emptor!

+2

+1 epicka nazwa klasy – vtortola

+0

Zamiast tego publikuj wewnętrzne informacje o tym, jak to zrobić. – JJS

0

Być może będziesz mógł ponownie użyć metody MembershipProvider.EncryptPassword, która z kolei wykorzystuje niektóre (niestety wewnętrzne) metody szyfrowania klasy MachineKeySection.

+0

Na szczęście już nie wewnętrzny. Zobacz odpowiedź Jeffa Mosera. –

40

Nowa klasa MachineKey w programie ASP.NET 4.0 robi dokładnie to, co chcesz.

Na przykład:

public static class StringEncryptor { 
    public static string Encrypt(string plaintextValue) { 
     var plaintextBytes = Encoding.UTF8.GetBytes(plaintextValue); 
     return MachineKey.Encode(plaintextBytes, MachineKeyProtection.All); 
    } 

    public static string Decrypt(string encryptedValue) { 
     try { 
      var decryptedBytes = MachineKey.Decode(encryptedValue, MachineKeyProtection.All); 
      return Encoding.UTF8.GetString(decryptedBytes); 
     } 
     catch { 
      return null; 
     } 
    } 
} 

UPDATE: Jak wspomniano here, należy uważać, jak użyć tego czy można pozwolić, aby ktoś wykuć tokenu uwierzytelniania formularzy.

+9

Od wersji .net 4.5 kod MachinKey.Encode/MachineKey.Decode został uznany za przestarzały i zamiast niego należy użyć MachineKey.Protect. Zgodnie z odpowiedzią Marco Bettiolo. – Anthony

3

Jeśli pracujesz z 3,5 lub wcześniej można uniknąć dużo kodu i po prostu to zrobić:

public static string Encrypt(string cookieValue) 
{ 
    return FormsAuthentication.Encrypt(new FormsAuthenticationTicket(1, 
                    string.Empty, 
                    DateTime.Now, 
                    DateTime.Now.AddMinutes(20160), 
                    true, 
                    cookieValue)); 
} 

public static string Decrypt(string encryptedTicket) 
{ 
    return FormsAuthentication.Decrypt(encryptedTicket).UserData; 
} 

Jeden z moich kolegów namówił mnie na to i myślę, że to dość rozsądne, aby zrobić to dla niestandardowe pliki cookie, jeśli nie dla ogólnych potrzeb szyfrowania.

+0

FYI, to zakłada, że ​​nie przekraczasz maksymalnej długości biletu (4096) – mgoodric

43

Z .NET 4.5 ram prac należy użyć nowego API:

public class StringProtector 
{ 

    private const string Purpose = "Authentication Token"; 

    public string Protect(string unprotectedText) 
    { 
     var unprotectedBytes = Encoding.UTF8.GetBytes(unprotectedText); 
     var protectedBytes = MachineKey.Protect(unprotectedBytes, Purpose); 
     var protectedText = Convert.ToBase64String(protectedBytes); 
     return protectedText; 
    } 

    public string Unprotect(string protectedText) 
    { 
     var protectedBytes = Convert.FromBase64String(protectedText); 
     var unprotectedBytes = MachineKey.Unprotect(protectedBytes, Purpose); 
     var unprotectedText = Encoding.UTF8.GetString(unprotectedBytes); 
     return unprotectedText; 
    } 

} 

Idealnie „Purpose” powinien być znany jednorazowy ważny wartość, aby zapobiec kucia.

+0

Dzięki! Chciałbym dodać: http://msdn.microsoft.com/en-us/library/system.web.security.machinekey.protect(v=vs.110).aspx Sprawdź "Uwagi", aby zobaczyć lepszy sposób używać "Cel". –

+2

Aby potwierdzić, ponieważ odwołuje się do klucza komputera, zakładam, że w zbalansowanej konfiguracji obciążenia bez lepkich sesji trzeba się upewnić, że klucz komputera jest taki sam. –

+2

Tak, klucz maszynowy musi być taki sam na wszystkich maszynach w systemie równoważenia obciążenia –

Powiązane problemy