對稱祕鑰對數據加密解密使用了ENCRYPTBYMKEY、DECRYPTBYKEY兩個函數,而非對稱祕鑰使用的加密解密函數分別是ENCRYPTBYASYMKEY、DECRYPTBYASYMKEY。本文將說明這兩個函數如何對數據加密和解密,以及如何應用於生產實踐中。
我們仍然使用《SQL Server 對稱密鑰在數據加密中的應用》中創建的數據庫,然後創建一個新表,存儲非對稱密鑰加密的數據。接着從文件中創建一個非對稱密鑰,這裏因爲前文已經在EncryptionTest 數據庫中創建了數據庫主密鑰,所以不需要再創建。關於創建非對稱密鑰的方式會在後面的文章中給大家介紹。
USE EncryptionTest
GO
CREATE TABLE [dbo].[EncryptionDataByAsy](
[id] [int] NULL,
[EncryptionCol] [varbinary](128) NULL
)
CREATE ASYMMETRIC KEY AsyKeyFromFile
FROM FILE='E:\AsmDll\pri.snk'
和對稱密鑰的加密函數ENCRYPTBYMKEY一樣,非對稱密鑰的加密函數ENCRYPTBYASYMKEY 也需要兩個參數,第一個參數爲加密數據的非對稱密鑰的編號,有兩種方式可以獲取非對稱密鑰的編號,一是查詢動態視圖 sys.asymmetric_keys ,另外一種是使用函數 ASYMKEY_ID :
SELECT asymmetric_key_id FROM sys.asymmetric_keys
WHERE name='AsyKeyFromFile'
SELECT ASYMKEY_ID('AsyKeyFromFile')
兩種方式獲得的結果相同,現在來實踐非對稱密鑰加密:
INSERT INTO EncryptionDataByAsy
VALUES(1,ENCRYPTBYASYMKEY(ASYMKEY_ID('AsyKeyFromFile'),'Jack'))
這樣就簡單的實現了數據的加密。
接下來,我們來解密數據,我們使用非對稱密鑰的解密函數DECRYPTBYASYMKEY ,此函數有三個參數,分別爲非對稱密鑰的編號,解密字符串及非對稱密鑰私鑰加密密碼(最後一個參數是可選參數,如果私鑰使用密碼加密的話,則需要加密私鑰的密碼;如果是數據庫主祕鑰加密私鑰的話,則不需此參數):
SELECT CONVERT(VARCHAR(128),DECRYPTBYASYMKEY(ASYMKEY_ID('AsyKeyFromFile'),EncryptionCol))
FROM EncryptionDataByAsy
非對稱密鑰使用數據庫主密鑰加密私鑰,解密起來太方便了,因爲只要能查看數據庫定義的用戶,都能查看到非對稱密鑰的名稱。接下來我們先改變非對稱密鑰的加密方式,使用密碼對非對稱密鑰的私鑰進行加密:
ALTER ASYMMETRIC KEY AsyKeyFromFile
WITH PRIVATE KEY(ENCRYPTION BY PASSWORD='!<Ilc/cby=`Qpae')
此時,我們再插入一行數據:
INSERT INTO EncryptionDataByAsy
VALUES(2,ENCRYPTBYASYMKEY(ASYMKEY_ID('AsyKeyFromFile'),'Jack'))
此時我們再使用上面的解密方式,看到結果均爲NULL了:
這時,我們就需要使用解密函數的第三個參數,加密非對稱密鑰私鑰的密鑰,第三個參數的數據類型是NVARCHAR:
SELECT CONVERT(VARCHAR(128),DECRYPTBYASYMKEY(ASYMKEY_ID('AsyKeyFromFile'),EncryptionCol,N'!<Ilc/cby=`Qpae'))
FROM EncryptionDataByAsy
這樣,我們要解密數據,除了需要知道是使用哪個非對稱密鑰加密的,還需要知道非對稱密鑰私鑰的加密密碼,相對於前一種方案,安全有了更大的保障(只要私鑰加密密碼不泄露)。
爲應用於實踐,我們同樣將加密、解密過程封裝爲加密的存儲過程:
USE EncryptionTest
GO
--加密數據的封裝過程
CREATE PROC InsertEncryptDataByAsy
@id INT,
@EncryptionCol VARCHAR(20)
WITH ENCRYPTION AS
BEGIN
SET NOCOUNT ON
INSERT INTO EncryptionDataByAsy VALUES
(@id,ENCRYPTBYASYMKEY(ASYMKEY_ID('AsyKeyFromFile'),@EncryptionCol))
END
GO
USE EncryptionTest
GO
--解密數據的封裝過程
CREATE PROC SelectDecryptionDataWithAsyKey
WITH ENCRYPTION AS
BEGIN
SET NOCOUNT ON
SELECT
id,
EncryptionCol,
CONVERT(VARCHAR(128),DECRYPTBYASYMKEY(ASYMKEY_ID('AsyKeyFromFile'),EncryptionCol,N'!<Ilc/cby=`Qpae'))
FROM EncryptionDataByAsy
END
創建完成後,我們可以在對象資源管理器,EncryptionTest數據庫下的可編程性,存儲過程中看到這兩個加密的存儲過程:
這裏僅僅爲了說明區分加密、解密的過程,實踐中不要使用這樣顯眼的描述,以對過程進行掩飾,提高安全性。
現在我們來測試用戶使用這兩個過程,首先爲Jack用戶賦予這兩個過程的執行權限:
GRANT EXEC ON OBJECT::InsertEncryptDataByAsy TO Jack
GRANT EXEC ON OBJECT::SelectDecryptionDataWithAsyKey TO Jack
然後在Jack上下文中執行兩個存儲過程:
EXEC AS USER='Jack'
EXEC InsertEncryptDataByAsy 3,'Jack3'
EXEC SelectDecryptionDataWithAsyKey
REVERT
從結果來看,雖然過程成功執行,沒有報錯,但既沒有實現對新插入的數據進行加密,也沒有解密已經加密的數據,看來僅僅執行權限是不夠的,下面我們給用戶增加查看非對稱祕鑰定義的權限:
GRANT VIEW DEFINITION ON ASYMMETRIC KEY::AsyKeyFromFile TO Jack
加密已經成功了,所以執行加密過程的用戶只需要執行權限加查看非對稱祕鑰定義的權限即可,但解密還是不行,解密需要對非對稱祕鑰的控制權限:
GRANT CONTROL ON ASYMMETRIC KEY::AsyKeyFromFile TO Jack
這樣就可以完美的實現加密和解密了:
現在我們更換非對稱祕鑰,使用強名稱加密的dll創建:
DROP ASYMMETRIC KEY SQLCLRTestKey
CREATE ASYMMETRIC KEY AsyKeyFromDll
FROM EXECUTABLE FILE = 'E:\AsmDll\UDF_CLR.dll'
使用新的非對稱祕鑰對數據進行加密,並查看結果:
INSERT INTO EncryptionDataByAsy
VALUES(6,ENCRYPTBYASYMKEY(ASYMKEY_ID('AsyKeyFromDll'),'Jack6'))
SELECT
id,
EncryptionCol,
CONVERT(VARCHAR(128),DECRYPTBYASYMKEY(ASYMKEY_ID('AsyKeyFromDll'),EncryptionCol))
FROM EncryptionDataByAsy
發現,我們可以對新數據進行加密,但卻不能解密新、老數據了。對於已經存在的數據,如果要更換加密方式,必須要先對數據進行解密,否則你就可能丟失了原先的數據。而對於新增加的數據,因爲我們剛剛創建的非對稱祕鑰是沒有私鑰的:
所以此時並不能直接對數據進行解密。
這有一個很好的應用場景,即當我們的數據加密後不需要解密時,如賬戶密碼的明文,除自己外其他任何人都不需要知道,我們可以創建非對稱祕鑰後直接移除私鑰,或者使用從dll或程序集中創建本身就沒有的私鑰的非對稱祕鑰進行加密。