StrongName概念及实例

StrongName是一种.Net提供的身份识别机制。它基于一些加密算法,例如RSA等,对程序集实施加密或称作签名。从而使得我们可以鉴别某个程序集的身份(该程序集是谁发布的)。其实,从本质上来说,StrongName和证书机制(例如X.509)的身份识别原理是相同的。区别只是,证书机制关心的是客户端能否证明自己具有一定的资格,而StrongName只是表识特定的身份,这个身份无所谓客户端或服务端,仅仅是对某个程序集而言,所以StrongName的用处会更广泛一些。后面的例子会看到这一点。

  • StrongName的原理

先说说StrongName——强名称,这个名字的含义。StrongName是相对于WeakName——弱名称,而言的。MSDN上有一个程序集全名称的概念——FullName。一个FullName是由一个FullName字符串表示的。它的格式如下:"[程序集名称]    Version=[版本号]   Culture=[文化区域]  PublicKeyToken=[公钥标识]"

举个例子:"MyAssemblyName   Version=1.0.0.1  Culture=en-US  PublicKeyToken=61dacc9e8a0b6f5f"

这些有关Name的信息中[程序集名称]、[版本号]和[文化区域]都是说明性的信息,他们与PublicKeyToken(至于PublicKeyToken是什么稍候介绍)最大的区别在于前3个可以任意伪造,但是PublicKeyToken是不可以伪造的,是和特定程序集唯一绑定的。在上面的FullName例子中,如果有人想冒充原程序集的作者来发布一个恶意的程序集,他可以给这个程序集取名MyAssemblyName,用1.0.0.1来生成版本号,将文化区域设置成en-US,但是由于他没有特定的密钥文件来给他的恶意程序集签名,所以他无法冒充原程序集的作者。

好了,我们已经开始接触StrongName的核心了,现在我们解释一下StrongName的原理。之前我已经说过StrongName是基于诸如RSA等加密算法的(事实上.NET的StrongName正是使用RSA加密的)。那么在RSA加密中我们首先需要什么呢——一个公钥/私钥对。这个公钥/私钥对是以特定的规则生成的,有兴趣的话你可以参考密码学方面的书籍来弄清楚这个规则,在此我就不作介绍了。好在.NET提供了一个工具来为我们生成公钥/私钥对,这样程序员就不需要在这个环节上花功夫了。这个工具就是:SN.EXE。

我们使用下面的命令:SN -k  KeyPair.snk来生成密钥文件。SN会将公钥/私钥对写入KeyPair.snk文件。请务必保证改文件不会落入不可信任的人手中,否则别人就可以以此完全冒充你来发布代码。然后,我们就可以用这个KeyPair.snk来为我们的程序集签名。我们使用CSC编译器的/KeyFile参数来在编译时为程序集签名:

CSC /target:Libary MyClassLib.cs /KeyFile:KeyPair.snk

如果你使用VS2005的话,在项目属性的编译属性里面有一个“签名”标签,可以方便地指定签名的密钥文件并签名。经过签名的程序和原来有什么不同呢?实际上,编译器用密钥对文件对.NET程序集文件的Meta区进行了加密。从而使得正常的元数据读取工作必须要经过一定解密动作才能进行,这时读取放必须要具有公钥才可以。

好了,我们已经为自己的程序集签名了。从现在起没人可以冒充了,StrongName的原理也基本介绍了。是不是还是一头雾水呢?下面介绍StrongName的应用。

  • StrongName的应用

前面已经介绍了StrongName的基本原理。实际上,StrongName本身如同它的字面意思一样,不具有什么特别的功能,而只是让你能识别一个特定程序集的归属。但正是这个特性,使得其他的安全性操作或管理操作得以进行。下面介绍一些StrongName的应用,以便深入地理解StrongName的工作原理。

1-控制代码访问

试想这样的情况。你发布了一个类库MyClassLib.dll。其中提供了一些具有破坏性的调用。比如对文件系统的操作,包括删除文件等。同时,你知道用户会信任你并将你的类库安装到本地。这样你的程序就具有了完全的信任特权,可以做很多破坏操作。当然,你不会去做这些,但是由于你的库将被其他程序调用,你不能保证那些程序是否具有恶意的企图。可能某个恶意程序本身不被用户完全信任,从而只具有部分信任的权限,例如它不能对文件系统做删除等操作,但是通过调用你的类库中的函数,它可以获得该权限,从而进行破坏活动。所以,你只希望你所信任的作者发布的程序集来调用你的类库。StrongName的作用在这里体现出来了,通过StrongName的签名验证机制,你可以只允许某个作者的程序调用你的类库。如何来操作呢?

