Unable to ADD SIGNATURE TO usp WITH SIGNATURE blob
-
Saturday, January 19, 2013 10:38 PM
PROBLEM
I am unable to add a signature to a proc using the [WITH SIGNATURE = signed_blob] argument. Very grateful for any pointers.
OVERVIEW
I want to create cross database access with a signed proc. However, because I do not want to share the private key, I want to sign the proc using a signed BLOB. The concept is outlined in example B here: http://msdn.microsoft.com/en-us/library/ms181700.aspx which , I cannot get to work.
TEST SCENARIO
The script below is an adaptation of John Kehavias' excellent script and uses the concepts presented in Laureniu Cristofor's blog. The script creates two DB's TargetDb and ReaderDb. TargetDb contains a populated table, a proc that reads the table, a certificate that signs the proc, and a user generated from the user in the master DB, see below. A backup of the public key is made. The master DB contains a copy of the certificate and two users, one from the certificate with required permissions and another, [MasterTestuserLoginX], with no permissions. ReaderDb contains a user for the master [MasterTestuserLoginX], a copy of the certificate, and a proc signed [unsuccessfully] using the signed_blob.
The first half of the script has to be run, upto the comment "-->>> STOP HERE", the BLOB must be copied into the readerDBx script, and then the second and final part of the script run.
-- >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
/********************************
Create two databases
*********************************/
CREATE DATABASE targetDbx
CREATE DATABASE readerDbx
GO
/********************************
Setup targetDbx
*********************************/
USE targetDbx
GO
CREATE TABLE testtbl (a int NOT NULL,
b int NOT NULL,
c int NOT NULL)
GO
-- Insert some test data.
INSERT testtbl (a, b, c) VALUES (12, 23, 34)
GO
-- Create a CERTIFICATE to sign the procedure.
CREATE CERTIFICATE cert_signature
ENCRYPTION BY PASSWORD = 'SimplePwd01'
WITH SUBJECT = 'ADD SIGNATURE demo';
GO
CREATE PROCEDURE signed_sp AS
SELECT 'Hello from: ' + DB_NAME();
SELECT a, b, c FROM targetDbx..testtbl
GO
-- Sign the procedure.
ADD SIGNATURE TO [signed_sp]
BY CERTIFICATE [cert_signature]
WITH PASSWORD = 'SimplePwd01' ;
GO
backup CERTIFICATE [cert_signature] to file = 'C:\cert_signature.cer'
GO
-- retrieve the signature from the catalogs and store the result for use below
select crypt_property [BLOB for cert signing in targetDbx]
from sys.crypt_properties where major_id = object_id('signed_sp')
-- Output from my system
-- 0x938531481351A7102D2A5DE654B00FA61A409ECA788A5FBED6B189C9CB9272958F44F213F082FA03CDCB7701F51F4FAC0BDF3E897BF48A43473332CF2CC3B140005E11E287CF7B61A7353B073683CCCBA9C54F045FD54595A8E62C27CEE658D9753C6001A78D78E8F58FFA2326819D56777F36B84D94C6CA6FA1E2184D4D6C4D
-- ###########################################################################################
-- Add Certificate to Master Database
-- ###########################################################################################
GO
USE master
GO
CREATE CERTIFICATE [SigningCertificateX]
FROM FILE = 'D:\Marko\cert_signature.cer';
GO
-- Create a login from the certificate
CREATE LOGIN [MasterSigningLoginX] FROM CERTIFICATE [SigningCertificateX];
GO
-- The Login must have Authenticate Sever access
-- per http://msdn.microsoft.com/en-us/library/ms190785.aspx
GRANT AUTHENTICATE SERVER TO [MasterSigningLoginX]
GO
-- Create a test login to test that the certificate signed procedure
-- can properly execute the target procedure without the login having
-- access to the target database or target procedure directly
CREATE LOGIN [MasterTestuserLoginX] WITH PASSWORD=N't3stp@$$', DEFAULT_DATABASE=[master], CHECK_EXPIRATION=OFF, CHECK_POLICY=OFF
GO
-- ###########################################################################################
-- Create a user in targetDbx
-- ###########################################################################################
USE targetDbx
GO
CREATE USER [signingLoginX] FOR LOGIN [MasterTestuserLoginX]
GO
-- Grant EXECUTE on the target stored procedure to the
-- certificate based login
GRANT EXECUTE ON [dbo].[signed_sp] TO [signingLoginX]
GO
/***********************************************************************************
Exec script to here, note the BLOB detail above, amend the BLOB details below, and then
run the second half of the script.
***********************************************************************************/
-->>> STOP HERE
/********************************
Setup readerDbx
*********************************/
USE readerDbx
GO
CREATE USER [testuser] FOR LOGIN [MasterTestuserLoginX]
GO
CREATE CERTIFICATE cert_sign_target from file = 'C:\cert_signature.cer'
GO
CREATE PROCEDURE proc_Crossdb
WITH EXECUTE AS OWNER
AS
select 'Hello from: ' + db_name();
exec targetDbx..signed_sp
GO
add signature to proc_Crossdb by CERTIFICATE cert_sign_target
with signature = 0x938531481351A7102D2A5DE654B00FA61A409ECA788A5FBED6B189C9CB9272958F44F213F082FA03CDCB7701F51F4FAC0BDF3E897BF48A43473332CF2CC3B140005E11E287CF7B61A7353B073683CCCBA9C54F045FD54595A8E62C27CEE658D9753C6001A78D78E8F58FFA2326819D56777F36B84D94C6CA6FA1E2184D4D6C4D ;
/* Error message here.
Msg 15299, Level 16, State 1, Line 2
The signature of the public key is invalid.
*/
GO
GRANT EXECUTE ON proc_Crossdb TO [testuser];
GO
EXECUTE AS LOGIN = 'MasterTestuserLoginX'
EXECUTE proc_Crossdb;
GO
USE master
GO
/*
USE master
DROP DATABASE targetDbx
DROP DATABASE readerDbx
DROP LOGIN MasterSigningLoginX
DROP LOGIN [MasterTestuserLoginX]
DROP CERTIFICATE [SigningCertificateX]
*/
- Edited by marko-ek Monday, January 21, 2013 6:13 AM
All Replies
-
Sunday, January 20, 2013 2:41 PM
No, that will not work out, and there is a different and simpler solution for what you want to achieve: backup the certificate with the private key and restore it the other database. Sign it, and then drop the private key from the certificate with ALTER CERTIFICATE REMOVE PRIVATE KEY.
I have a long article on my web site that discusses procedure signing at great length, including case of cross-db access:
http://www.sommarskog.se/grantperm.html
Beware that the example on cross-db access does not drop the private key; you will need to add that.I don't cover SIGNED_BLOB in my article, and I have not played with it a lot. I nevertheless believe that I understand when you would use it and how. Say that you develop an application that you ship to a customer site. Some of the procedures requires signing for whatever reason. When you send an install package, you don't want to include the private key of the certificate. Therefore, in a database at home you sign the procedure and extract the signature blob, and in the deploy script you use the SIGNED_BLOB option. Now here is the key part which explains that it not work in your case: the blob you have is only valid for exactly that procedure text you signed originally. In your example, you sign one procedure, and then try to apply that signature on a different procedure. Or more precisely on a different piece of text. If you would have the same procedure text in both databases, it would work out. Going back to the example where the procedure was deployed to a customer site, this means that the customer cannot change the procedure, and re-sign the procedure, because the old signature is no longer valid, and he does not have the private key.
Also, in the script you posted, you create the certificate login, as well as a login from that certificate which you grant Authenticate Server. There should not be any need for this if all you want is cross-database access. The only time you need to create a certificate and login in master is when you want to bundle server-level permissions in a stored procedure.
Finally, certificate signing is a very powerful way to package permissions with stored procedures in a controlled way.
Erland Sommarskog, SQL Server MVP, esquel@sommarskog.se- Marked As Answer by marko-ek Monday, January 21, 2013 10:51 AM
-
Monday, January 21, 2013 10:50 AM
Erland, many thanks for your reply. My apologies for failing to give credit to you for your excellent article Giving Permissions through Stored Procedures. In fact, I came to this site via the link you provide in that article.
Regarding your reply:
1) I agree with your assessment that my use of the [WITH SIGNATURE = signed_blob] argument above appears to be inappropriate. For interest I retried this technique but using precisely the same proc syntax in both DB's. Again it failed. If I ever discover the correct syntax, I'll post it here.
2) I amended your Cross Database script adding the [ALTER CERTIFICATE crossdbcert REMOVE PRIVATE KEY;] in db2. As you stated, this prevents the certificate being reused. I also restricted access to proc in db1, which is pretty much the solution I was looking for. Great.
3) It is a pity that in SQL 2008 there is no way to grant Cross DB access using certificates without passing the private key around? I note the solution in 2012, also documented on your site.
Your answer above is therefore the solution to my question.
TY
- Edited by marko-ek Monday, January 21, 2013 10:53 AM wrong DB (db1 --> db2)
-
Monday, January 21, 2013 10:39 PM
1) I agree with your assessment that my use of the [WITH SIGNATURE = signed_blob] argument above appears to be inappropriate. For interest I retried this technique but using precisely the same proc syntax in both DB's. Again it failed. If I ever discover the correct syntax, I'll post it here.
Probably there was some minor difference between the procedures. A space, an extra line-break - everything counts.
Here is a script that is not particularly useful, but it achieves the feat.
CREATE DATABASE db1
CREATE DATABASE db2
go
USE db1
go
CREATE PROCEDURE my_sleazy_proc AS
PRINT 'Some random garbage'
SELECT 'More contraband stuff' AS [This is enough]
go
CREATE CERTIFICATE cer
ENCRYPTION BY PASSWORD = 'Taxman'
WITH SUBJECT = 'Testing'
go
ADD SIGNATURE TO my_sleazy_proc BY CERTIFICATE cer WITH PASSWORD = 'Taxman'
go
BACKUP CERTIFICATE cer TO FILE = 'C:\temp\cer.bak'
go
USE db2
go
DECLARE @code nvarchar(MAX)
SELECT @code = sm.definition
FROM db1.sys.objects o
JOIN db1.sys.sql_modules sm ON o.object_id = sm.object_id
WHERE o.name = 'my_sleazy_proc'
EXEC(@code)
go
CREATE CERTIFICATE cer FROM FILE = 'C:\temp\cer.bak'
go
DECLARE @blob varbinary(MAX)
SELECT @blob = cp.crypt_property
FROM db1.sys.crypt_properties cp
JOIN db1.sys.objects o ON cp.major_id = o.object_id
WHERE o.name = 'my_sleazy_proc'
DECLARE @sql nvarchar(MAX) = 'ADD SIGNATURE TO my_sleazy_proc BY CERTIFICATE cer ' +
'WITH SIGNATURE=' + convert(nvarchar(MAX), @blob, 1)
PRINT @sql
EXEC(@sql)
go
USE tempdb
DROP DATABASE db1
DROP DATABASE db2
EXEC xp_cmdshell 'DEL C:\temp\cer.bak'
Erland Sommarskog, SQL Server MVP, esquel@sommarskog.se

