How SQL Server Always Encrypted works on client side - sql-server

I have some confusion about Always Encrypted concept. Please follow below scenario.
I have created CMK (name - CMK_01) on Machine A under CurrentUser/My folder. By this way I have Always Encrypted certificate generated on Machine A which I have exported.
After this I have created CEK (name - CEK_01) from Machine A using this CMK (CMK_01).
After this I have created table (name - TBL_01) on Machine B and used this CEK_01 to encrypt its column (name – COL_01).
Now to test Always Encrypted concept I have installed Always Encrypted certificate on Machine B and applied Column Encryption Setting = Enabled in SSMS.
After doing this I am able to insert data with parameterized query in this table (TBL_01).
I queried on this table and I found that data is in decrypted form (i.e. plain text).
After this I queried on this table from Machine C without installing certificate and I found data is in encrypted form. So basically it works perfectly from db side.
This works based on certificate installed on individual machine and depends on folder in which it installed (Current or Local computer).
If Current then it works only for user which certificate is installed and if Local then works for all the users on that machine.
Now issue comes over here,
I tried to leverage this functionality to .NET side.
So I have included Column Encryption Setting = Enabled in connection string of .NET code.
And then I deployed .NET code on server machine (Machine D).
After this I have installed always encrypted certificate on one of the user machine (Machine E) under CurrentUser/My folder.
Now when user on Machine E is trying to see the data in UI, it is giving an error. Basically encryption does not work here.
To resolve this I have created new CMK (name – CMK_01) with same certificate key but path of CMK is under LocalMachine\My.
Then I have added this new CMK value in existing CEK (CEK_01).
So now basically we have 2 CMK - same certificate key with different path and 1 CEK with 2 different CMK values which means both CMK can access this CEK.
After this we installed Always Encrypted Certificate on IIS server (under LocalMachine\My path) where our code has been deployed (Machine D).
After this error has been removed but all the users who can login to that web page they can encrypt/decrypt data because we have installed certificate on IIS server (Machine D).
Now my ask is -
Is this our implementation correct?
Is this how Always Encrypted works? I mean can't we have individual user wise encryption and decryption by installing certificate on user's individual machine so who has that certificate access that user is only able to encrypt/decrypt data otherwise rest of the users who does not have that certificate those users can only see data in encrypted form on that web page?

Related

Convert SQL Certificate and key file into one .pfx

From a database certificate backup with private key, I'm looking to convert the two files into one .pfx file, to store it in a secure key vault. Currently using SQL server 2019
I look at the certs currently in use within the master database
`SELECT C.name,
C.certificate_id,
C.pvt_key_encryption_type,
C.pvt_key_encryption_type_desc,
C.subject,
C.expiry_date,
C.start_date,
C.thumbprint
FROM sys.certificates AS C;`
CertName1 appears on this list, encrypted by the master database
Take a backup of the certificate with private key
`BACKUP CERTIFICATE CertName1
TO FILE ='C:\temp\CertName1.crt'
WITH PRIVATE KEY(
FILE = 'C:\temp\CertName1.key',
ENCRYPTION BY PASSWORD = 'MadeUpPassword101!'
);`
This creates two files CertName1.crt, CertName1.key
Using certutil within Windows, I try to combine the files into one .pfx
`certutil -mergepfx CertName1.crt CertName1.pfx`
I get an error saying ASN1 bad tag value met. Doing some research, this error means the key doesn't match the certificate.
Will I need to use the master key instead, which pretty much makes this useless as will need the private key to restore the encrypted database? Or is this something that can't be done with SQL certificates?

I need help to set up ColdFusion to work with MS SQL Always Encrypted columns