首先,假设你是A开发者,你的类库为MyClassLib.dll;客户程序的开发者为B,B编写了一个EXE程序为ClientB.exe。那么,B用前面介绍的方法生成一个密钥对文件KeyPair.snk,其中包含了公钥/私钥对。然后,B用KeyPair.snk为ClientB.exe签名。同时,A和B之间具有信任关系,所以B将公钥分发给A,A使用公钥来验证调用方是否为B所开发出来的,否则不允许调用方的调用请求。

B如何将公钥分发给A呢?B使用下面的命令从KeyPair.snk文件中提取公钥并将其写入公钥文件PublicKey.snk:

SN -p KeyPair.snk PublicKey.snk

然后用下面的命令将公钥信息提取到PublicKey.txt文件中(当然,B也可以将PublicKey.snk给A,由A来做下面的动作):

SN -tp PublicKey.snk  > PublicKey.txt

PublicKey.txt的内容格式如下:

 

Microsoft (R) .NET Framework 强名称实用工具版本 1.1.4322.573
Copyright (C) Microsoft Corporation 1998-2002. All rights reserved.

公钥为
00240000048000009400000006020000002400005253413100040000010001004b9d6223032dd5
6e3031dbc515ad085f627feb3cc92901e5266b79ef5e11bc87a68d4fe15909da3059d4094185b4
5dc27c4accc6eaaba82f2ffe3ee308810480735a6440ba50cd3ec742be0f2908926a8c21f69d80
31e07091ce8c321cc0ea0a777a89b067d440a7f77b08c3309f852affb4c4e801d78dcecf32a414
bdabbcb4

公钥标记为 61dacc9e8a0b6f5f

还记得我们前面提到的FullName中的PublicKeyToken吗?这里的61dacc9e8a0b6f5f就是PublicKeyToken。PublicKey和PublicKeyToken之间是一种Hash的关系,不过我们并不用在意其具体是如何做Hash映射的。

现在A已经有了公钥,那如何来限制调用者呢?看下面的代码:

using System;
using System.Windows.Forms;
using System.Security.Permissions;

namespace SN_TestDll
{
 /// <summary>
 /// Class1 的摘要说明。
 /// </summary>
 public class Class1
 {
  public Class1()
  {
   //
   // TODO: 在此处添加构造函数逻辑
   //
  }

  [StrongNameIdentityPermissionAttribute(SecurityAction.LinkDemand,PublicKey="00240000048000009400000006020000002400005253413100040000010001004b9d6223032dd5"+
    "6e3031dbc515ad085f627feb3cc92901e5266b79ef5e11bc87a68d4fe15909da3059d4094185b4"+
    "5dc27c4accc6eaaba82f2ffe3ee308810480735a6440ba50cd3ec742be0f2908926a8c21f69d80"+
    "31e07091ce8c321cc0ea0a777a89b067d440a7f77b08c3309f852affb4c4e801d78dcecf32a414"+
    "bdabbcb4")]
  public void ShowMsgBox()
  {
   MessageBox.Show("This MsgBox is called from Dll!");
  }
 }
}

上面的StrongNameIdentityPermissionAttribute属性告诉.Net CLR的CAS(Code Access Security)模块当有程序试图调用本类库中的ShowMsgBox()函数时检查调用者程序是否具有PublicKey="00240000048000009400000006020000002400005253413100040000010001004b9d6223032dd5"+
    "6e3031dbc515ad085f627feb3cc92901e5266b79ef5e11bc87a68d4fe15909da3059d4094185b4"+
    "5dc27c4accc6eaaba82f2ffe3ee308810480735a6440ba50cd3ec742be0f2908926a8c21f69d80"+
    "31e07091ce8c321cc0ea0a777a89b067d440a7f77b08c3309f852affb4c4e801d78dcecf32a414"+
    "bdabbcb4"的签名。通过前面的介绍我们已经知道,除非程序的签名者具有KeyPair.snk密钥对文件,否则即使他有公钥,也无法签出一个具有该公钥的程序集。

