2012年7月17日 星期二

無法使用指定的憑證進行解密或加密,可能是因為其中沒有私密金鑰,或是所提供的私密金鑰密碼不正確。


原本我有個預存程序使用憑證簽署過,後來為了加註說明文字得修改預存程序的內容,可是我僅僅只是加上備註而已,預存程序卻不能執行了,好像簽章失效了一般,後來我嘗試再簽署一次卻出現如標題之錯誤,經研究終於找到較佳的解決辦法,說明如下供各位參考,至於我為何會使用憑證簽署可參考如何用預存程序以最小權限原則呼叫遠端SSIS這篇

當你使用簽章簽署預存程序後,使用以下指令,可確認到所有已簽署簽章的模組資訊,此例預存程序為usp_Exectest1,憑證名稱為certTest

SELECT object_name(cp.major_id) '已簽署簽章的模組' ,
   cp.crypt_property ,
   cp.crypt_type ,
   cp.crypt_type_desc ,
   cer.name ,
   cer.pvt_key_encryption_type ,
   cer.pvt_key_encryption_type_desc ,
   cer.subject ,
   cer.expiry_date
    FROM sys.crypt_properties AS cp
    right outer JOIN sys.certificates AS cer
        ON cp.thumbprint = cer.thumbprint



注意,接下來這個動作很重要!
請抄下二進位大型物件 (BLOB) crypt_property的值!

        若之後預存程序usp_Exectest1有作修改,若再執行同樣的指令,你會發現已簽署簽章的模組等前幾欄都變NULL囉,這是因為當模組變更時,會自動卸除模組的簽章。


        模組的簽章被卸除了,就不受驗證器信任,可以想見執行這模組時權限會出問題,此時用以下命令模擬user1登入執行usp_Exectest1,會出現錯誤喔(假設原本user1可以正常執行usp_Exectest1)

USE master

EXECUTE AS LOGIN = 'user1'

SELECT SUSER_NAME(), USER_NAME()

EXECUTE dbo.usp_Exectest1
GO
/*
訊息229,層級14,狀態5,程序sp_start_job,行1
結構描述'dbo',資料庫'msdb',物件'sp_start_job' 沒有EXECUTE 權限。
*/

--記得模擬完畢切換回來
REVERT
GO

        既然我們知道是模組的簽章被卸除,那就試著用以下命令再用憑證簽署預存程序一次,結果竟然失敗,出現如標題的錯誤訊息

--使用憑證加上密碼簽署預存程序(假設原本密碼就是123456)
ADD SIGNATURE TO OBJECT::[usp_Exectest1]
      BY CERTIFICATE certTest
        WITH PASSWORD = '123456'
GO
/*
訊息15556,層級16,狀態1,行1
無法使用指定的憑證進行解密或加密,可能是因為其中沒有私密金鑰,或是所提供的私密金鑰密碼不正確。
*/

        因為之前備份憑證前,將私密金鑰移除了,所以之後簽署不讓你簽,要解決上面這個錯誤,有兩個方法,一個是如果你沒有記住剛剛那個要你抄下的BLOB的話,你就只能用重建憑證來解決,另一個就是利用BLOB重簽一次即可(我試過假使憑證沒有移除私密金鑰,重新再簽署不會遇到錯誤的)

/*
一、從檔案重建憑證(前提是憑證與私密金鑰要有備份)
*/

--不可直接從檔案重建憑證,必須加上私密金鑰才行喔
--drop certificate [certTest]
create certificate [certTest]
  from file='c:\temp\certTest_WithKey.cer'
  WITH PRIVATE KEY (FILE = 'c:\temp\certTest_WithKey.pvk',
  DECRYPTION BY PASSWORD = '123456',
  ENCRYPTION BY PASSWORD = '123456');
GO

--使用憑證加上密碼簽署預存程序
ADD SIGNATURE TO OBJECT::[usp_Exectest1]
      BY CERTIFICATE certTest
        WITH PASSWORD = '123456'
go

--確認簽署是否成功
SELECT object_name(cp.major_id) '已簽署簽章的模組' ,
   cp.crypt_property ,
   cp.crypt_type ,
   cp.crypt_type_desc ,
   cer.name ,
   cer.pvt_key_encryption_type ,
   cer.pvt_key_encryption_type_desc ,
   cer.subject ,
   cer.expiry_date
    FROM sys.crypt_properties AS cp
    right outer JOIN sys.certificates AS cer
        ON cp.thumbprint = cer.thumbprint

/*
再試著執行預存程序,成功
*/
USE master
EXECUTE AS LOGIN = 'user1'

SELECT SUSER_NAME(), USER_NAME()

execute dbo.usp_Exectest1

REVERT

/*
二、使用簽署的 BLOB 來簽署預存程序
*/

--使用之前得到的BLOB來簽署.
ADD SIGNATURE TO OBJECT::[usp_Exectest1]
      BY CERTIFICATE certTest
    WITH SIGNATURE = 0x56D5358CB3101B827A33B807D065882E220DB03E3AF71E8FD025F9D3133B8DCD4D46815E0DE99C3B0E1F3829CB3C9EBD5D78481569C21CD6C29265955835317F2B7AE5BACB87623FCE8B23F08F40187074B353F66FB0C3FD3FD17F050622410B011CB379E6C28995C2C9E60DA184330F5CA2630EE25C3BC22D2B296AD0416874
  
GO

--確認簽署是否成功
SELECT object_name(cp.major_id) '已簽署簽章的模組' ,
   cp.crypt_property ,
   cp.crypt_type ,
   cp.crypt_type_desc ,
   cer.name ,
   cer.pvt_key_encryption_type ,
   cer.pvt_key_encryption_type_desc ,
   cer.subject ,
   cer.expiry_date
    FROM sys.crypt_properties AS cp
    right outer JOIN sys.certificates AS cer
        ON cp.thumbprint = cer.thumbprint

/*
再試著執行預存程序,成功
*/
USE master
EXECUTE AS LOGIN = 'user1'

SELECT SUSER_NAME(), USER_NAME()

execute dbo.usp_Exectest1

REVERT


0 意見:

張貼留言