Questo è un esempio di implementazione della risposta proposta da @TheCloudlessSky. Ho pensato che avrebbe aiutato qualcuno che si stava chiedendo come implementarlo.
Stavo lavorando con un database esistente, quindi la classe di base del modello è stata generata automaticamente per me.
User.cs generato automaticamente:
namespace MyApp.Model
{
public partial class User
{
public int UserId { get; set; }
public byte[] SSN { get; set; }
...
}
}
ho creato il mio User.cs. (Si noti che si trova nello stesso spazio dei nomi del file User.cs generato automaticamente e non ci sono stati errori del compilatore perché il file User.cs generato automaticamente è stato dichiarato come classe parziale! Inoltre, il mio User.cs non può essere nella stessa cartella di User.cs auto-generate a causa del nome del file di conflitto!)
namespace MyApp.Model
{
public partial class User
{
public string DecryptedSSN { get; set; }
...
}
}
Ora ogni volta che dovessi recuperare utente dal mio DbContext, vedrò tutte le proprietà definite nella classe generata automaticamente, così come quelle definite nelle la mia classe migliorata.
Ecco una implementazione del mio UserRepository.cs:
namespace MyApp.Model
{
public interface IUserRepository
{
User Get(int userId);
...
}
public class UserRepository : IUserRepository
{
public User GetById(int userId)
{
using (var dataContext = MyDbContext())
{
var user = dataContext.Users.Find(u => u.UserId == userId);
var decryptedSSNResult = dataContext.Decrypt(u.SSN);
user.DecryptedSSN = decryptedSSNResult.FirstOrDefault();
return user;
}
}
}
}
Ora si potrebbe chiedere come/dove ho ricevuto MyDbContext.Decrypt() da?
Questo NON viene generato automaticamente. Tuttavia, è possibile importare questa procedura memorizzata nel file Model.Context.cs generato automaticamente. (Questo processo è molto ben documentato nell'articolo ufficiale di EntityFramework: Come: Importare una stored procedure (Strumenti modello Entity Data) al http://msdn.microsoft.com/en-us/library/vstudio/bb896231(v=vs.100).aspx)
Nel caso in cui non si sa quale dovrebbe essere il risultato finale, qui è ciò che è stato generato automaticamente nei miei Model.Context.cs:
namespace MyApp.Model
{
// using statements found here
public partial class MyDbContext : DbContext
{
public MyDbContext()
: base("name = MyDbContext")
{ }
public virtual ObjectResult<string> Decrypt(byte[] encryptedData)
{
var encryptedDataParameter = encryptedData != null ?
new ObjectParameter("encryptedData", encryptedData) :
new ObjectParameter("encryptedData", typeof(byte[]));
return ((IObjectContextAdapter)this).ObjectContext.ExecuteFunction<string>("Decrypt", encryptedDataParameter);
}
// similar function for Encrypt
}
}
Ecco come il mio Decrypt stored procedure assomiglia:
CREATE PROCEDURE decrypt
@encryptedData VARBINARY(8000)
AS
BEGIN
OPEN SYMMETRIC KEY xxx_Key DECRYPTION BY CERTIFICATE xxx_Cert;
SELECT CAST(DECRYPTIONBYKEY(@encryptedData) AS NVARCHAR(MAX)) AS data;
CLOSE ALL SYMMETRIC KEYS;
END;
GO
Considerazioni sulle prestazioni
Ora che vi ho mostrato un'implementazione della risposta data dal @TheCloudlessSky, io desidero sottolineare rapidamente alcuni punti relativi alle prestazioni.
1) Ogni volta che si recupera un oggetto utente, al database vengono effettuati 2 viaggi anziché 1. Primo viaggio per il recupero dell'oggetto; secondo viaggio per decifrare SSN. Questo può causare problemi di prestazioni se non si presta attenzione.
Raccomandazione: NON decodificare automaticamente i campi crittografati! Nel mio esempio mostrato sopra, ho decodificato SSN durante il recupero dell'oggetto utente. L'ho fatto solo a scopo dimostrativo! Chiediti se hai davvero bisogno di SSN ogni volta che l'utente viene richiamato. Se possibile, scegli la decodifica pigro su una decifratura entusiasta!
2) Anche se non l'ho dimostrato, ogni volta che si crea/aggiorna un oggetto utente, ci saranno anche 2 viaggi nel database. Primo viaggio per la crittografia SSN; secondo viaggio per l'inserimento dell'oggetto. Di nuovo questo può causare problemi di prestazioni se non stai attento.
Raccomandazione: essere consapevoli di questo problema di prestazioni ma non delegare la crittografia e il salvataggio di SSN come metodo diverso. Mantieni tutto in un'unica operazione altrimenti potresti dimenticarti di salvarlo del tutto. Quindi la raccomandazione per la creazione/l'aggiornamento è opposta al recupero: scegli una codifica desiderosa su una crittografia pigra!
sembra una buona soluzione e mi sarebbe probabilmente andare con che, se ho creato un nuovo database. Il fatto è che il database è piuttosto grande e viene utilizzato da tonnellate di progetti, quindi sarebbe un lavoro enorme scavalcare il codice e cambiarlo. – Andreas
@Andreas - Vedere la mia modifica sopra. – TheCloudlessSky
Grazie per il suggerimento. Questa mi è sembrata una buona idea e l'ho provata. purtroppo se voglio essere in grado di fare query LINQ in tutti i miei dati decifrati dalla tabella, la stored procedure deve prima eseguire. Ciò richiede un tempo infinito poiché sono 250.000+ righe con 5 colonne ciascuna da decifrare. Così farlo: context.AllMembers() Dove (x => x.MemberId == 1) avrà troppo tempo.. Certo che potrei fare un SP che prende un argomento di memberid, ma cosa succede se voglio cercare su ad es. nome con LINQ? Forse im manca qualcosa di importante qui ... – Andreas