现在有C开发者,他写了如下的代码ClientC.exe来调用MyClassLib.dll中的函数:

using System;
using SN_TestDll;

namespace SN_Test
{
 /// <summary>
 /// Class1 的摘要说明。
 /// </summary>
 class Class1
 {
  /// <summary>
  /// 应用程序的主入口点。
  /// </summary>
  [STAThread]
  static void Main(string[] args)
  {
   //
   // TODO: 在此处添加代码以启动应用程序
   //

   SN_TestDll.Class1 obj = new SN_TestDll.Class1();
   obj.ShowMsgBox();
  }
 }
}

但是C没有KeyPair.snk文件,所以他无法冒充B来对其程序签名。所以,当执行ClientC.exe来调用MyClassLib.dll中的ShowMsg()函数时,CAS根据MyClassLib.dll的要求对ClientC.exe进行StrongName的身份验证,发现ClientC.exe并不具有指定的StrongName签名,于是调用请求被拒绝,ClientC.exe得到这样的一个Exception:

未处理的“System.Security.SecurityException”类型的异常出现在 未知模块 中。

其他信息: 请求 System.Security.Permissions.StrongNameIdentityPermission, mscorlib, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 类型的权限已失败。

 

2-StrongName控制代码访问的漏洞

上面的方法真的是不错的!可惜当我看到Keith Brown的《小心完全信任代码》一文以后,却发现StrongName并不是万能的,甚至在控制代码访问方面有巨大的漏洞。

caspol -s off

我们可以通过上面代码来关闭CAS的验证。在.Net Framework1.1中可以完全关闭,在.Net Framework2.0中也可以暂时关闭。上面的方法完全没有效果了!不过这种危险只发生在攻击代码具有了完全信任权限以后。因为恶意代码可以具有了完全信任权限后可以通过编程方式来关闭CAS。这样它就可以调用任何本地的代码了!所以,只要我们不随便赋予代码完全信任权限(最常见的情况就是不要将网络上的代码保存到本地再运行,而是应该直接点击运行,这样网络上的代码就以部分信任运行),我们还是安全的。

你也许要问,如果攻击者能够以Admin登陆到部署有MyClassLib.dll的计算机的话,他也可以直接运行上面的命令来关闭CAS啊?的确!但是那已经不是StrongName可以保护的范围了。就代码保护而言,能作为Admin登陆的攻击者可以得到MyClassLib.dll文件本身,所以他完全可以对MyClassLib.dll中的内容进行逆向从而得到自己的代码。对于系统安全而言,一个能以Admin登陆的攻击者有什么不能做的?!

3-StrongName在版本管理上的应用

这是StrongName的一个不错的应用方向。不过鉴于网上有比较多的相关文章这里就不多介绍了。

 

  • StrongName的延迟签名

上面的介绍中,我们一直默认程序集的编写者就是发布者,所以并不牵涉到延迟签名的问题。但是实际操作中往往不是如此的。在上面控制代码访问的例子中,很可能ClientB.exe是由一些开发人员编写。最后,由一个发布机构发布的。用户真正信任的不是那些幕后的开发人员,而是发布机构,比如某个站点或是Microsoft公司。那么可以这样:开发人员在编写程序的时候通知编译器在程序集文件中为保存签名信息预留一定空间,然后由发布机构产生公钥/私钥对文件,并对该程序集实施签名。这样也避免了开发人员本身的不安全因素。

我们可以在AssemblyInfo.cs中加入以下代码来做到延迟签名:

    [assembly:AssemblyKeyFileAttribute("myKey.snk")]
    [assembly:AssemblyDelaySignAttribute(true)]

当发布机构要发布程序集时,由于程序集已经为签名预留了空间,所以只需要用下面命令对程序集进行签名即可:
SN -R MyClassLib.dll myKey.snk

  • 一个尚未解决的奇怪问题

本文中的控制代码访问的例子只在.Net Framework 1.1中实验成功了。而我在VS2005提供的.Net Framework 2.0版本中发现,DLL中所要求的StrongNameIdentityPermission并没有使得CAS对调用请求作验证,或者说CAS并没有按照DLL的要求作相应的验证。所以在.Net Framework2。0中本文的方法不起作用。至今此问题仍困扰我。如果哪位看到此文并找出了问题所在麻烦回复一下原因!在此先谢过!

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值