我想加密始终加密的现有数据库列。我的项目是一个使用代码的ASP.NET项目,数据库是SQL Server。数据库已有数据。我创建了一个迁移来实现我的目标。
首先,我尝试使用以下内容更改列类型。
ALTER TABLE [dbo].[TestDecrypted] ALTER COLUMN [FloatCol] [float] ENCRYPTED WITH (COLUMN_ENCRYPTION_KEY = [CEK_Auto1], ENCRYPTION_TYPE = Randomized, ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256') NULL
我收到了以下错误。
Operand type clash: float is incompatible with float encrypted with (encryption_type = 'RANDOMIZED', encryption_algorithm_name = 'AEAD_AES_256_CBC_HMAC_SHA_256', column_encryption_key_name = 'CEK_Auto1', column_encryption_key_database_name = 'TestEncrypt')
然后我决定创建另一个列并迁移数据。
ALTER TABLE [dbo].[TestDecrypted] ADD [FloatCol2] [float] ENCRYPTED WITH (COLUMN_ENCRYPTION_KEY = [CEK_Auto1], ENCRYPTION_TYPE = Randomized, ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256') NULL
UPDATE [dbo].[TestDecrypted] SET [FloatCol2] = [FloatCol]
我也遇到了同样的错误。
在我查看this之后,我注意到可以插入如下的数据
DECLARE @floatCol FLOAT = 1.1
UPDATE [dbo].[TestDecrypted] SET [FloatCol2] = @floatCol
但是,如果我尝试从现有列中获取值,则会失败。
DECLARE @floatCol FLOAT = (SELECT TOP 1 FloatCol FROM TestDecrypted)
UPDATE [dbo].[TestDecrypted] SET FloatCol2 = @floatCol
错误如下。
Encryption scheme mismatch for columns/variables '@floatCol'. The encryption scheme for the columns/variables is (encryption_type = 'PLAINTEXT') and the expression near line '4' expects it to be (encryption_type = 'RANDOMIZED', encryption_algorithm_name = 'AEAD_AES_256_CBC_HMAC_SHA_256', column_encryption_key_name = 'CEK_Auto1', column_encryption_key_database_name = 'TestEncrypt').
有谁知道我怎样才能实现目标?
更新1
@Nikhil-Vithlani-微软做了一些有趣的建议。
无论如何,他的建议驱使我再次尝试:获取解密的值并用它们更新加密列。
var values = new Dictionary<Guid, double>();
var connectionString = ConfigurationManager.ConnectionStrings["MainDb"].ConnectionString;
using (var sourceConnection = new SqlConnection(connectionString))
{
var myCommand = new SqlCommand("SELECT * FROM dbo.TestDecrypted", sourceConnection);
sourceConnection.Open();
using (var reader = myCommand.ExecuteReader())
{
while (reader.Read())
{
values.Add((Guid)reader["Id"], (double)reader["FloatCol"]);
}
}
}
Sql("ALTER TABLE [dbo].[TestDecrypted] ADD [FloatCol2] [float] ENCRYPTED WITH (COLUMN_ENCRYPTION_KEY = [CEK_Auto1], ENCRYPTION_TYPE = Randomized, ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256') NULL");
foreach (var valuePair in values)
{
// The error occurs here
Sql($@"DECLARE @value FLOAT = {valuePair.Value}
UPDATE [dbo].[TestDecrypted] SET [FloatCol2] = @value WHERE Id = '{valuePair.Key}'");
}
事实上,我没有尝试创建另一个列并迁移数据,如上面的示例所述。我只在SSMS上尝试过。 现在我得到了一个不同的错误。
Transaction (Process ID 57) was deadlocked on lock resources with another process and has been chosen as the deadlock victim. Rerun the transaction.
我试图在不加密新列的情况下这样做,并且它正常工作。
知道这个错误发生的原因吗?
答案 0 :(得分:1)
您必须在实体框架之外进行始终加密的相关迁移。这个博客应该有帮助 https://blogs.msdn.microsoft.com/sqlsecurity/2015/08/27/using-always-encrypted-with-entity-framework-6/
如果您要加密现有列,可以使用Always Encrypted Wizard in SSMS,或使用这篇解释how to migrate existing data的文章。
另请注意,支持通过C#(.NET 4.6.1+客户端)应用程序进行批量插入。
您可以使用SqlBulkCopy专门使用SqlBulkCopy.WriteToServer(IDataReader)方法在c#中执行此操作。
例如, 明文表
CREATE TABLE [dbo].[Patients](
[PatientId] [int] IDENTITY(1,1),
[SSN] [char](11) NOT NULL)
加密表
CREATE TABLE [dbo].[Patients](
[PatientId] [int] IDENTITY(1,1),
[SSN] [char](11) COLLATE Latin1_General_BIN2
ENCRYPTED WITH (ENCRYPTION_TYPE = DETERMINISTIC,
ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256',
COLUMN_ENCRYPTION_KEY = CEK1) NOT NULL)
至于为什么你的方法不起作用, 当您使用参数化始终加密时,declare语句的右侧(RHS)需要是文字。因为驱动程序将识别文字并为您加密。因此,以下操作无效,因为RHS是一个sql表达式,无法通过驱动程序加密
DECLARE @floatCol FLOAT = (SELECT TOP 1 FloatCol FROM TestDecrypted)
UPDATE [dbo].[TestDecrypted] SET FloatCol2 = @floatCol
<强>更新强>
以下代码无效,因为Always Encrypted的参数化仅适用于SSMS
foreach (var valuePair in values)
{
// The error occurs here
Sql($@"DECLARE @value FLOAT = {valuePair.Value}
UPDATE [dbo].[TestDecrypted] SET [FloatCol2] = @value WHERE Id = '{valuePair.Key}'");
}
但是,如果您按如下方式重写代码,那应该可以正常工作
foreach (var valuePair in values)
{
SqlCommand cmd = _sqlconn.CreateCommand();
cmd.CommandText = @"UPDATE [dbo].[TestDecrypted] SET [FloatCol2] = @FloatVar WHERE Id = '{valuePair.Key}'");";
SqlParameter paramFloat = cmd.CreateParameter();
paramFloat.ParameterName = @"@FloatVar";
paramFloat.DbType = SqlDbType.Float;
paramFloat.Direction = ParameterDirection.Input;
paramFloat.Value = floatValue;
cmd.Parameters.Add(paramFloat);
cmd.ExecuteNonQuery();
}
希望有帮助,如果您有其他问题,请将其留在评论中。