I enabled Always Encrypted option on a varchar column in database (MS SQL 2019).
Now, ColdFusion (2016) application is generating an error ByteArray objects cannot be converted to strings.
How to enable or register encryption for this database in the CF 2016 server?
UPDATE:
Thanks to Miguel-F comment, I went with this guide below to enable Always Encrypted and configuration of data source in CF Administrator.
https://community.hostek.com/t/sql-server-always-encrypted-columns/315#heading--ColdFusion
But, I stack on the paragraph under the heading
Using Always Encrypted Columns with ColdFusion
....
You must also ensure that the ColdFusion service account has access to the
private key. This service usually runs under the ‘cfusion’ user so you will
want to give read permissions for that user to the private key of the
‘Column Master Key’.
Do I need to create a standard user and login as that user and assign to the service ColdFusion 2016 Application Server? Does this service is reffered here as a "cfusion"?
Then, how would I give that user read permissions for the private key of the ‘Column Master Key’? Is that running certlm?
The column that I encrypted with Always Encrypted option is nvarchar(50), when encrypted, the collation changed to Latin1_General_BIN2.
Still getting this error while open the page with the reference to the column
ByteArray objects cannot be converted to strings.
Any help would be greatly appreciated.
Gena

SSL / certificate validation error in spite of TrustServerCertificate=true in connection string

