在.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格式的檔案,其中<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版本就可以了。
總結:publickeytoken或者publickey不僅僅是用于安全方面,也是用于差別同一個dll的不同版本方面。其實在一般的開發中我們用到的不多,最起碼從我自己淺薄的經驗來講,接觸的不多,基本上是透明的。