代码改变世界

Modern C# 系列课程笔记 第9节 基于组件的程序设计

2011-06-11 16:10  lujiao_cs  阅读(325)  评论(0编辑  收藏  举报

 

强命名(Strong Name)

程序集的四个标识:

1)friendly, human readable name

2)culture 

3)version #

4)PublicToken

 

强命名的好处:

1)安全性,组件无法篡改! 

2)组件的多个版本可以共存,防止dll冲突。

3)应用程序可以使用它建立时的组件版本,而不是第一个找到的DLL。

 

创建强命名组件的步骤: 

1.  生成一组公私密钥对

使用SN工具来生成密钥对。该工具位于安装.NET Framework SDKBin目录中,在命令行中使用“ SN -k [驱动器号]:[放置密钥的目录][密钥名称].snk 这样的语句可以生成密钥对。例如:在VS工具的命令行下输入: SN.exe  -k  MySloutionKey.snk。MySloutionKey.snk文件将包含以对以二进制格式存储的公有密钥和私有密钥。注意保护Key文件的安全,因为可以通过Key 文件可以获取私钥,它就相当于密码文件。
2. 查看公有密钥:
首先生成一个只包含公有密钥的文件: 

D:\>SN -p   MySloutionKey.snk MySloutionKey.PublicKey

然后用-tp参数查看:

SN tp MySloutionKey.PublicKey

公钥为:

0024000004800000940000000602000000240000525341310004000001000100f9c1966ca6f03d152c6a4c7402536409164d442fa344c24680c4207dbce9fb3b29eb3bcf856e0c57512f16e90e3507b561e22ac0a513794a7053502f85ee0fbcaed726640d0dc4fd314fbb031a51618a08b91957083dd372938773cf49252c02ef4f0019a8282de208d56b106eec8368a2575ae96f4d8d7fb151362177b7fdea

公钥标记为: 

ae07abb9cc837b53


   创建好了公钥/私钥对,创建强命名程序集就很容易了。只需要把System.Reflection.AssemblyKeyFileAttribute特性加入到源代码中就可以了:[assembly:AssemblyKeyFile("MySloutionKey.snk")]
  
   说明:公钥/私钥对文件的扩展名可以是任意的(也可以没有),因为编译的时候都是以元数据的格式读取的。 

3. 将该密钥对与相应项目的程序集建立关联 

打开项目(Project)的AssemblyInfo.cs 文件。此文件具有一个程序集属性列表,默认情况下,在Visual Studio .NET中创建项目时将包括这些属性。在代码中修改名为AssemblyKeyFile的属性,如下所示: 

[assembly:AssemblyKeyFile("C:\MySloutionKey.snk")] 

4.对模块签名

(1)从.cs文件生成模块文件

csc /out:myModule /target:module form1.cs

(2)从模块文件生成强名程序集

al /out:myDll.dll myModule /keyfile:mykeyfile.snk

然后可以在其它强名程序集中或网站中,引用myDll.dll。

 

签名过程:


  为了给一个程序集赋予强名称,首先需要使用.NET Framework 2.0 SDK 的sn.exe工具生成公钥私钥对。sn.exe工具生成一个文件(通常以*.snk(Strong Name Key)作为文件扩展名),该文件包含两个不同的但算术上相关的钥:“公”钥和“私”钥。一旦C#编译器确定*.snk文件的位置,它就会在编译过程中使用.publickey标记把公钥值记录到程序集清单里。

  C#编译器还会产生一个基于整个程序集内容(CIL代码、元数据等)的散列值。散列值是对于某一固定输入的独一无二的数值输出。因此,如果更改了.NET程序集的内容(就算只是改变一个字符),编译器将产生完全不同的散列值。散列值结合*.snk文件中的私钥组成数字签名。数字签名将被嵌入到程序集的CLR首部数据中。产生程序集的强名称的整个过程如图所示。

  实际的私钥并不在程序集清单中列出,它只用做对程序集内容进行数字签名(结合生成的散列值)。重申一点,使用公钥私钥这个密码学概念,完全是为了确保在.NET领域中,没有任意两个公司、部门或个人拥有相同的标识。程序集被赋予强名称后,就可以安装到GAC中。

 

程序集的部署方式
   a)私有方式
   和应用程序部署在同一目录下的程序集称作私有部署程序集。弱命名程序集只能进行私有部署。
   b)全局方式
   全局部署方式将程序集部署在一些CLR已确知的地方,当CLR搜索程序集时,它会知道到这些地方去找。强命名程序集既可以进行私有部署,也可以进行全局部署。

