NHibernate 中始终加密映射
Always encrypted mapping in NHibernate
目前我正在使用 SQL Server 2016 来利用 Always Encrypted
功能。有几列我应该加密。我用 SQL 服务器加密了这些列。 NHibernate
可以很容易地从 SQL Server
读取数据,但是当它试图在数据库中插入数据时,它会抛出如下异常:
Operand type clash: nvarchar(4000) encrypted with (encryption_type =
'DETERMINISTIC', encryption_algorithm_name =
'AEAD_AES_256_CBC_HMAC_SHA_256', column_encryption_key_name =
'CEK_Auto1', column_encryption_key_database_name = 'EncTest') is
incompatible with nvarchar(250) encrypted with (encryption_type =
'DETERMINISTIC', encryption_algorithm_name =
'AEAD_AES_256_CBC_HMAC_SHA_256', column_encryption_key_name =
'CEK_Auto1', column_encryption_key_database_name = 'EncTest')
这是我对 NHibernate
中特定列的列映射:
<column name="DisableTxt" length="100" sql-type="NVarChar" />
我应该在 hbm
文件中定义什么映射?
我找到了解决这个问题的方法,首先我想描述一下为什么 NHibernate
不能与 Always Encrypted
中的 Encrypted Columns
一起工作:
当我们在连接字符串中启用 AlwaysEncrypted
时,ADO.NET
会在任何数据库操作之前自动执行 store procedure sp_describe_parameter_encryption
以确定哪些参数对应于数据库列使用 Always Encrypted 功能进行保护。此 sp 对字段的长度敏感,如果指定的参数长度不等于列长度,SQL 服务器将给我们以下错误:
Operand type clash: nvarchar(4000) encrypted with (encryption_type =
'DETERMINISTIC', encryption_algorithm_name =
'AEAD_AES_256_CBC_HMAC_SHA_256', column_encryption_key_name =
'CEK_Auto1', column_encryption_key_database_name = 'EncTest') is
incompatible with nvarchar(250) encrypted with (encryption_type =
'DETERMINISTIC', encryption_algorithm_name =
'AEAD_AES_256_CBC_HMAC_SHA_256', column_encryption_key_name =
'CEK_Auto1', column_encryption_key_database_name = 'EncTest')
发生这种情况是因为 NHibernate
总是为 NVarchar
列定义 4000
的参数大小(如果列长度不是 NVarchar(max)
)。所以想象一下,我们有一个长度为 30
的列,但是 NHibernate
为指定列定义了一个长度为 4000
的参数。为什么 NHibernate
这样做?
如果您查看 Nhibernate
来源的 SqlClientDriver.cs
行 146,您将看到以下评论:
// Do not override the default length for string using data from
SqlType, since LIKE expressions needs
// larger columns.
https://nhibernate.jira.com/browse/NH-3036
那么我们如何解决这个问题呢?我们可以为 NHibernate
创建一个新的 Driver
来定义准确的参数长度。 (当然如果你不关心 %%
之类的表达式)。 (我在 NHiberate 3.x
上用过这个方法)
public class NewDriver : NHibernate.Driver.Sql2008ClientDriver
{
public override IDbCommand GenerateCommand(CommandType type, SqlString sqlString, SqlType[] parameterTypes)
{
IDbCommand command = base.GenerateCommand(type, sqlString, parameterTypes);
NewDirver.SetParameterSizes(command.Parameters, parameterTypes);
return command;
}
public static void SetParamterSizes(IDataParameterCollection parameters, SqlType[] parameterTypes)
{
for(int index=0;index<parameters.Count;++index)
{
NewDriver.SetVariableLengthParameterSize((IDbDataParameter)parameters[index], parameterTypes[index]);
}
}
public static void SetVariableLengthParmaeterSize(IDbDataParameter dbParam, SqlType sqlType)
{
SqlClientDriver.SetDefaultParameterSize(dbParam, sqlType);
if(sqlType.LengthDefined && !IsText(dbParam, sqlType) && !IsBlob(dbParam, sqlType))
{
dbParam.Size = sqlType.Length;
}
if(sqlType.PrecesionsDefined)
{
dbParam.Precision = sqlType.Precision;
dbParam.Scale = sqlType.Scale;
}
}
}
目前我正在使用 SQL Server 2016 来利用 Always Encrypted
功能。有几列我应该加密。我用 SQL 服务器加密了这些列。 NHibernate
可以很容易地从 SQL Server
读取数据,但是当它试图在数据库中插入数据时,它会抛出如下异常:
Operand type clash: nvarchar(4000) encrypted with (encryption_type = 'DETERMINISTIC', encryption_algorithm_name = 'AEAD_AES_256_CBC_HMAC_SHA_256', column_encryption_key_name = 'CEK_Auto1', column_encryption_key_database_name = 'EncTest') is incompatible with nvarchar(250) encrypted with (encryption_type = 'DETERMINISTIC', encryption_algorithm_name = 'AEAD_AES_256_CBC_HMAC_SHA_256', column_encryption_key_name = 'CEK_Auto1', column_encryption_key_database_name = 'EncTest')
这是我对 NHibernate
中特定列的列映射:
<column name="DisableTxt" length="100" sql-type="NVarChar" />
我应该在 hbm
文件中定义什么映射?
我找到了解决这个问题的方法,首先我想描述一下为什么 NHibernate
不能与 Always Encrypted
中的 Encrypted Columns
一起工作:
当我们在连接字符串中启用 AlwaysEncrypted
时,ADO.NET
会在任何数据库操作之前自动执行 store procedure sp_describe_parameter_encryption
以确定哪些参数对应于数据库列使用 Always Encrypted 功能进行保护。此 sp 对字段的长度敏感,如果指定的参数长度不等于列长度,SQL 服务器将给我们以下错误:
Operand type clash: nvarchar(4000) encrypted with (encryption_type = 'DETERMINISTIC', encryption_algorithm_name = 'AEAD_AES_256_CBC_HMAC_SHA_256', column_encryption_key_name = 'CEK_Auto1', column_encryption_key_database_name = 'EncTest') is incompatible with nvarchar(250) encrypted with (encryption_type = 'DETERMINISTIC', encryption_algorithm_name = 'AEAD_AES_256_CBC_HMAC_SHA_256', column_encryption_key_name = 'CEK_Auto1', column_encryption_key_database_name = 'EncTest')
发生这种情况是因为 NHibernate
总是为 NVarchar
列定义 4000
的参数大小(如果列长度不是 NVarchar(max)
)。所以想象一下,我们有一个长度为 30
的列,但是 NHibernate
为指定列定义了一个长度为 4000
的参数。为什么 NHibernate
这样做?
如果您查看 Nhibernate
来源的 SqlClientDriver.cs
行 146,您将看到以下评论:
// Do not override the default length for string using data from SqlType, since LIKE expressions needs
// larger columns. https://nhibernate.jira.com/browse/NH-3036
那么我们如何解决这个问题呢?我们可以为 NHibernate
创建一个新的 Driver
来定义准确的参数长度。 (当然如果你不关心 %%
之类的表达式)。 (我在 NHiberate 3.x
上用过这个方法)
public class NewDriver : NHibernate.Driver.Sql2008ClientDriver
{
public override IDbCommand GenerateCommand(CommandType type, SqlString sqlString, SqlType[] parameterTypes)
{
IDbCommand command = base.GenerateCommand(type, sqlString, parameterTypes);
NewDirver.SetParameterSizes(command.Parameters, parameterTypes);
return command;
}
public static void SetParamterSizes(IDataParameterCollection parameters, SqlType[] parameterTypes)
{
for(int index=0;index<parameters.Count;++index)
{
NewDriver.SetVariableLengthParameterSize((IDbDataParameter)parameters[index], parameterTypes[index]);
}
}
public static void SetVariableLengthParmaeterSize(IDbDataParameter dbParam, SqlType sqlType)
{
SqlClientDriver.SetDefaultParameterSize(dbParam, sqlType);
if(sqlType.LengthDefined && !IsText(dbParam, sqlType) && !IsBlob(dbParam, sqlType))
{
dbParam.Size = sqlType.Length;
}
if(sqlType.PrecesionsDefined)
{
dbParam.Precision = sqlType.Precision;
dbParam.Scale = sqlType.Scale;
}
}
}