Reputation: 51
I am using .NET DirectoryServices and DirectoryServices.AccountManagement in my C# CLR code. I want to publish to my SQL Server database as an Unsafe CLR stored procedure. In Visual Studio, I signed my CLR project. I want to avoid turning Trustworthy ON. How do I also sign the assemblies? I know this sounds pretty simple, but I have searched 4 hours on google without coming up with an answer. I thought I was pretty close when I downloaded Stairway to CLR, but couldn't find the answer there.
Upvotes: 5
Views: 3703
Reputation: 3439
We have several older projects for which this is needed. Up until this week when I took the time to figure this out, we had been using the dreaded Trustworthy flag.
After a bunch of research I was able to set this up to be fairly painless by using a Pre-Deployment sql script in my Visual Studio project and some one-time setup. This article helped me the most.
I have tested and confirmed this works in:
Steps:
Create a new strong name key using Developer command prompt for Visual Studio and typing:
sn -k ".\CLRKey.snk"
In your Visual Studio project settings, in the SQLCLR area, click the Signing button and choose to sign your assembly by browsing to the key file you just created:
Copy CLRKey.snk
to C:\
of each target SQL server, or some other path that the server can read.
Add this Pre-Deployment script to your Visual Studio project, adjusting the key path and password to fit your environment:
DECLARE @sp nvarchar(255) = '$(DatabaseName)..sp_executesql';
DECLARE @create_user nvarchar(4000) = N'CREATE USER [CLRExtensionLogin] FOR LOGIN [CLRExtensionLogin]'
DECLARE @MSG varchar(8000)
use master;
BEGIN TRY
BEGIN TRAN
IF NOT EXISTS (select 1 from sys.asymmetric_keys where name=N'CLRExtensionKey') BEGIN
create asymmetric key CLRExtensionKey
from file = 'c:\CLRKey.snk'
encryption by password = '<invent a password>'
RAISERROR( 'Key created.', 0, 1 ) WITH NOWAIT
END ELSE BEGIN
RAISERROR( 'Key exists.', 0, 1 ) WITH NOWAIT
END
IF NOT EXISTS (SELECT * FROM sys.syslogins WHERE name=N'CLRExtensionLogin') BEGIN
CREATE LOGIN CLRExtensionLogin FROM ASYMMETRIC KEY CLRExtensionKey;
EXEC @sp @create_user
RAISERROR( 'User created.', 0, 1 ) WITH NOWAIT
END ELSE BEGIN
RAISERROR( 'User exists.', 0, 1 ) WITH NOWAIT
END
GRANT EXTERNAL ACCESS ASSEMBLY, UNSAFE ASSEMBLY TO [CLRExtensionLogin];
COMMIT TRAN
SET @MSG = 'Assembly access granted.' + CHAR(10) + 'Done.'
RAISERROR( @MSG, 0, 1 ) WITH NOWAIT
END TRY
BEGIN CATCH
SET @MSG = ERROR_MESSAGE() + CHAR(10) + 'Aborted.'
RAISERROR( @MSG, 16, 1 )
IF @@TRANCOUNT > 0 ROLLBACK
END CATCH
USE [$(DatabaseName)];
As long as SQL Server can read in that key file the first time the script runs, you should be able to Publish to the same server freely without having to worry about all of this again.
If you have other projects that publish to the same server, all you have to do is sign them with the same key like you did in step 2, but it might be a best practice to link to that same Pre-Deployment script in all your projects in case you want to publish one to a different server at some point. Hope this helps!
Upvotes: 3
Reputation: 48826
First: Thank you for not going the easy route and enabling TRUSTWORTHY
. Seriously. It is not that much work to do this correctly, so I (and your employer) appreciate you doing more research, and when you couldn't get any farther, reaching out for help 😺.
If you are referring to the "Stairway to SQLCLR" series on SQL Server Central, I am the author of those articles. I am sorry if the information was not easily understood. I will outline the steps here:
Depending on what you are doing and the overall requirements, you have some options here:
[master]
, marked as SAFE
, to create the Asymmetric Key from it (you won't execute the code here so it doesn't need to be marked as UNSAFE
here).UNSAFE
), then I find it best to create a separate, empty Assembly, also signed with the same Strong Name Key file (i.e. the .pfx
file) used for the main Assembly. I then load that, empty yet signed, Assembly into [master]
, marked as SAFE
Once you have an Assembly created in [master]
that was signed with the same .pfx
file that was used to sign your main, UNSAFE
Assembly, then you can simply create, in [master]
, an Asymmetric Key from that Assembly.
Create a Login from that Asymmetric Key
Grant that Key-based Login the UNSAFE ASSEMBLY
permission
These steps work for SQL Server 2005 - 2016, and I provide instructions on how to automate this (Option 2 above -- using a separate, empty, signed Assembly) in Visual Studio in the Stairway to SQLCLR Level 7: Development and Security article. (btw, I do realize that the steps described in Level 7 are admittedly a bit much, but they do provide a means of automation using Visual Studio AND they can be simplified by using T4 templates -- which comes with VS -- but I haven't had time yet to write up the simplified approach -- hopefully soon, though).
IF, however, you need to account for SQL Server 2017 (or newer, presumably), then the steps above will not work as not even SAFE
Assemblies can be created anymore without being signed and having the corresponding signature-based Login that has the UNSAFE ASSEMBLY
permission. And, unfortunately, CREATE ASYMMETRIC KEY
does not allow for creating from a hex bytes string / VARBINARY
literal, so you need to use a Certificate which does allow for being created that way. Along those lines, I have two blog posts which detail, step-by-step, how to accomplish this, fully automated via Visual Studio (or even not using Visual Studio):
Whatever you do, do not get fooled into using the new "Trusted Assemblies" feature of SQL Server 2017 as there are numerous problems with it, as described here: SQLCLR vs. SQL Server 2017, Part 4: “Trusted Assemblies” – The Disappointment and one more post that I am working on now.
To help improve this needlessly painful process, please support my enhancement requests to improve CREATE ASYMMETRIC KEY
and SSDT:
Upvotes: 5