Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1004437
  • 博文数量: 78
  • 博客积分: 1473
  • 博客等级: 上尉
  • 技术积分: 2124
  • 用 户 组: 普通用户
  • 注册时间: 2012-05-23 12:50
文章分类

全部博文(78)

文章存档

2016年(6)

2015年(2)

2014年(13)

2013年(4)

2012年(53)

分类: 嵌入式

2012-06-24 16:38:02

 

为了保证文档的完整性,防止文档被篡改,同时确保文档的来源,Open XML提供对文档进行数字签名的支持。通过使用Office文档中的签名行捕获数字签名的能力,使组织能够对合同或其他协议等文档使用无纸化签署过程。与纸质签名不同,数字签名能提供精确的签署记录,并允许在以后对签名进行验证。

创建签名

在Office中,可以通过插入签名行来来对文档进行数字签名。在“插入”选项卡中单击“签名行”,可以看到它的两个选项,如图14-28所示。

图14-28 签名行

在图14-28中,可看到三个选项,前两项是添加签名的选项,区别不大,最后一项是链接到微软的帮助页。单击Microsoft签名行,会看到如图14-29所示的“签名设置”界面。

图14-29  签名设置

设置完基本信息,单击“确定”,会看到在文档右侧关于签名的提示信息,如图14-30所示。

图14-30  尚未签署文档的提示

可以右键单击签名,单击签署,会看到如图14-31所示的签署界面。

图14-31  签署文档

在签署界面,可以选择作为签名的图像,然后单击“签名”,正式对文档进行签名。在图14-31中的颁发者中可以看到默认选择的证书,单击“更改”按钮更改用于签名的证书,如图14-32所示。

图14-32  选择证书

正式签名之后,会得到如图14-33的提示。

图14-33  签名成功提示

此时,如果尝试修改文档,会得到如图14-34的提示信息。

图14-34  已签名的文档不允许修改

那么Open XML文档是如何存储数字签名的信息呢?解压已经被签名的Word文档,一探究竟。如图14-35所示。

图14-35 查看签名后的.docx文档结构

如果细心的话,可以看到在图14-35中有一个名为_xmlsignatures的文件夹,该文件夹下的内容如图14-36所示。

图14-36  _xmlsignatures文件夹内容

图14-36中,origin.sigs文件是签名数据,用来对比文档是否被篡改;sig1.xml文件内是签名的相关信息,包括签名算法,数字证书和签名数据。文件内容较多,这里就不展示了,读者可自行查看。现在可以修改主文档中的某个文字,然后重新压缩,看看会是什么结果?如图14-37所示。

图14-37  无效签名

修改文档中的信息之后,查看签名信息,会提示无效签名。

用Open XML SDK处理签名

下面介绍如何在程序中使用Open XML SDK来处理Open XML文档的数字签名。

为了更方便地处理Open XML文档的数字签名,.NET的System.IO.Packaging名称空间提供了PackageDigitalSignatureManager类来对文档签名,获取签名信息,移除签名,验证签名。

该类包含如下10个属性:

q  CertificateOption。获取或设置由Sign方法使用的X.509证书嵌入选项以对包部件进行数字签名。

q  DefaultHashAlgorithm。获取URI字符串,该字符串可标识用于创建和验证签名的默认哈希算法。

q  HashAlgorithm。获取或设置用于创建和验证签名的 HashAlgorithm 实例的URI标识符。

q  IsSigned。获取一个值,该值指示包是否包含任何签名。

q  ParentWindow。获取或设置父窗口的句柄,以显示证书选择对话框。

q  SignatureOrigin。获取签名源部件的URI。

q  SignatureOriginRelationshipType。获取默认签名源关系的类型。

q  Signatures。获取包中包含的所有签名的集合。

q  TimeFormat。获取或设置用于创建签名 SigningTime 的日期/时间格式。

q  TransformMapping。获取一个字典,其中包含每个定义的 ContentType 及其关联 XML Transform.Algorithm 标识符。

PackageDigitalSignatureManager类的每个方法就不一一解释了,下面来看一个实际的对Open XML包进行签名的示例。

第一个函数名为CreatePackage,其作用为创建一个Open XML包,如代码清单14-25所示。