GAC(Global Assembly Cache的概念:

  安装有公共语言运行库的每台计算机都具有称为全局程序集缓存的计算机范围内的代码缓存。全局程序集缓存中存储了专门指定给由计算机中若干应用程序共享的程序集。

GAC的作用:

  在开发一般的、非共享的程序时,我们不需要使用强命名的程序集,仅将项目(Project)编辑成.DLL或者.EXE即可。但是,如果我们开发的是组件库、框架时,通过对程序集进行强命名,并使用将其部署到GAC中,可以保证我们的程序集不会出现版本问题。同时如果一个Assembly要被多个应用程序访问,那么它就必须放在一个CLR已确知的目录下,并且CLR在探测到有对该Assembly的引用时,它必须能自动到该目录下寻找这个程序集。这个已确知的目录就是GAC。它一般位于下面的目录下:<System Drive>:\Windows\Assembly\GACGAC的作用就是提供给CLR一个已知的确定的目录去寻找引用的程序集。

  把程序集安装到GAC有几个好处。首先,GAC使得很多程序可以共享程序集,这从整体上减少了使用的物理内存;其次,我们很容易将一个新版的程序集部署到 GAC中,并通过一种发布者策略(差不多就是一种重定向方法,比如将原来引用版本为1.0.0.0程序集的程序,通过更改它的配置文件,转而让程序去引用版本为2.0.0.0的程序集)来使用新版本;最后,GAC还提供了对不同版本程序集的并存(side-by-side)管理方式。但是,GAC的安全策略通常只允许管理员更改,同时,向GAC中安装程序集也破坏了.NET框架的简单拷贝部署的许诺。 

GAC的内部结构

  GAC是一个特殊的结构化的目录,用Windows Explorer浏览你会以为它只是一个包含很多程序集的普通目录。其实不是这样的,在命令行下查看,你会发现它实际上包含很多子目录,子目录的名字和程序集的名称是相同的,但它们都不是实际的程序集,实际的程序集位于程序集名对应的目录下。比如进入GCFWK子目录,我们会发现其中又有很多的子目录。机器内每一个安装到GACGCFWK.dllGCFWK中都会有一个子目录。
这里只 有一个目录表明只有一个版本的GCFWK程序集被安装。实际的程序集保存在每一个对应的版本目录下。目录的名称以下划线的形式分割为“(Version)_(Culture)_(PublicKeyToken)”
GCFWK的语言文化信息为netture,就表示为0.0.0__bf5779af662fc055”。 表示得意义是: “GCFWK, Version=1.0.0.0, Culture=neutral,PublicKeyToken=bf5779af662fc055” 如果语言文化信息为”ja”,就表示”1.0.0.0_ja_bf5779af662fc055”。表示得意义是:

“GCFWK, Version=1.0.0.0, Culture=ja, PublicKeyToken=bf5779af662fc055”

 

如何把程序集(必须是强命名的)部署到GAC中? 

1)使用 Windows 资源管理器将程序集拖到缓存中。 
2使用 .NET Framework SDK 所提供的名为全局程序集缓存工具 (Gacutil.exe) 的开发人员工具。其命令行语法为
Gacutil [options] [assemblyName | assemblyPath | assemblyListFile]

例如:
   1.把程序集添加到GAC中: GACUtil /i sample.dll (参数/i是安装的意思)
   2.把程序集移出GAC GACUtil /u sample.dll (参数/u就移除的意思)
   注意:不能将一个弱命名程序集安装到GAC中。
   如果你试图把弱命名程序集加入到GAC中,会收到错误信息:
   Failure adding assembly to the cache: Attempt to install an assembly without a strong name

优势有:

             共享DLLs 

             并行安装不同版本 

             更快的下载时间 (在安装时刻,组件完整的检查) 

 

不使用强命名的定位Assembly的算法:

1)同EXE相同的目录查找,没找到则转2)步。

2)子目录查找,每找到则转3)步。

3)写一个配置文件,可以指定privatePath,然后在配置文件查找:

<configuration> 

<runtime> 

      <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"> 

             <probing privatePath="MyAssemblies"/> 

      </assemblyBinding> 

</runtime> 

</configuration>

 

使用强命名的定位Assembly的算法:

1)查找GAC,没找到,则转2)

2)写一个配置文件,查找CodeBase

 

版本转向:

    <configuration> 

     <runtime> 

      <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"> 

       <dependentAssembly> 

           <assemblyIdentity name="BusinessComponent" 

                             publicKeyToken="1234567890abcdef" /> 

           <bindingRedirect oldVersion="1.0.0.0“ newVersion="2.0.0.0" /> 

       </dependentAssembly> 

      </assemblyBinding> 

     </runtime> 

</configuration>

 

CodeBase:

可以转向到不同的目录和机器 

      DLLs 保存用户的internet 下载缓存 

      对于每个版本只有一个codebase 

 

   <configuration> 

    <runtime> 

     <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"> 

      <dependentAssembly> 

         <assemblyIdentity 

           name="BusinessComponent" 

           publicKeyToken="1234567890abcdef" /> 

         <codeBase 

           version="2.0.0.0" 

           href="http://www.company.com/downloads/BusinessComponent.dll" /> 

      </dependentAssembly> 

     </assemblyBinding> 

    </runtime> 

   </configuration>  GUI.exe.config