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

Update always encrypted column from decrypted column

我想用始终加密的方式加密现有的数据库列。我的项目是 ASP.NET 使用代码优先的项目,数据库是 SQL 服务器。数据库已有数据。我创建了一个迁移来实现我的目标。

首先,我尝试使用以下方法更改列类型。

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-Microsoft 提出了一些有趣的建议。

无论如何,他的建议促使我进行了另一次尝试:获取解密值并用它们更新加密列。

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.

我尝试在不加密新列的情况下执行此操作,并且它工作正常。

知道为什么会出现这个错误吗?

您必须在 entity framework 之外进行始终加密的相关迁移。这个博客应该有帮助 https://blogs.msdn.microsoft.com/sqlsecurity/2015/08/27/using-always-encrypted-with-entity-framework-6/

如果要加密现有列,可以使用Always Encrypted Wizard in SSMS, or use this article that explains how to migrate existing data

此外,请注意支持通过 C#(.NET 4.6.1+ 客户端)应用程序进行批量插入。

您可以在 c# 中使用 SqlBulkCopy 执行此操作,特别是使用 SqlBulkCopy.WriteToServer(IDataReader) 方法。

  • 使用与纯文本 table(未加密Table)相同的架构创建一个新的 table(加密Table),但加密已打开所需的列。
  • Do select * from unencryptedTable 将数据加载到 SqlDataReader,然后使用 SqlBulkCopy 使用 SqlBulkCopy.WriteToServer(IDataReader) 方法将其加载到加密的Table

例如, 明文Table

CREATE TABLE [dbo].[Patients](
 [PatientId] [int] IDENTITY(1,1), 
 [SSN] [char](11) NOT NULL)

加密Table

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)

至于为什么你的方法不行, 当您对始终加密使用参数化时,声明语句的右侧 (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();


}

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