強式名稱組件 (Strong-named Assembly)

要註冊到 GAC 的組件,都必須是強式名稱組件,而要將組件變成強式名稱組件,必須使用一組金鑰對該組件進行簽署的動作。首先必須使用 sn.exe 產生一對金鑰,例如:

sn.exe -k MyKey.snk

產生的 MyKey.snk 會包含一組公開金鑰與私密金鑰。

接著再利用這個金鑰檔簽署組件,做法是在專案屬性的「Signing」頁籤中勾選「Sign the assembly」核取方塊,並且指定金鑰的檔案名稱。

註:以往在使用 Visual Studio 2003 時,為組件簽署的做法是在專案的 AssemblyInfo.cs (或 AssemblyInfo.vb) 中加入以下 attribute 敘述:
[Assembly:AssemblyKeyFile("MyKey.snk")]

可是這種做法到了 Visual Studio 2005 會產生編譯警告:

Use command line option '/keyfile' or appropriate project settings instead of 'AssemblyKeyFile'

因此使用 Visual Studio 2005 時,建議直接使用專案的屬性視窗來簽署組件。如果您還是希望使用舊的方式,可以利用以下的方法將這個編譯警告關閉:

#pragma warning disable 1699
[assembly:AssemblyKeyFile("MyKey.snk")]
#pragma warning restore 1699

為何需要將組件延遲簽章?

由於組件的簽章代表了唯一性,對一家專門生產 .NET 元件的公司來說,所有的組件的簽章都會使用同一組金鑰,也就是說,某家公司所建立的組件都會具有相同的簽章,而經由組件的簽章,也可以得知某個組件是否是由該公司所生產的。

然而,從建立強式名稱的過程當中我們知道,開發人員必須有金鑰檔,才能建立強式名稱組件。這樣一來,公司裡面的每一個開 發人員都可以拿到代表該公司的金鑰,萬一其中有人拿金鑰暗中建立了一個含有惡意程式的組件,就會影響到該公司的信譽。換句話說,金鑰應該視為公司的機密資 料,一但外洩,很可能會被冒名頂替去做一些違法的事情。

另一種情況是,當你辛苦寫了一套元件或應用程式,希望不要被別人用逆向工程的方式看到原始碼,你可能會利用 obfuscator 工具將邊編譯過的組件重新"攪亂"一番。如果你事先就加入組件簽章,那麼組件再經過 obfuscator 工具更動過後,程式就不能執行了(.NET CLR 會發現組件被"污染"過了,而拒絕執行)。

組件延遲簽章的技術就是用來解決上述問題。

組件延遲簽章的做法

組件延遲簽章的做法,是只先用公開金鑰簽署組件,等到組件開發完成,要打包、發行產品時,才使用完整金鑰對該組件進行簽署(完整金鑰的保管人通常是是高階主管)。

舉例來說,假設某家公司限定只有開發部門的主管可以拿到金鑰,那麼該主管可以使用以下命令從金鑰檔中取出公開金鑰:

sn.exe -p MyKey.snk MyPublicKey.snk

然後把 MyPublicKey.snk 交給其他開發人員。這樣一來,開發人員就只有拿到公開金鑰。可是開發人員如果依照前面介紹的方式簽署組件,在建立組件時就會發生編譯錯誤:

Cryptographic failure while signing assembly 'MyAsmb.dll' -- 
'Key file 'MyPublicKey.snk' is missing the private key needed for signing' DelaySignedAsmb

因為在為組件加入簽章時,還必須要有私密金鑰才行。此時就必須用到延遲簽章,做法是在專案屬性的「Signing」頁籤 中勾選「Delay sign only」選項。這樣就可以順利在開發時期建立延遲簽章的組件了。利用此方法所建立的組件,編譯器會在組件中加入公開金鑰,並且保留一些空間給私密金鑰, 以便未來正式簽署組件時寫入這個保留的空間。

但是這樣還是有問題,當其他開發人員要使用這個組件時,可以順利加入組件參考和編譯專案,可是在執行程式時卻會出現 FileLoadException:

Could not load file or assembly 'MyAsmb, Version=1.0.0.0, Culture=neutral, 
PublicKeyToken=f0bed8b38a4d3590' or one of its dependencies. 
Strong name validation failed. (Exception from HRESULT: 0x8013141A)

這是因為 CLR 在載入組件時,會先驗證該組件的簽章是否正確。由於組件使用了延遲簽章,組件裡面只有公開金鑰而沒有私密金鑰,因此 CLR  在驗證組件簽章時就會失敗,因而無法載入組件。因此,開發人員還必須做一件事情,就是告訴 CLR 在載入這個組件時,不要驗證它的簽章是否有效。做法是使用 sn.exe 加上 -Vr 參數,例如:

sn.exe -Vr MyAsmb.dll

這個命令並不會改變 MyAsmb.dll 的檔案內容,只是在系統中加入一項註冊資訊,告訴 CLR 在載入 MyAsmb.dll 時,不要驗證它的簽章。

 

註:如果要恢復驗證簽章,可以用 -Vu 參數,例如:
sn.exe -Vu MyAsmb.dll

這樣一來,所有的開發人員就可以在自己的機器上順利進行開發的工作了。等到所有的組件都開發測試完畢,要發行產品時,只要由持有完整金鑰的主管對組件重新簽署就行了,做法是使用 sn.exe 加上 -R 參數,例如;

sn.exe -R MyAsmb.dll MyKey.snk

這樣會將 MyAsmb.dll 以 MyKey.snk 重新簽署,組件的專案完全不用修改或重新編譯,只要將此重新簽署過的組件部署到用戶端就行了。