我有SQL Server 2012,我无法迁移到SQL Server 2016。
我正在使用加密,以这种方式使用Entity Framework Code First。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Configuration;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace x.y.Api.Models
{
[Table("Tbl_Naturalezas")]
public class Naturalezas: EncryptDecrypt
{
public Naturalezas()
{
_locked = true;
}
[Key]
public int idNaturaleza { get; set; }
string _naturaleza;
[StringLength(350)]
public string naturaleza
{
get { return locked ? Decrypt(_naturaleza, ConfigurationManager.AppSettings["appKeyPassword"]) : naturaleza; }
set { _naturaleza = IsEncrypted(value) ? value : Encrypt(value, ConfigurationManager.AppSettings["appKeyPassword"]) ; }
}
public virtual ICollection<Contactos> Contactos { get; set; }
}
}
哪个继承自此类:
public class EncryptDecrypt
{
public bool _locked;
public const string EncryptedStringPrefix = "X";
private const int Keysize = 256;
private const int DerivationIterations = 1000;
public string Encrypt(string atributoClase, string passPhrase)
{
string plainText = atributoClase.ToUpper();
if (plainText != null)
{
var saltStringBytes = Generate256BitsOfRandomEntropy();
var ivStringBytes = Generate256BitsOfRandomEntropy();
var plainTextBytes = Encoding.UTF8.GetBytes(plainText);
using (var password = new Rfc2898DeriveBytes(passPhrase, saltStringBytes, DerivationIterations))
{
var keyBytes = password.GetBytes(Keysize / 8);
using (var symmetricKey = new RijndaelManaged())
{
symmetricKey.BlockSize = 256;
symmetricKey.Mode = CipherMode.CBC;
symmetricKey.Padding = PaddingMode.PKCS7;
using (var encryptor = symmetricKey.CreateEncryptor(keyBytes, ivStringBytes))
{
using (var memoryStream = new MemoryStream())
{
using (var cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write))
{
cryptoStream.Write(plainTextBytes, 0, plainTextBytes.Length);
cryptoStream.FlushFinalBlock();
// Create the final bytes as a concatenation of the random salt bytes, the random iv bytes and the cipher bytes.
var cipherTextBytes = saltStringBytes;
cipherTextBytes = cipherTextBytes.Concat(ivStringBytes).ToArray();
cipherTextBytes = cipherTextBytes.Concat(memoryStream.ToArray()).ToArray();
memoryStream.Close();
cryptoStream.Close();
return Convert.ToBase64String(cipherTextBytes);
}
}
}
}
}
}
else
{
return plainText;
}
}
public string Decrypt(string atributoClase, string passPhrase)
{
string cipherText = atributoClase.ToUpper();
if (cipherText != null)
{
// Get the complete stream of bytes that represent:
// [32 bytes of Salt] + [32 bytes of IV] + [n bytes of CipherText]
var cipherTextBytesWithSaltAndIv = Convert.FromBase64String(cipherText);
// Get the saltbytes by extracting the first 32 bytes from the supplied cipherText bytes.
var saltStringBytes = cipherTextBytesWithSaltAndIv.Take(Keysize / 8).ToArray();
// Get the IV bytes by extracting the next 32 bytes from the supplied cipherText bytes.
var ivStringBytes = cipherTextBytesWithSaltAndIv.Skip(Keysize / 8).Take(Keysize / 8).ToArray();
// Get the actual cipher text bytes by removing the first 64 bytes from the cipherText string.
var cipherTextBytes = cipherTextBytesWithSaltAndIv.Skip((Keysize / 8) 2).Take(cipherTextBytesWithSaltAndIv.Length - ((Keysize / 8) 2)).ToArray();
using (var password = new Rfc2898DeriveBytes(passPhrase, saltStringBytes, DerivationIterations))
{
var keyBytes = password.GetBytes(Keysize / 8);
using (var symmetricKey = new RijndaelManaged())
{
symmetricKey.BlockSize = 256;
symmetricKey.Mode = CipherMode.CBC;
symmetricKey.Padding = PaddingMode.PKCS7;
using (var decryptor = symmetricKey.CreateDecryptor(keyBytes, ivStringBytes))
{
using (var memoryStream = new MemoryStream(cipherTextBytes))
{
using (var cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read))
{
var plainTextBytes = new byte[cipherTextBytes.Length];
var decryptedByteCount = cryptoStream.Read(plainTextBytes, 0, plainTextBytes.Length);
memoryStream.Close();
cryptoStream.Close();
return Encoding.UTF8.GetString(plainTextBytes, 0, decryptedByteCount);
}
}
}
}
}
}
else
{
return cipherText;
}
}
private static byte[] Generate256BitsOfRandomEntropy()
{
var randomBytes = new byte[32]; // 32 Bytes will give us 256 bits.
using (var rngCsp = new RNGCryptoServiceProvider())
{
// Fill the array with cryptographically secure random bytes.
rngCsp.GetBytes(randomBytes);
}
return randomBytes;
}
public void Lock()
{
_locked = true;
}
public void Unlock()
{
_locked = false;
}
public bool IsEncrypted(string atributosClases)
{
if (atributosClases != null)
{
if(atributosClases.Length > 50)
{
return true;
}
else
{
return false;
}
}
else
{
return true;
}
}
}
在POST api控制器中,我这样做:
// POST: api/Naturalezas
[ResponseType(typeof(Naturalezas))]
public IHttpActionResult PostNaturaleza(Naturalezas naturaleza)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
naturaleza.Unlock();
db.Naturalezas.Add(naturaleza);
db.SaveChanges();
naturaleza.Lock();
return CreatedAtRoute("DefaultApi", new { id = naturaleza.idNaturaleza }, naturaleza);
}
我在此博客文章中使用此加密:
现在,这仅适用于一个表,但在另一个表中我们有20个字段,所有字段都必须加密,但是我们需要能够使用LIKE,=等搜索这20个字段。
什么是最佳解决方案(指导我的代码解决方案),能够:
答案 0 :(得分:1)
在使用中间层解决方案加密静态数据时,您将需要进行许多权衡,正如您目前所做的那样。虽然Always Encrypted确实使事情变得更加容易,并且消除了应用程序中的自定义加密代码,但自定义加密仍然存在类似的限制,例如无法进行通配符LIKE
过滤,因为它们的功能类似于加密/解密不会发生在数据库级别。
一些建议:
基本过滤仍有效
使用相同的加密密钥&amp; salt,您仍然可以执行正常WHERE x = 'y'
类型的过滤。
Shift搜索&amp;过滤到中间层
再次,权衡和&amp;可能会影响性能,但是一旦数据被解密,您就可以使用普通的旧LINQ
执行更复杂的过滤您真的需要加密该列吗?
您的数据是否未归类为PHI,PII或类似内容?考虑不对其进行加密,您可以执行普通的SQL WHERE
&amp; LIKE
过滤
答案 1 :(得分:1)
这些问题可能导致各种正确的答案。我将谈谈我将为每个问题做些什么
<强> 1。加密数据库中的所有字段。
我认为这个问题可以分为两部分:
这部分是最简单的(但不是最短的),可以通过简单的oneshot项目来解决每一行并将其更新为加密格式。此部分可以是可选的,因为您的项目将加密数据与清晰数据区分开来。
这里有两个选择,具体取决于您想要解决第一个问题的时间。最好的方法是更改从EF项目中保存数据的方式。最糟糕的是在给定时间重新运行A部分项目。
<强> 2。 DO Searches。
这里最简单,最安全的是加载所有数据并使用Linq请求它。
第3。保持业绩。
他们有很多方法,这取决于你是来自网络项目还是软件项目。我将谈谈可能涉及两者的解决方案。
但要注意!如果您不特别注意这一点,下面的每个解决方案都会增加许多安全问题。实施许多人会要求更多的修补。
最好的通用解决方案之一就是拥有一些$ cache $。它可以使用SqlChangeMonitor
(example)直接缓存您的数据库,也可以使用Entity Framework Extended从您的EF项目中延迟一段时间。
也许您可以同时使用SqlChangeMonitor
更新您的EF缓存。
示例:您的columnA,B和C为每个可能的值均匀分布数据。使用带有索引的列和每个Where
的列应该可以快速响应。
如何实现:使用Select inside或函数创建一个存储过程,返回一个表并仅从结果中查询。您需要重新设计EF项目,以便从存储过程的函数/结果集进行查询。
在登录/输入时,您的用户可以使用未加密的数据将所有数据保存在临时索引表中,并在此临时表上进行查询。如果您开发了解决方案1.A.您可以使用Keep Fixed Plan
或Keep Plan
选项,以便更好地执行。它会再次要求你重新设计你的EF项目(但这个应该更简单)。
警告:不要使用全局临时表。它将打破加密数据的重点。