调用Always Encrypted SQL Server存储过程出现TDS协议错误的原因
问题
从启用Always Encrypted的SQL Server数据库调用存储过程时,遇到以下错误:
SQLServerException: The incoming tabular data stream (TDS) remote procedure call (RPC) protocol stream is incorrect. Parameter 1 (""): Data type 0xC0 is unknown.
相关代码与配置
表和存储过程创建语句
CREATE TABLE [dbo].[test_table] ( [id] [int] IDENTITY(1,1) NOT NULL, [encrypted_col] [nvarchar](255) COLLATE Latin1_General_BIN2 ENCRYPTED WITH (COLUMN_ENCRYPTION_KEY = [MY_KEY], ENCRYPTION_TYPE = Randomized, ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256') NOT NULL ) GO CREATE PROCEDURE [dbo].[test_sp] @test_param nvarchar(255) AS SELECT * from test_table where encrypted_col = @test_param GO
Java调用代码
try(CallableStatement callableStatement = connection.prepareCall("{call test_sp(?)}")) { callableStatement.setNString(1, "TEST VALUE"); ResultSet rs = callableStatement.executeQuery(); // <-- 抛出异常 }
数据库连接URL
jdbc:sqlserver://localhost:1433;applicationName=MY-APP;databaseName=test_encryption;sendStringParametersAsUnicode=false;encrypt=true;trustServerCertificate=true;columnEncryptionSetting=enabled;enclaveAttestationUrl=http://localhost/Attestation;enclaveAttestationProtocol=HGS;
备注
- 使用SQL Server 2019和最新版mssql驱动(12.6),二者兼容,降级驱动后问题依旧。
- Hibernate读写数据库表正常,连接URL和加密密钥配置正确,仅调用存储过程时出错。
- 存储过程可在SQL Server Management Studio中成功调用,数据库层面的存储过程和加密配置正确。
- 尝试多种调用存储过程的方式(Hibernate Query、Spring Jpa Query),均出现相同错误。
- 创建未加密的数据库表时,调用存储过程可正常工作。
- 已尝试将
sendStringParametersAsUnicode设为true和false,问题依旧。
原因分析与解决方案
核心原因
错误的根源是存储过程的参数未指定与加密列匹配的排序规则:
- 加密列
encrypted_col使用了Latin1_General_BIN2排序规则,但存储过程参数@test_param仅定义为nvarchar(255),未同步指定该排序规则。 - JDBC驱动在处理Always Encrypted加密列的存储过程调用时,对参数元数据的要求远高于SSMS:必须确保参数的数据类型、排序规则与加密列完全一致,否则会导致TDS协议层面的数据类型识别错误(0xC0是SQL Server内部标识带特定排序规则的Unicode字符串的类型码,但驱动无法正确解析)。
解决方案
修改存储过程,为参数添加与加密列一致的排序规则,重新编译后即可解决问题:
CREATE PROCEDURE [dbo].[test_sp] @test_param nvarchar(255) COLLATE Latin1_General_BIN2 AS SELECT * from test_table where encrypted_col = @test_param GO
逻辑验证
- 直接读写表时,JDBC驱动能从表结构中直接获取列的完整元数据(包括排序规则),自动匹配参数,因此Hibernate操作表正常。
- 未加密列对排序规则的匹配要求较低,因此未加密表的存储过程能正常运行。
内容的提问来源于stack exchange,提问作者Alexandru Severin
相关产品推荐
相关产品推荐

