Open XML应用安全(5)数字签名

Open XML应用安全(5)数字签名

保证文档完整性,防止文档被篡改,同时确保文档来源,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文档数字签名,.NETSystem.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。设置dsmCertificateOption属性为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 安全揭秘》

posted @ 2012-06-24 16:36  玄魂  阅读(1964)  评论(0编辑  收藏  举报