4

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()

The code works without error but the encrypted column (Enc, varchar(50)) is returned as a byte array. I seem to be getting the encrypted value when I should get the plain text value. I've also tried calling a Stored Procedure with the same results. No filter in the query, so nothing to parameterize. Any ideas what to try next?


Answer:
1) Import the cert as the same user as the AppPool Identity for that web app.
2) Set Anon authorization for the web app to Application Pool Identity.
3) Use this connection string:

  cnn = "Provider=MSDASQL;" _
      & "Extended Properties=""Driver={ODBC Driver 17 for SQL Server};" _
      & "SERVER=***; UID=***; PWD=***;" _
      & "DATABASE=***;ColumnEncryption=Enabled;"" "
Len
  • 43
  • 5
  • Cert was installed in the private store under my own account. App pool is normally under AppPoolIdentity; changed it to my own account with no change in results. My understanding was that ADO used OleDB to connect through ODBC to SQL; is that wrong? I've tried several combinations of PROVIDER and DRIVER with no luck. – Len Apr 18 '19 at 15:22
  • Adding a comment to note that this connection cannot receive varChar(MAX) data. Debugger shows it receives \b instead of expected data. No solution yet other than changing the data type or connection. – Len Jun 26 '19 at 20:46

1 Answers1

5

The new Microsoft OleDb Provider for SQL Server (MSOLEDBSQL) doesn't support AlwaysEncrypted (currently). You'll have to use ODBC, which means the OleDb provider should be the Microsoft OleDb Provider for ODBC (MSDASQL). So you can either configure a system DSN using Microsoft® ODBC Driver 17 for SQL Server with a connection string like:

cnn = "Provider=MSDASQL;DSN=testdb;"

or embed all the ODBC driver parameters in the "Extended Properties" of the MSDASQL connection string. Like

cnn = "Provider=MSDASQL;Extended Properties=""Driver={ODBC Driver 17 for SQL Server};Server=localhost;Database=testdb;Trusted_Connection=yes;ColumnEncryption=Enabled;"" "

Here's walkthrough, using first VBScript for testing before testing with ASP.

Starting with:

create database testdb
go
use testdb

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

insert into tbl1(id,enc) values (1,'Hello')

Then running through the column encryption wizard in SSMS, which stores the certificate on for the current user, on the machine running SSMS:

gif of the Column Encryption Wizard

Then the listing of 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")) )

Which can be run from the commandline with:

cscript  .\query.vbs

To do this from ASP you'll additionally have to place the certificate in the user certificate store of the IIS App Pool account, per the docs here. Note that the relative path to the certificate has to be the same for all users. You can't store it in the machine store on the IIS box if you initially configured it to be stored in the user's certificate store. SQL Server stores the key_path of the key and instructs the clients where to find the certificate, eg CurrentUser/my/388FF64065A96DCF0858D84A88E1ADB5A927DECE.

So discover the key path of the Column Master Key

select name, key_path from sys.column_master_keys

Then export the certificate from the machine that has it:

 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

Running as the app pool identity user on the IIS server, import it

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

And if you're using Anonymous/Forms auth sure you've configured IIS Anonymous Auth to run under the App Pool identity, not the default IUSR.

IIS Anonymous Authentication Edit Dialog

Here's an ASP page to test with:

<!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>
David Browne - Microsoft
  • 80,331
  • 6
  • 39
  • 67
  • That's working as a VBS file, but in ASP I get this: Microsoft OLE DB Provider for ODBC Drivers error '80004005' [Microsoft][ODBC Driver Manager] The specified DSN contains an architecture mismatch between the Driver and Application The AppPool is set for 32-bit applications. – Len Apr 18 '19 at 15:16
  • You'll definitely need the to load 32bit driver. But it should be installed along with the 64bit driver. The x86 install package won't even run on 64bit Windows. May be a problem with the system path. I'll test. – David Browne - Microsoft Apr 18 '19 at 15:26
  • Tried to install msocbcsql_17.3.2.2_x86 and couldn't: "Installation of this product failed because it is not supported on this operating system." I'm on Windows 10 Enterprise. – Len Apr 18 '19 at 15:31
  • Ensure you have the 32bit driver installed in the correct location:: `dir C:\Windows\SysWOW64\msodbcsql17.dll` This folder appears as `c:\windows\system32' to 32bit processes. And ensure that there is no copy of this file where it might get loded before looking in the system folder. – David Browne - Microsoft Apr 18 '19 at 15:34
  • Especially ensure that no copy of the 64bit driver appears in 'C:\Windows\SysWOW64\inetsrv\' which is the application directory for w3wp.exe. – David Browne - Microsoft Apr 18 '19 at 15:41
  • Error on my part - accidentally created the DSN as 64-bit instead of 32. Fixed that and the connection works in ASP, but encrypted data is still a byte array. ODBC driver 17 shows as version 2017.172.00.1. Still looking into other ways to import the cert... – Len Apr 18 '19 at 16:12