从解密列更新始终加密的列

时间:2017-04-05 15:04:05

标签: asp.net sql-server ef-migrations always-encrypted

我想加密始终加密的现有数据库列。我的项目是一个使用代码的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-微软做了一些有趣的建议。

  • SSMS中的始终加密向导 - 我希望通过代码优先迁移实现我的目标,因此这个想法不合适。
  • SqlBulkCopy - 它在迁移中不起作用,因为新列只会在所有' Up'之后存在。方法运行。因此,我们无法以此方式在此方法中将数据插入此列。

无论如何,他的建议驱使我再次尝试:获取解密的值并用它们更新加密列。

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.

我试图在不加密新列的情况下这样做,并且它正常工作。

知道这个错误发生的原因吗?

1 个答案:

答案 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#中执行此操作。

  • 使用与明文表(unencryptedTable)相同的架构创建一个新表(encryptedTable),但为所需列启用加密。
  • 从unencryptedTable中选择*以在SqlDataReader中加载数据,然后使用SqlBulkCopy将其加载到使用SqlBulkCopy.WriteToServer(IDataReader)方法的encryptedTable

例如, 明文表

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();


}

希望有帮助,如果您有其他问题,请将其留在评论中。