At first, please note that I am aware that this question has already been asked several times. However, the accepted (and non-accepted) solutions given so far did not work in my case, so something substantial must have changed since then, which hopefully justifies asking again.
Having said this:
I am currently trying to upgrade an Access 2010 .adp application to Access 2019 .accdb. The application includes a lot of VBA code which uses ADO objects to connect with and operate on Microsoft SQL server (currently: 2008 R2, but will be upgraded soon).
I'd like to keep the most part of the code, which means to stick with ADO, so the way to go is the new OleDB SQL server driver (which has been undeprecated / newly released in 2018). The SQL server runs on another machine than my client application.
I am not able to establish a connection to SQL server from VBA. When executing the following code snippet
Dim cnTemp As Connection
Set cnTemp = New Connection
cnTemp.CursorLocation = adUseServer
cntemp.Open "Provider=MSOLEDBSQL;Server=dbserver.example.com;Initial Catalog=MyDB;Authentication=SqlPassword;User ID=sa;Password=secret;DataTypeCompatibility=80;"
I get the following error when the last line is executed:
SSL Provider: The certificate chain was issued by an authority which is not trusted.
OK, no problem, after all we have found all the other questions dealing with the same issue, all suggesting the same solution: Add Trust Server Certificate=True; to the connection string.
Well, tried that, but -to my surprise- still the same situation. Then I tried some other variants like TrustServerCertificate=True; or using true instead of True, but to no avail. I also tried adding Use Encryption for Data=True; which didn't help either (that could be expected). Furthermore, I tried some of the snippets I had found when researching the problem, but which are not documented by Microsoft as being valid in ADO connection strings (like Encrypt=true or Trusted_Connection=true;); of course, that made the situation worse, raising other error messages.
I have understood that I could solve that problem by putting the SQL server certificate into the client's trusted root certificate store, or by having SQL server use a certificate which has been issued by a known, trusted CA (e.g. Let's Encrypt).
However, I'd strongly like to know why adding Trust Server Certificate=true; to my connection string does not make the error go away and what I have to put in there to disable certificate validation (and by the way, I would be grateful if we wouldn't start a discussion about why this would be bad; this is just development and testing in a trusted, closed network, and I am aware of possible risks).
The reason TrustServerCertificate=True in the connection string is not honored is twofold. One is that it isn't a valid ADO classic (ADODB) connection string keyword. According to the ActiveX Data Objects (ADO) Connection String Keywords documentation, the keyword/value pair should be Trust Server Certificate=True (note spaces). The keyword is ignored entirely without the spaces and not trusted as a result.
However, this change alone will not trust the certificate because of the Authentication-SqlPassword specification. When the Authentication keyword is specified, the documentation footnote calls out:
To improve security, encryption and certificate validation behavior is
modified when using Authentication/Access Token initialization
properties or their corresponding connection string keywords. For details, see Encryption and certificate validation link.
The referenced link includes this important note:
Certificate validation can also be controlled through the Value field
of the
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\MSSQLServer\Client\SNI18.0\GeneralFlags\Flag2
registry entry. Valid values are 0 or 1. The OLE DB driver chooses the
most secure option between the registry and the connection
property/keyword settings. That is, the driver will validate the
server certificate as long as at least one of the registry/connection
settings enables server certificate validation.
So even with Trust Server Certificate=True, the cert will be validated when this registry value is set to 0.
One solution is to simply remove the Authentication=SqlPassword specification as long as you don't need the improved security provided by not trusting the server certificate:
cntemp.Open "Provider=MSOLEDBSQL;Server=dbserver.example.com;Initial Catalog=MyDB;User ID=sa;Password=secret;Trust Server Certificate=True;DataTypeCompatibility=80;"
At first, I'd like to state that all credit goes to #Dan Guzman. It's his answer / comment which provided the solution.
However, I'd like to add some background, based on research I've done since posting my question.
The problem is that Microsoft's documentation obviously is wrong. Please have a look at the following document:
https://learn.microsoft.com/en-us/sql/connect/oledb/applications/using-connection-string-keywords-with-oledb-driver-for-sql-server?view=sql-server-2017#table3_1
It is located in the section SQL Server 2017 -> OLE DB -> Applications -> Using connection string keywords with OLE DB Driver for SQL server, so it should be the right one. It is divided into three sections; in the context of this question, the last table is what we're interested in, because only this one relates to connection strings with ADO.
That last table explicitly shows that Authentication=SqlPawword is valid in ADO / OLE DB connection strings (reformatting mine, no content altered):
Authentication SSPROP_AUTH_MODE Specifies the SQL or Active
Directory authentication used. Valid values are:
(not set): Authentication mode determined by other keywords.
ActiveDirectoryPassword: Active Directory authentication using login ID and password.
ActiveDirectoryIntegrated: Integrated authentication to Active Directory using the currently logged-in user's Windows account
credentials.
NOTE: It's recommended that applications using Integrated Security (or Trusted_Connection) authentication keywords or their corresponding
properties set the value of the Authentication keyword (or its
corresponding property) to ActiveDirectoryIntegrated to enable new
encryption and certificate validation behavior.
SqlPassword: Authentication using login ID and password.
NOTE: It's recommended that applications using SQL Server authentication set the value of the Authentication keyword (or its
corresponding property) to SqlPassword to enable new encryption and
certificate validation behavior.
It also says (again, formatting mine, no content altered):
Trust Server Certificate SSPROP_INIT_TRUST_SERVER_CERTIFICATE
Accepts the strings "true" and "false" as values. The default value
is "false", which means that the server certificate will be validated.
Every reasonable human being will understand this in the sense that Trust Server Certificate=true will disable certificate validation.
But when you look here
https://learn.microsoft.com/en-us/sql/relational-databases/native-client/applications/using-connection-string-keywords-with-sql-server-native-client?view=sql-server-2017
you'll notice that this document is structured like the first one, and that the last table does not mention the Authentication parameter.
However, this document is located in SQL Server 2017 -> Development -> SQL Server Native Client -> Applications -> Using Connection String Keywords. That means that it is not relevant for our case because it relates to SQL server native client (and not OLE DB), but it provides the correct information.
So we have the right document which provides the wrong information and an irrelevant document which provides the right information. Congratulations, Microsoft, you have made me waste a whole day again ...
Furthermore, I have found the following document:
https://learn.microsoft.com/en-us/sql/connect/oledb/features/using-azure-active-directory?view=sql-server-2017#encryption-and-certificate-validation
Reading the title ("Using Azure Active Directory"), it should relate to Azure only. However, I suspect that the following section relates to local SQL server installations as well (formatting mine, no content altered):
Certificate validation
To improve security, the new connection properties/keywords respect
the TrustServerCertificate setting (and its corresponding connection
string keywords/properties) independently of the client encryption
setting. As a result, server certificate is validated by default.
Note
Certificate validation can also be controlled through the Value field
of the
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\MSSQLServer\Client\SNI18.0\GeneralFlags\Flag2
registry entry. Valid values are 0 or 1. The OLE DB driver chooses the
most secure option between the registry and the connection
property/keyword settings. That is, the driver will validate the
server certificate as long as at least one of the registry/connection
settings enables server certificate validation.
So it could well be that we also have to change values in the registry to finally disable certificate validation when connecting to SQL server via ADO / OLE DB.

How to perform insert, update and delete operations, on encrypted column in a table in SQL server 2017

