SQL Server Always Encrypted搭配JDBC失效问题求助
Always Encrypted配置验证及插入数据类型冲突问题
背景配置情况
我正在开发企业级应用,需启用Always Encrypted。目前已完成的配置:
数据库端(DBA/生产IT团队)
- 创建Azure Key Vault
- 在Azure AD中创建服务主体/应用注册
- 在KeyPass中注册客户端ID与密钥
- 在Key Vault层面添加角色分配
- 通过SSMS生成并执行了Column Master Key(CMK)脚本:
USE [DE_NAME] /****** Object: ColumnMasterKey [*****] Script Date: 23/12/2022 17:30:17 ******/ CREATE COLUMN MASTER KEY [*****] WITH ( KEY_STORE_PROVIDER_NAME = N'AZURE_KEY_VAULT', KEY_PATH = N'https://xxxxxxxxx.vault.azure.net/keys/******/####' ) GO
应用端
- Spring Boot + Java 8
- 使用mssql驱动版本:9.2.1.jre8
- 已将
mssql-jdbc_auth-9.2.1.x64.dll与sqljdbc_auth.dll添加至-Djava.library.path - 按官方文档配置了Always Encrypted相关连接参数
疑问1:当前数据库是否已完成Always Encrypted配置?
仅创建CMK不代表Always Encrypted配置完成,还需完成以下关键步骤:
- 在数据库中创建Column Encryption Key (CEK),并指定使用已创建的CMK进行加密
- 对目标表的特定列(比如测试代码中的
eee列)设置加密属性,指定加密类型、算法及关联的CEK
测试代码与报错信息
测试插入代码
try (Connection sourceConnection = DriverManager.getConnection("jdbc:sqlserver://server;DatabaseName=db_name;encrypt=true;trustServerCertificate=true;integratedSecurity=true;columnEncryptionSetting=Enabled;keyVaultProviderClientId=xxxxxxxx-xxxx-xxxx-xxxx-xx71xx1axxd3;keyVaultProviderClientKey=XXX7X~XXXMv21mXqXxX4FBi7XoXmxxxXDSZXw")) { PreparedStatement stmt = sourceConnection.prepareStatement("INSERT INTO Table_name(id, aaa, bbb, ccc, ddd, eee) VALUES (?, ?, ?, ?, ?, ?)"); stmt.setInt(1, 999999); stmt.setString(2, "local_test_0"); stmt.setInt(3, 989898); stmt.setString(4, "local_test_0"); stmt.setString(5, "local_test_0"); stmt.setString(6, "local_test_0");// 该列已加密 stmt.executeUpdate(); } catch (SQLException e) { e.printStackTrace(); }
报错信息
com.microsoft.sqlserver.jdbc.SQLServerException: Operand type clash: varchar(12) encrypted with (encryption_type = 'DETERMINISTIC', encryption_algorithm_name = 'AEAD_AES_256_CBC_HMAC_SHA_256', column_encryption_key_name = 'CEK-XXXX-Shared-XX-XX', column_encryption_key_database_name = 'XXXX') collation_name = 'SQL_Latin1_General_CP1_CI_AS' is incompatible with nvarchar(132) encrypted with (encryption_type = 'DETERMINISTIC', encryption_algorithm_name = 'AEAD_AES_256_CBC_HMAC_SHA_256', column_encryption_key_name = 'CEK-XXXX-Shared-XX-XX', column_encryption_key_database_name = 'XXXX') at com.microsoft.sqlserver.jdbc.SQLServerException.makeFromDatabaseError(SQLServerException.java:262) at com.microsoft.sqlserver.jdbc.SQLServerStatement.getNextResult(SQLServerStatement.java:1632) at com.microsoft.sqlserver.jdbc.SQLServerPreparedStatement.doExecutePreparedStatement(SQLServerPreparedStatement.java:602) at com.microsoft.sqlserver.jdbc.SQLServerPreparedStatement$PrepStmtExecCmd.doExecute(SQLServerPreparedStatement.java:524)
问题核心
插入时出现加密后varchar与nvarchar类型冲突,怀疑驱动未在插入前执行客户端加密,寻求解决方案。
解决方案步骤
1. 补全数据库端Always Encrypted配置
- 确认已创建CEK:执行以下SQL检查
SELECT * FROM sys.column_encryption_keys
若未创建,需执行类似脚本创建CEK(关联已有的CMK):
CREATE COLUMN ENCRYPTION KEY [CEK-XXXX-Shared-XX-XX] WITH VALUES ( COLUMN_MASTER_KEY = [你的CMK名称], ALGORITHM = 'RSA_OAEP', ENCRYPTED_VALUE = 加密后的CEK值(由SSMS生成或手动生成) ) GO
- 确认目标列的加密配置:执行以下SQL检查
eee列的加密属性是否正确
SELECT name, encryption_type, encryption_algorithm_name, column_encryption_key_name FROM sys.columns WHERE is_encrypted = 1 AND object_id = OBJECT_ID('Table_name')
2. 修复数据类型不匹配问题
报错提示加密后的varchar(12)与nvarchar(132)不兼容,说明参数类型与列定义不匹配:
- 检查数据库中
eee列的类型:若列是varchar类型,在连接字符串中添加sendStringParametersAsUnicode=false,强制驱动将字符串参数按varchar发送,避免默认转成nvarchar - 若列是
nvarchar类型,改用stmt.setNString(6, "local_test_0")代替setString,确保参数类型与列定义一致
3. 修正连接字符串配置
- 移除
integratedSecurity=true:该参数是Windows身份验证,与服务主体(Azure AD)身份验证冲突,保留keyVaultProviderClientId和keyVaultProviderClientKey即可 - 确认
columnEncryptionSetting=Enabled已正确设置,确保驱动启用客户端加密
4. 验证Key Vault权限与驱动依赖
- 确认服务主体拥有Azure Key Vault的Crypto User角色权限,确保驱动能获取CMK解密CEK
- 检查maven依赖:确保
mssql-jdbc的依赖正确,9.x版本驱动已集成Azure KV支持,无需额外引入KV依赖,但需确保classpath中无冲突依赖
内容的提问来源于stack exchange,提问作者M-BNCH
相关产品推荐
相关产品推荐

