CREATE CERTIFICATE [cert_keyring_demo]
WITH SUBJECT = 'key ring demo'
go
CREATE SYMMETRIC KEY [symkey_keyring_demo]
WITH ALGORITHM = AES_192
ENCRYPTION BY CERTIFICATE [cert_keyring_demo]
go
CREATE USER [lowpriv_user] WITHOUT LOGIN
go
CREATE PROC [sp_openkey]
-- We will be runnign this module under an impersonated context
WITH EXECUTE AS OWNER
AS
OPEN SYMMETRIC KEY [symkey_keyring_demo]
DECRYPTION BY CERTIFICATE [cert_keyring_demo]
-- Notice that the key is not being closed on purpose
--
go
-- Grant minimum privielges
--
GRANT EXECUTE ON [dbo].[sp_openkey] TO [lowpriv_user]
GRANT VIEW DEFINITION ON SYMMETRIC KEY::[symkey_keyring_demo] TO [lowpriv_user]
go
EXECUTE AS USER = 'lowpriv_user'
go
SELECT * FROM sys.openkeys
go
-- fails with error 15151
--
OPEN SYMMETRIC KEY [symkey_keyring_demo]
DECRYPTION BY CERTIFICATE [cert_keyring_demo]
go
-- This will succeed
--
EXEC [dbo].[sp_openkey]
go
-- And we can verify that the key is opened on our session.
SELECT * FROM sys.openkeys
go
-- and we can encrypt & decrypt
declare @blob varbinary(1000)
declare @pt varchar(1000)
SET @blob = encryptbykey( key_guid( 'symkey_keyring_demo'), 'data' )
SET @pt = convert( varchar(1000), decryptbykey( @blob ))
SELECT @pt, @blob
go
-- We can swithc context
REVERT
go
-- and verify that the key ring is still opened
SELECT * FROM sys.openkeys
go
-- And the key remains opened until we close it
-- or we terminate the session
--
CLOSE SYMMETRIC KEY symkey_keyring_demo
CREATE TABLE [dbo].[tabl_keyring_demo]( id int IDENTITY PRIMARY KEY,
data varbinary(1000), LastUsedDate datetime )
go
OPEN SYMMETRIC KEY [symkey_keyring_demo]
DECRYPTION BY CERTIFICATE [cert_keyring_demo]
go
INSERT INTO [dbo].[tabl_keyring_demo]
VALUES ( encryptbykey( key_guid( 'symkey_keyring_demo'), 'lowpriv_user' ), GetDate())
INSERT INTO [dbo].[tabl_keyring_demo]
VALUES ( encryptbykey( key_guid( 'symkey_keyring_demo'), 'outdated_user' ), GetDate())
go
CLOSE SYMMETRIC KEY [symkey_keyring_demo]
go
CREATE PROC [sp_keyring_demo2]( @id int )
WITH EXECUTE AS OWNER
AS
-- 改存储过程目的为解密数据,
-- 并在退出存储过程前关闭密钥
--
declare @username varchar(1000)
if( EXISTS(SELECT count(*) FROM [dbo].[tabl_keyring_demo] WHERE Id = @id))
BEGIN
OPEN SYMMETRIC KEY [symkey_keyring_demo]
DECRYPTION BY CERTIFICATE [cert_keyring_demo];
SELECT @username = convert( varchar(1000), decryptbykey( data ))
FROM [dbo].[tabl_keyring_demo] WHERE Id = @id;
-- For demonstration purposes, I will add a DDL statetemnt that I know
-- will fail the second time I execute the module
if( @username is not null )
BEGIN
EXECUTE AS USER = @username
-- do something interesting under this context
REVERT
END
-- Updating LastUsedDate column
UPDATE [dbo].[tabl_keyring_demo] SET LastUsedDate = GetDate() WHERE Id = @id;
CLOSE SYMMETRIC KEY [symkey_keyring_demo];
END
go
GRANT EXECUTE ON [dbo].[sp_keyring_demo2] TO [lowpriv_user]
go
--Let's give it a try
EXECUTE AS USER = 'lowpriv_user'
go
-- This one will work as expected
--
EXEC [dbo].[sp_keyring_demo2] 1
go
-- and no keys in the key ring
--
SELECT * FROM sys.openkeys
go
--Clean up
CLOSE SYMMETRIC KEY [symkey_keyring_demo];
go
REVERT
go
注意,上面的代码设不当,使密钥打开的时间过长,代码的错误处理也可能失败。下面是改进代码:
ALTER PROC [sp_keyring_demo2]( @id int )
WITH EXECUTE AS OWNER
AS
-- The intention of this SP is to decrypt data, but close the key
-- before leaving the module frame
--
declare @username varchar(1000)
if( EXISTS(SELECT count(*) FROM [dbo].[tabl_keyring_demo] WHERE Id = @id))
BEGIN
SELECT @username = convert( varchar(1000), DecryptByKeyAutoCert( cert_id('cert_keyring_demo'), null, data ))
FROM [dbo].[tabl_keyring_demo] WHERE Id = @id;
-- For demonstration purposes, I will add a DDL statetemnt that I know
-- will fail the second time I execute the module
if( @username is not null )
BEGIN
BEGIN TRY
EXECUTE AS USER = @username
-- do something interesting under this context
REVERT
END TRY
BEGIN CATCH
print 'Unexpected error'
print error_message()
-- add code to handle error properly here
--
RAISERROR( 'Error in impersonated context', 16, 1 )
END CATCH
END
-- Updating LastUsedDate column
UPDATE [dbo].[tabl_keyring_demo] SET LastUsedDate = GetDate() WHERE Id = @id;
END
go
-- succeeds
--
EXEC [dbo].[sp_keyring_demo2] 1
go
-- fails gracefully
--
EXEC [dbo].[sp_keyring_demo2] 2
go