My name is Hari varma, I am working as a SQL DBA.
Currently I am facing issues with Always Encryption in SQL Server 2017 Development Edition in our testing server.
I don't have any experience in Always Encryption and TDE. One of my client asked me to do encryption on database.
I have done some testing on Always Encryption and I was able to encrypt and decrypt the column data by using doing the following:
On the SQL Server instance
-->Options-->Additional connection Parameter-->Column Encryption Setting = Enabled
After I enabled the column encryption I am able to view the encrypted data in the table.
However I am not able to insert, update, and delete data in this encrypted column.
Also I need to be able to set permissions on users who are allowed / not allowed to view the data on this encrypted column.
Which permissions I need to give on a particular user and provide any prerequisites for Always Encryption and TDE.
First of all it's important to understand that your SQL Server instance does not know the keys used for encrypting and decrypting data when using Always Encrypted. The keys are kept externally, usually in a key store such as Windows Certificate Store or Azure Key Vault. This means that SQL Server itself cannot encrypt or decrypt the data - this instead has to be done by a client application that has access to the keys.
However I am not able to insert, update, and delete data in this encrypted column.
I assume you are attempting to insert, update, and delete data directly through SSMS or something similar. This is only possible to a limited extent. This is because SSMS (which is your client application in this case) needs to be able to encrypt the data before sending it to your SQL Server.
Read more about inserting data into columns that are encrypted via Always Encrypted in SQL Server here (using SSMS).
A brief summary of how to insert encrypted data via SSMS:
You need to enable the column encryption setting in your connection string. This is done under Options>>Additional Connection Parameters when you connect to your SQL Server instance in SSMS. Add this text in there: Column Encryption Setting=Enabled
Once you've connected to your database and opened a query window, you need to enable parameterization for always encrypted. This is done in SSMS under Query>>Query Options>>Execution>>Advanced>>Enable Parameterization for Always Encrypted.
When you've completed the two steps above you'll be able to insert data into an encrypted column like this:
DECLARE #ParameterToBeEncrypted NVARCHAR(100) = 'Decrypt me';
INSERT INTO dbo.MyTable(MyEncryptedColumn) VALUES (#ParameterToBeEncrypted);
This works because your client application (SSMS) is able to encrypt the value that you're initializing #ParameterToBeEncrypted with before sending it to SQL Server. This only works if your current user has access to the column encryption key. SQL Server will never see the plain/non-encrypted value ('Decrypt me') - it will only see the encrypted value that should be inserted into the encrypted column.
Which permissions I need to give on a particular user and provide any prerequisites for Always Encryption
It's a combination of permissions in SQL Server and being able to access the keys used for encrypting and decrypting the data. The necessary database permissions are VIEW ANY COLUMN MASTER KEY DEFINITION and VIEW ANY COLUMN ENCRYPTION KEY DEFINITION.
You can read more about the necessary permissions here.
If you want to encrypt a set of existing data in your database, then your best bet is to write your own client application (e.g. in C# or similar) or create a SSIS package (which would serve as a client application). The client application or SSIS package should read the data from the database, encrypt the data outside of the database, and then send it back to the database as encrypted data.

Absent symmetric key and master key after restoring to new server

I had a SQL database on a previous server, of which I had a master key and certificate creating using the following syntax:
CREATE MASTER KEY ENCRYPTION BY PASSWORD = 'AReallyReallyReallySecurePassword!!!!!'
CREATE CERTIFICATE CPCertificate01 WITH SUBJECT = 'CP Certificate'
CREATE SYMMETRIC KEY SSN_Key_01 WITH ALGORITHM = TRIPLE_DES ENCRYPTION BY CERTIFICATE CPCertificate01
I've done a backup of this database, and now restored it onto a new server (fresh install of SQL Server as well).
When I try to run commands against the database, I get this error:
Cannot find the symmetric key 'SSN_Key_01', because it does not exist
or you do not have permission.
However if I run this code...
select * from sys.symmetric_keys
...I can see SSN_Key_01 listed in the result set.
I also get other errors relating the master key not existing.
Can anyone please guide me as to how I can recreate the encryption settings on the new server without losing any of my data? I still have access to the old server if required. Thanks.

Resources