How to read SQL Always-encrypted column from classic ASP

Len*_*Len 4 sql-server asp-classic always-encrypted

I maintain a classic ASP app (yeah, I know, we're working on it) and need to access Always Encrypted columns in SQL 2017.

I've imported the cert and tested in SSMS and PowerShell and that much works. The best I've been able to do in ASP is to get the encrypted value as a byte array. I've tried more connection string combinations than I can remember. My ASP dev box is Windows 10; the data server is SQL 2017.

cnn = "Provider=MSOLEDBSQL; DataTypeCompatibility=80; " _
    & "DRIVER={ODBC Driver 17 for SQL Server}; " _
    & "SERVER=xxxx; UID=xxxxx; PWD=xxxxx; DATABASE=xxxx; " _
    & "ColumnEncryption=Enabled; Column Encryption Setting=Enabled;"
Set oDB = CreateObject( "ADODB.Connection" )
oDB.Open cnn
set oCmd = Server.CreateObject("ADODB.Command") 
oCmd.ActiveConnection = cnn
oCmd.CommandText = "SELECT top 10 ID, Enc FROM tbl1"
set rst = oCmd.Execute()
Run Code Online (Sandbox Code Playgroud)

代码正常工作,但加密列 (Enc, varchar(50)) 作为字节数组返回。当我应该获取纯文本值时,我似乎正在获取加密值。我也试过用相同的结果调用存储过程。查询中没有过滤器,因此无需参数化。有什么想法接下来要尝试什么吗?


答:
1) 以与该 Web 应用程序的 AppPool 身份相同的用户身份导入证书。
2) 将 Web 应用程序的匿名授权设置为应用程序池标识。
3) 使用此连接字符串:

  cnn = "Provider=MSDASQL;" _
      & "Extended Properties=""Driver={ODBC Driver 17 for SQL Server};" _
      & "SERVER=***; UID=***; PWD=***;" _
      & "DATABASE=***;ColumnEncryption=Enabled;"" "
Run Code Online (Sandbox Code Playgroud)

Dav*_*oft 5

新的 Microsoft OleDb Provider for SQL Server (MSOLEDBSQL) 不支持 AlwaysEncrypted(当前)。您必须使用 ODBC,这意味着 OleDb 提供程序应该是 Microsoft OleDb Provider for ODBC (MSDASQL)。因此,您可以使用 Microsoft® ODBC Driver 17 for SQL Server 配置系统 DSN,连接字符串如下:

cnn = "Provider=MSDASQL;DSN=testdb;"
Run Code Online (Sandbox Code Playgroud)

或在 MSDASQL 连接字符串的“扩展属性”中嵌入所有 ODBC 驱动程序参数。喜欢

cnn = "Provider=MSDASQL;Extended Properties=""Driver={ODBC Driver 17 for SQL Server};Server=localhost;Database=testdb;Trusted_Connection=yes;ColumnEncryption=Enabled;"" "
Run Code Online (Sandbox Code Playgroud)

这是演练,先使用 VBScript 进行测试,然后再使用 ASP 进行测试。

从...开始:

create database testdb
go
use testdb

create table tbl1(id int, Enc varchar(200))

insert into tbl1(id,enc) values (1,'Hello')
Run Code Online (Sandbox Code Playgroud)

然后在运行 SSMS 的机器上运行 SSMS 中的列加密向导,该向导为当前用户存储证书:

列加密向导的 gif

然后是query.vbs的列表:

cnn = "Provider=MSDASQL;Extended Properties=""Driver={ODBC Driver 17 for SQL Server};Server=localhost;Database=testdb;Trusted_Connection=yes;ColumnEncryption=Enabled;"" "
Set oDB = CreateObject( "ADODB.Connection" )
oDB.Open cnn
set oCmd = CreateObject("ADODB.Command") 
oCmd.ActiveConnection = cnn
oCmd.CommandText = "SELECT top 10 ID, Enc FROM tbl1"
set rst = oCmd.Execute()
rst.MoveFirst()
msgbox( cstr(rst("Enc")) )
Run Code Online (Sandbox Code Playgroud)

可以从命令行运行:

cscript  .\query.vbs
Run Code Online (Sandbox Code Playgroud)

要从 ASP 执行此操作,您还必须根据此处的文档将证书放在 IIS 应用程序池帐户的用户证书存储中。请注意,对于所有用户,证书的相对路径必须相同。如果您最初将其配置为存储在用户的证书存储中,则无法将其存储在 IIS 机器上的机器存储中。SQL Server 存储key_path密钥并指示客户端在哪里找到证书,例如CurrentUser/my/388FF64065A96DCF0858D84A88E1ADB5A927DECE.

所以发现Column Master Key的关键路径

select name, key_path from sys.column_master_keys
Run Code Online (Sandbox Code Playgroud)

然后从拥有它的机器导出证书:

 PS C:\Windows> $path = "cert:\CurrentUser\My\388FF64065A96DCF0858D84A88E1ADB5A927DECE"
 PS C:\Windows> $mypwd = ConvertTo-SecureString -String "xxxxxxx" -Force -AsPlainText
 PS C:\Windows> Export-PfxCertificate -Cert $path -FilePath c:\temp\myexport.pfx -ChainOption EndEntityCertOnly  -Password $mypwd
Run Code Online (Sandbox Code Playgroud)

在 IIS 服务器上作为应用程序池标识用户运行,将其导入

PS C:\WINDOWS> $mypwd = ConvertTo-SecureString -String "xxxxxxx" -Force -AsPlainText
PS C:\WINDOWS> Import-PfxCertificate -FilePath C:\temp\myexport.pfx -CertStoreLocation Cert:\LocalMachine\My -Password $mypwd
Run Code Online (Sandbox Code Playgroud)

如果您使用匿名/表单身份验证,请确保您已将 IIS 匿名身份验证配置为在应用程序池身份下运行,而不是默认的 IUSR。

IIS 匿名身份验证编辑对话框

这是一个用于测试的 ASP 页面:

<!DOCTYPE html>
<html>
<body>

<p>Output :</p>

<%

Set objNetwork = CreateObject("Wscript.Network")
Response.write("The current user is " & objNetwork.UserName)

cnn = "Provider=MSDASQL;Extended Properties=""Driver={ODBC Driver 17 for SQL Server};Server=localhost;Database=testdb;Trusted_Connection=yes;ColumnEncryption=Enabled;"" "
Set oDB = CreateObject( "ADODB.Connection" )
oDB.Open cnn
set oCmd = CreateObject("ADODB.Command") 
oCmd.ActiveConnection = cnn
oCmd.CommandText = "SELECT top 10 ID, Enc FROM tbl1"
set rst = oCmd.Execute()
rst.MoveFirst()
Response.write(cstr(rst("Enc")) )

%>

</body>
</html>
Run Code Online (Sandbox Code Playgroud)