zoukankan      html  css  js  c++  java
  • 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 安全揭秘》


    作者:玄魂
    出处:http://www.cnblogs.com/xuanhun/
    原文链接:http://www.cnblogs.com/xuanhun/ 更多内容,请访问我的个人站点 对编程,安全感兴趣的,加qq群:hacking-1群:303242737,hacking-2群:147098303,nw.js,electron交流群 313717550。
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
    关注我:关注玄魂的微信公众号

  • 相关阅读:
    面向对象的链式调用
    浅显易懂的理解JavaScript中的this关键字
    js 页面刷新location.reload和location.replace的区别小结
    JavaScript中两种类型的全局对象/函数
    event.srcElement ,event.fromElement,event.toElement
    json jsonp的区别
    createElement与createDocumentFragment的点点区别
    解决Ajax不能跨域的方法
    mysqli_fetch_assoc php的新的库函数
    500 501 502 503 504 505 服务器错误
  • 原文地址:https://www.cnblogs.com/xuanhun/p/2560146.html
Copyright © 2011-2022 走看看