代码清单14-25  CreatePackage函数

     private static void CreatePackage(String packageFilename)

        {

            Uri uriDocument = new Uri(@"Content\Document.xml", UriKind.Relative);

            Uri uriResource = new Uri(@"Resources\Image1.jpg", UriKind.Relative);

 

 

            Uri partUriDocument = PackUriHelper.CreatePartUri(uriDocument);

            Uri partUriResource = PackUriHelper.CreatePartUri(uriResource);

 

            using (Package package =

                           Package.Open(packageFilename, FileMode.Create))

            {

               

                PackagePart packagePartDocument =

                    package.CreatePart(partUriDocument,

                                    System.Net.Mime.MediaTypeNames.Text.Xml);

 

             

                using (FileStream fileStream =

                    new FileStream(uriDocument.ToString(), FileMode.Open, FileAccess.Read))

                {

                    CopyStream(fileStream, packagePartDocument.GetStream());

                }

                package.CreateRelationship(packagePartDocument.Uri,

                    TargetMode.Internal, _samplePackageRelationshipType);

 

              

                PackagePart packagePartResource =

                    package.CreatePart(partUriResource,

                                    System.Net.Mime.MediaTypeNames.Image.Jpeg);

 

             

                using (FileStream fileStream =

                    new FileStream(uriResource.ToString(), FileMode.Open, FileAccess.Read))

                {

                    CopyStream(fileStream, packagePartResource.GetStream());

                }

                packagePartDocument.CreateRelationship(

                                        new Uri(@"../Resources/Image1.jpg",

                                        UriKind.Relative),

                                        TargetMode.Internal,

                                        _sampleResourceRelationshipType);

 

                package.Flush();

                SignAllParts(package);

 

            }

        }

在CreatePackage函数中,首先创建URI,这些URI将用于那些将要被添加到包中的部件,以及部件中的内容。然后,创建Open XML包,然后添加部件和关系,并添加了一个图片,将被用于签名。在方法的末尾调用SignAllParts方法,该方法用于执行真正的签名操作,代码如清单14-26所示。

代码清单14-26  SignAllParts函数

    private static void SignAllParts(Package package)

        {

            if (package == null)

                throw new ArgumentNullException("SignAllParts(package)");

 

          

            PackageDigitalSignatureManager dsm =

                new PackageDigitalSignatureManager(package);

            dsm.CertificateOption =

                CertificateEmbeddingOption.InSignaturePart;

            System.Collections.Generic.List<Uri> toSign =

                new System.Collections.Generic.List<Uri>();

            foreach (PackagePart packagePart in package.GetParts())

            {

                toSign.Add(packagePart.Uri);

            }

            toSign.Add(PackUriHelper.GetRelationshipPartUri(dsm.SignatureOrigin));

            toSign.Add(dsm.SignatureOrigin);

            toSign.Add(PackUriHelper.GetRelationshipPartUri(new Uri("/", UriKind.RelativeOrAbsolute)));

            try

            {

                dsm.Sign(toSign);

            }

 

            catch (CryptographicException ex)

            {

                MessageBox.Show(

                    "Cannot Sign\n" + ex.Message,

                    "No Digital Certificates Available",

                    MessageBoxButton.OK,

                    MessageBoxImage.Exclamation);

            }

 

        }

在SignAllParts函数中,利用传进的Package实例创建了PackageDigitalSignatureManager对象的示例dsm。设置dsm的CertificateOption属性为CertificateEmbeddingOption.InSignaturePart。随后,遍历每一个Part,取出URI,放到集合toSign中。准备工作做好以后,调用dsm.Sign(toSign)方法对每一个部件进行签名。

验证签名的有效性

下面再来看如何验证签名的有效性,先看代码清单11-27。

代码清单11-27  验证签名的有效性

      private static bool ValidateSignatures(Package package)

        {

            if (package == null)

                throw new ArgumentNullException("ValidateSignatures(package)");

            PackageDigitalSignatureManager dsm =

                new PackageDigitalSignatureManager(package);

            if (!dsm.IsSigned)

                return false;   

        VerifyResult result = dsm.VerifySignatures(false);

            if (result != VerifyResult.Success)

                return false;   

                return true;     

        }

在代码清单11-27中,仍然使用PackageDigitalSignatureManager类验证签名的有效性,首先通过IsSigned属性来判断Open XML包是否经过签名,如果经过签名则调用VerifySignatures方法来验证签名是否有效。该方法的参数是一个bool类型,表示如果首次失败时退出,为true;如果要继续检查所有签名,则为 false。

此外也可以调用VerifyCertificate方法来验证证书是否有效。

 ----------------------------注:本文部分内容改编自《.NET 安全揭秘》

阅读(1914) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~