2010-09-10 13 views
42

Ho alcuni dati che desidero crittografare in un'applicazione ASP.NET MVC per impedire agli utenti di interferire con essa. Posso usare le classi di crittografia per fare la vera crittografia/decodifica, nessun problema lì. Il problema principale è capire dove conservare la chiave di crittografia e gestirne le modifiche.Utilizzo della macchina ASP.NETKey per la crittografia dei dati personali

Poiché ASP.NET mantiene già un machineKey per varie cose (crittografia ViewData, ecc.), Mi chiedevo se esistessero funzioni ASP.NET che consentissero di crittografare/decifrare i miei dati utilizzando la macchinaKey? In questo modo non avrei dovuto ideare il mio sistema di gestione delle chiavi.

risposta

9

Immagino non direttamente. Non ricordo da dove ho preso questo, probabilmente una combinazione di Reflector e alcuni blog.

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

Caveat emptor!

+2

+1 nome classe epico – vtortola

+0

Pubblicazione piuttosto completa degli interni di come viene eseguita. – JJS

0

Potrebbe essere possibile riutilizzare il metodo MembershipProvider.EncryptPassword, che a sua volta utilizza alcuni metodi di crittografia (sfortunatamente interni) della classe MachineKeySection.

+0

Fortunatamente non più interno. Vedi la risposta di Jeff Moser. –

40

La nuova classe MachineKey in ASP.NET 4.0 fa esattamente ciò che si desidera.

Ad esempio:

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: Come accennato here, fare attenzione a come si utilizza questo o si potrebbe permettere a qualcuno di forgiare un token di autenticazione moduli.

+9

A partire da .net 4.5, MachinKey.Encode/MachineKey.Decode è stato deprecato e dovrebbe essere utilizzato MachineKey.Protect. Secondo la risposta di Marco Bettiolo. – Anthony

3

Se si lavora con 3.5 o versioni precedenti si può evitare un sacco di codice e fare solo questo:

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

Uno dei miei colleghi mi ha parlato in esso e penso che sia abbastanza ragionevole per fare questo per cookie personalizzati, se non per esigenze di crittografia generali.

+0

FYI, questo presuppone che non si superi la lunghezza massima del biglietto (4096) – mgoodric

43

Con .NET Framwork 4.5 è necessario utilizzare la nuova 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; 
    } 

} 

Idealmente il "Purpose" dovrebbe essere un noto una volta valore valido per prevenire forgiatura.

+0

Grazie! Vorrei aggiungere: http://msdn.microsoft.com/en-us/library/system.web.security.machinekey.protect(v=vs.110).aspx Controllare "Osservazioni" per vedere un modo migliore usare "Scopo". –

+2

Giusto per confermare poiché si fa riferimento alla Machine Key, suppongo che in una configurazione con bilanciamento del carico senza sessioni appiccicose dovresti assicurarti che la chiave della macchina sia la stessa. –

+2

Sì, la chiave del computer deve essere la stessa su tutte le macchine in un servizio di bilanciamento del carico –

Problemi correlati