標籤:style blog class code tar color
在.NET的GAC出現之前,曾經有DLL Hell的問題。這是因為當時對於共用的DLL的處理方式,是通過採用註冊表的方式實現的。當我們安裝一個程式A的時候,這個程式包含一個共用的DLL,那麼這個DLL就會就會寫入到註冊表中,但是注意這裡並沒有寫入版本資訊,只是告訴你在哪個地方有一個叫做XX的DLL可以使用。當安裝另外的一個程式B的時候,也包含這個共用的DLL,但是是一個更加新一些的版本,系統會發現這個DLL已經註冊存在了,就會用這個DLL去覆蓋原來的DLL,但是因為註冊表中前後沒有任何版本的標示,所以系統還是認為這就是一個DLL. 但是現在已經更新了DLL,A程式可能會出現使用這個DLL的時候不相容的現象,但是此時如果你重裝A程式,再把共用DLL換回去,那麼B程式又可能出現不相容的現象。這就是DLL HELL問題。引發這個問題的原因,還是同一個DLL不能多個版本同時存在的問題。
DLL Hell 是指當多個應用程式試圖共用一個公用群組件(如某個動態串連庫(DLL)或某個元件物件模型(COM)類)時所引發的一系列問題。最典型的情況是,某個應用程式將要安裝一個新版本的共用組件,而該組件與機器上的現有版本不向後相容。雖然剛安裝的應用程式運行正常,但原來依賴前一版本共用組件的應用程式也許已無法再工作。
然後GAC的出現,解決了這個問題。GAC中對於可以同時存在同一個DLL的多個不同的版本。如果多個程式都用到了這個DLL,那麼這個程式就可以到GAC中去找相應版本的DLL.但是在GAC中,比如說同一個Data.dll,即使有多個版本的檔案,但是其檔案名稱都應該全是Data.dll,而且在不同的目錄總,其version資訊不是在檔案名稱中體現出現了,而是在DLL的頭資訊中儲存的。每個程式都會有一個資訊清單,這個清單存在和程式同名的.manifest檔案中,裡面列出其所需要的所有依賴,這兒所列出的依賴可不是簡單地靠檔案明來區分的,而是根據一種叫做“強檔案名稱”的東西區分的。
<?xml version=‘1.0‘ encoding=‘UTF-8‘ standalone=‘yes‘?> <assembly xmlns=‘urn:schemas-microsoft-com:asm.v1‘ manifestVersion=‘1.0‘> <dependency> <dependentAssembly> <assemblyIdentity type=‘win32‘ name=‘Microsoft.VC80.CRT‘ version=‘8.0.50608.0‘ processorArchitecture=‘ x86‘ publicKeyToken=‘1fc8b3b9a1e18e3b‘ /> </dependentAssembly> </dependency> </assembly>
我們發現原來這是一個XML格式的檔案,其中<dependency>這一部分指明了其依賴於一個名字叫做Microsoft.VC80.CRT的庫。但是我們發現,<assemblyIdentity>屬性裡面還有其它的東東,分別是 type系統類別型,version版本號碼,processorArchitecture平台環境,publicKeyToken公匙(一般用來標示一個公司),把他們加在一起便成了“強檔案名稱”了,有了這種“強檔案名稱”,我們就可以根據其區分不同的版本、不同的平台,總之,有了這種強檔案名稱,系統中可以有多個不同版本的相同的庫共存而不會發生衝突。
其實PublicKeyToken就是Public Key的簡單形式,我們就可以把PublicKeyToken當成PublicKey.這裡說明一下PublicKeyToken的存在。PublicKeyToken的作用就是確定要載入的DLL一定要是最初的那個DLL,其實,這一方面也起到了安全方面的防範問題。比如說,有的程式,有人寫了一個同名的DLL覆蓋了你原來的DLL,程式如果不加分辨就使用這個DLL,可能就有安全問題了。那麼PublicKeyToken是這麼樣來確保這個唯一性的呢?
這裡涉及到了密碼編譯演算法。最初DLL的開發人員在開發這個DLL的時候,會加密這個DLL,使用的加密方法就是公開金鑰私密金鑰的方法。公開金鑰與私密金鑰是同時存在並且唯一對應的。對於同一段內容,用私密金鑰加密之後,只有用公開金鑰才能解密。如果用其他的私密金鑰加密的東西,用這個公開金鑰是解不開任何東西的。DLL開發人員有自己的私密金鑰,而這個私密金鑰別人是不知道的,是保密的,在DLL開發的時候用私密金鑰加密,並且把公開金鑰資訊寫入到程式中。當這個程式開發完成後,在一台電腦上啟動並執行時候,系統會從程式的程式清單中去找用到了哪個DLL,並且去查看這個DLL的版本,而且要用PublicKeyToken來確保這個DLL的原始性。那麼是通過一個怎麼樣的過程來確保這個DLL的原始性呢?比如說,有一個人在你的電腦上,在你安裝好這個程式後,到你程式的安裝目錄下,用一個同名的,具有同樣的命名空間和類的DLL替換掉了你原來的DLL,系統是怎麼能夠發現的呢?程式在啟動並執行時候,會從程式的程式清單中取看用到了哪個dll,這個dll的公開金鑰是多少。(我認為這個程式清單是輕易不能被篡改的,如果這個都能篡改了,那麼程式就無安全性可言了。起碼我是這樣認為的。)然後去GAC中或者程式的目錄下尋找這個DLL。在這個DLL的標頭檔中有一些資訊,是DLL的內容和加密後的字串。程式會用公開金鑰去解密這個字串,如果解密出的內容和DLL中記錄的內容一致,那麼就證明這個DLL是原始的,沒有被篡改過的。
下面我們考慮幾種可能被篡改的情況
有人僅僅修改了DLL的內容,並沒有修改加密後的值,此時很容易被程式發現出來,因為解密後的值和DLL內容不一致,程式會提示異常。
有人不僅修改了DLL的內容,還想修改加密後的值,但是要知道,此時的使用者是沒有原來的開發人員的私密金鑰的,如果他隨便用一個私密金鑰加密,那麼在程式解密的是,用那個公開金鑰是解密不出來任何東西的,因為公開金鑰私密金鑰是對應的。此時,程式會提示異常。這篇文章比較細緻的解釋了公開金鑰私密金鑰對於DLL加密的過程。
有時候會在Web.config檔案中Runtime標籤下看到一些<runtime>bindingRedirect的內容,這裡涉及到了PublicKey。這裡的作用是把不同版本的檔案對應到某一個特定的版本,即程式清單中記載的某個dll是2.0的版本,可是程式在啟動並執行時候在GAC中只找到1.0的版本,那麼告訴程式此時不提示異常,只要把這個1.0版本當成2.0版本就可以了。
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"> <dependentAssembly> <assemblyIdentity name="System.Web.Mvc" publicKeyToken="31bf3856ad364e35"/> <bindingRedirect oldVersion="1.0.0.0" newVersion="2.0.0.0"/> </dependentAssembly> </assemblyBinding> </runtime>
總結:PublicKeyToken或者PublicKey不僅僅是用於安全方面,也是用於區別同一個DLL的不同版本方面。其實在一般的開發中我們用到的不多,最起碼從我自己淺薄的經驗來講,接觸的不多,基本上是透明的。