DotNet加密方式解析--数字签名

    马上就要过年回村里了,村里没有wifi,没有4G,没有流量,更加重要的是过几天电脑就得卖掉换车票了,得赶紧写几篇文章。

    数据安全的相关技术在现在愈来愈变得重要,因为人们对于自身的信息都有一种保护的欲望,不想被人获取到自己的私密信息,加密几乎已经是这个时代的关键词了。在这个HTTPS盛行的时代,作为一个开发人员怎么可能不去了解和学习呢。这篇博文就来给大家简单介绍一个HTTPS在.NET种的应用和实现方法。

    数字证书和数字签名的实现主要是基于非对称加密和数字摘要,数字签名是数字证书不可或缺的一部分。这篇博客主要讲解数字签名、数字证书,以及数字签名在.NET种的实现方法。

一.数字签名概述:

   1.数字签名的基本原理:

      这里首先来了解一些什么叫做数字签名,数字签名是附加在数据单元上的一些数据,或是对数据单元所做的密码变换。数字签名是对非对称加密和消息摘要的应用。数签名的原理:使用非对称密钥将签名函数添加到非对称算法,创建一个“签名”,另一方接收加密的信息,使用确认函数来验证签名。有如下图:

     说明:用户A选择一个非对称签名算法创建一对新密钥,自己保留私钥,公钥发给B。用户B使用用户A的公钥来验证签名。

     将散列码做为创建数字签名,有如下图:

    将散列码作为确认一个数字签名,有如下图:

    2.数字签名的特点:

      第三方不能伪造用户A的数字签名;第三方不能重新使用用户A的数字签名;第三方不能改变签名后的文件;用户A无法否认自己的签名文件。数字签名能够提供一种和物理签名类似的合理机制。数字签名的安全性和加密的其他方面是一样的,他们都是基于可能的有效密钥管理的。数字签名只采用了非对称密钥加密算法,能保证发送信息的完整性、身份认证和不可以否认行,数字加密采用了对称密钥加密算法和非对称密钥加密算法相结合的方法,能够保证发送信息的保密性。

二.数字证书概述:

   对于HTTPS(Hyper Text Transfer Protocol over Secure Socket Layer)很多开发人员都不会陌生,即使是普通用户也是比较的熟悉。数字证书(公钥证书):用于电子信息活动中电子文件行为主体的验证和证明,并可实现电子文件保密性和完整性的电子数据。数字证书是一个经证书认证中心发行的证书。

   数字证书:个人数字证书,单位数字证书、单位员工数字证书、服务器证书、VPN证书、WAP证书、代码签名证书和表单签名证书等。

   数字证书是一个经证书授权重心数字签名的包含公开密钥拥有者信息以及公开密钥的文件,最简单的证书包含一个公开密钥、名称一剂证书授权中心的数字签名。

   数字证书的特点:信息的保密性;交易者身份的确定性;不可否认性、不可修改性。

   数字证书的三种保存形式:带有私钥的证书;二进制编码的证书;Base64编码证书。

三.DotNet数字签名核心对象解析:

     在.NET中包含两种支持数字签名的非对称算法:RSA算法(为两种数据加密和数字签名定义了函数);DSA算法(支持数字签名,不支持数据加密)。在.NET中使用RSA算法进行数字签名使用RSACryptoServiceProvider类,使用DSA进行数字签名的四个核心类如下图:

   DSA类:数字签名算法DSA的基类;DSACryptoServiceProvider类:定义访问DSA算法的加密服务提供程序实现的包装对象;DSASignatureDeformatter类:验证DSA签名;DSASignatureFormatter类:创建DSA签名;

   接下来我们具体了解一下这些类:

     1.RSACryptoServiceProvider类:

        (1).SignData()方法:使用指定的哈希算法计算指定输入流的哈希值,并对计算所得的哈希值签名。

public byte[] SignData(Stream inputStream, object halg)
    {
      int calgHash = Utils.ObjToAlgId(halg, OidGroup.HashAlgorithm);
      return this.SignHash(Utils.ObjToHashAlgorithm(halg).ComputeHash(inputStream), calgHash);
    }

     该方法存在三个重载方法,三个重载方法的第一个参数不同,分别是Stream、byte[]两个类型。由代码可以看出,该方法接受两个参数,inputStream是要计算其哈希值的输入数据,halg用于创建哈希值的哈希算法。SignHash()通过用私钥对其进行加密来计算指定哈希值的签名。

        (2).VerifyData():通过使用提供的公钥确定签名中的哈希值并将其与所提供数据的哈希值进行比较验证数字签名是否有效。

 public bool VerifyData(byte[] buffer, object halg, byte[] signature)
    {
      int calgHash = Utils.ObjToAlgId(halg, OidGroup.HashAlgorithm);
      return this.VerifyHash(Utils.ObjToHashAlgorithm(halg).ComputeHash(buffer), calgHash, signature);
    }

    该方法没有重载版本,有源码可以看出该方法接收三个参数,分别是:buffer已签名的数据,halg用于创建数据的哈希值的哈希算法名称,signature要验证的签名数据。该方法返回一个布尔类型,如果签名有效,则为 true;否则为 false。VerifyHash()通过使用提供的公钥确定签名中的哈希值并将其与提供的哈希值进行比较来验证数字签名是否有效。

   2.DSA类解析:

     (1).CreateSignature():创建指定数据的 Cryptography.DSA 签名。

 public abstract byte[] CreateSignature(byte[] rgbHash);

     该方法为一个抽象方法,在派生类中重写,接受一个字节数组表示要签名的数据,返回指定数据的数字签名。在使用CreateSignature方法时,必须自己创建SHA-1散列码,返回一个用字节数组表示的DSA签名。

     (2).VerifySignature():验证指定数据的 Cryptography.DSA 签名。

public abstract bool VerifySignature(byte[] rgbHash, byte[] rgbSignature);

     该方法接受字符数组表示的SHA-1散列码和签名来验证。

    3.DSACryptoServiceProvider类解析:

     (1).ImportParameters():导入指定的 DSAParameters。该方法接受一个参数,Cryptography.DSA的参数。

     (2).VerifyData():通过将指定的签名数据与为指定数据计算的签名进行比较来验证指定的签名数据。

 public bool VerifyData(byte[] rgbData, byte[] rgbSignature)
    {
      return this.VerifyHash(this._sha1.ComputeHash(rgbData), (string) null, rgbSignature);
    }

      该方法接受两个参数,rgbData已签名的数据;rgbSignature要验证的签名数据,如果签名验证为有效,则为 true;否则,为 false。VerifyHash()通过将指定的签名数据与为指定哈希值计算的签名进行比较来验证指定的签名数据,我们看一下VerifyHash()的实现代码:

 public bool VerifyHash(byte[] rgbHash, string str, byte[] rgbSignature)
    {
      if (rgbHash == null)
        throw new ArgumentNullException("rgbHash");
      if (rgbSignature == null)
        throw new ArgumentNullException("rgbSignature");
      int calgHash = X509Utils.NameOrOidToAlgId(str, OidGroup.HashAlgorithm);
      if (rgbHash.Length != this._sha1.HashSize / 8)
      {
        string key = "Cryptography_InvalidHashSize";
        object[] objArray = new object[2];
        int index1 = 0;
        string str1 = "SHA1";
        objArray[index1] = (object) str1;
        int index2 = 1;
        // ISSUE: variable of a boxed type
        __Boxed<int> local = (ValueType) (this._sha1.HashSize / 8);
        objArray[index2] = (object) local;
        throw new CryptographicException(Environment.GetResourceString(key, objArray));
      }
      this.GetKeyPair();
      return Utils.VerifySign(this._safeKeyHandle, 8704, calgHash, rgbHash, rgbSignature);
    }

     该方法接收三个参数,rgbHash要签名的数据的哈希值,str用于创建数据的哈希值的哈希算法名称,rgbSignature要验证的签名数据。

    4.X509Certificate类解析:

        该类在System.Security.Cryptography.X509Certificates空间下,提供帮助你使用 X.509 v.3 证书的方法。

      (1).LoadCertificateFromBlob():加载证书:

private void LoadCertificateFromBlob(byte[] rawData, object password, X509KeyStorageFlags keyStorageFlags)
    {
      if (rawData == null || rawData.Length == 0)
        throw new ArgumentException(Environment.GetResourceString("Arg_EmptyOrNullArray"), "rawData");
      if (X509Utils.MapContentType(X509Utils._QueryCertBlobType(rawData)) == X509ContentType.Pfx && (keyStorageFlags & X509KeyStorageFlags.PersistKeySet) == X509KeyStorageFlags.PersistKeySet)
        new KeyContainerPermission(KeyContainerPermissionFlags.Create).Demand();
      uint dwFlags = X509Utils.MapKeyStorageFlags(keyStorageFlags);
      IntPtr num = IntPtr.Zero;
      RuntimeHelpers.PrepareConstrainedRegions();
      try
      {
        num = X509Utils.PasswordToHGlobalUni(password);
        X509Utils._LoadCertFromBlob(rawData, num, dwFlags, (keyStorageFlags & X509KeyStorageFlags.PersistKeySet) != X509KeyStorageFlags.DefaultKeySet, ref this.m_safeCertContext);
      }
      finally
      {
        if (num != IntPtr.Zero)
          Marshal.ZeroFreeGlobalAllocUnicode(num);
      }
    }

   该方法是X509Certificate类构造函数等几个方法加载证书的具体实现方法。

      (2).Export():使用指定的格式和密码将当前 X509Certificate对象导出到字节数组。

 public virtual byte[] Export(X509ContentType contentType, SecureString password)
    {
      return this.ExportHelper(contentType, (object) password);
    }

        该方法接受两个参数,contentType描述如何设置输出数据格式的 X509ContentType 值之一。password访问 X.509 证书数据所需的密码。返回表示当前 X509Certificate 对象的字节数组。

四.DotNet数字签名实例:

    下面提供一个X509Certificate的操作方法实例:

  public void EncryptXmlDocument(string arqXmlAssinar, string tagAssinatura, string tagAtributoId, X509Certificate2 x509Cert)
        {
            StreamReader sr = null;
            try
            {
                sr = System.IO.File.OpenText(arqXmlAssinar);
                var xmlString = sr.ReadToEnd();
                sr.Close();
                sr = null;
                XmlDocument doc = new XmlDocument { PreserveWhitespace = false };
                doc.LoadXml(xmlString);
                if (doc.GetElementsByTagName(tagAssinatura).Count == 0)
                {
                    throw new Exception(tagAssinatura.Trim());
                }
                if (doc.GetElementsByTagName(tagAtributoId).Count == 0)
                {
                    throw new Exception(tagAtributoId.Trim());
                }
                XmlNodeList lists = doc.GetElementsByTagName(tagAssinatura);
                foreach (XmlNode nodes in lists)
                {
                    foreach (XmlNode childNodes in nodes.ChildNodes)
                    {
                        if (!childNodes.Name.Equals(tagAtributoId))
                            continue;
                        if (childNodes.NextSibling != null && childNodes.NextSibling.Name.Equals("Signature"))
                            continue;
                        Reference reference = new Reference { Uri = "" };                                 
                        XmlElement childElemen = (XmlElement)childNodes;
                        if (childElemen.GetAttributeNode("Id") != null)
                        {
                            var attributeNode = childElemen.GetAttributeNode("Id");
                            if (attributeNode != null)
                                reference.Uri = "#" + attributeNode.Value;
                        }
                        else if (childElemen.GetAttributeNode("id") != null)
                        {
                            var attributeNode = childElemen.GetAttributeNode("id");
                            if (attributeNode != null)
                                reference.Uri = "#" + attributeNode.Value;
                        }
                        XmlDocument documentoNovo = new XmlDocument();
                        documentoNovo.LoadXml(nodes.OuterXml);
                        SignedXml signedXml = new SignedXml(documentoNovo) { SigningKey = x509Cert.PrivateKey };
                        XmlDsigEnvelopedSignatureTransform env = new XmlDsigEnvelopedSignatureTransform();
                        reference.AddTransform(env);
                        XmlDsigC14NTransform c14 = new XmlDsigC14NTransform();
                        reference.AddTransform(c14);
                        signedXml.AddReference(reference);
                        KeyInfo keyInfo = new KeyInfo();
                        keyInfo.AddClause(new KeyInfoX509Data(x509Cert));
                        signedXml.KeyInfo = keyInfo;
                        signedXml.ComputeSignature();
                        XmlElement xmlDigitalSignature = signedXml.GetXml();
nodes.AppendChild(doc.ImportNode(xmlDigitalSignature, true));
                    }
                }
                var xmlDoc = doc;
                var stringXmlAssinado = xmlDoc.OuterXml;
                StreamWriter sw2 = System.IO.File.CreateText(arqXmlAssinar);
                sw2.Write(stringXmlAssinado);
                sw2.Close();
            }
            catch (CryptographicException ex)
            {
                throw new CryptographicException(ex.Message);
            }
            catch (Exception e)
            {
                throw new Exception(e.Message);
            }
            finally
            {
                if (sr != null) sr.Close();
            }
        }

五.总结:

   上面是有关.NET数字证书的简单介绍,如有写的不对的地方还望多多见谅,在博文中有些类和方法没有较多的列举出来,有兴趣的可以自己去深入的了解。我们学习一个知识时,已经从知识的结构了解开始,这样有利于我们站在全局思考问题。

加密算法系列:

       DotNet加密方式解析--散列加密:https://cloud.tencent.com/developer/article/1013386

       DotNet加密方式解析--数字签名:https://cloud.tencent.com/developer/article/1013440

       DotNet加密方式解析--非对称加密:https://cloud.tencent.com/developer/article/1013444

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏blackpiglet

如何使用 Gin 和 Gorm 搭建一个简单的 API 服务 (一)

  今天工作上的事情比较多,所以就把工作中参考的英文文章搬运过来了,这是我参考文章的链接: Developing a simple CRUD API with...

1005
来自专栏跟着阿笨一起玩NET

使用XML向SQL Server 2005批量写入数据——一次有关XML时间格式的折腾经历

原文:使用XML向SQL Server 2005批量写入数据——一次有关XML时间格式的折腾经历

410
来自专栏java一日一条

彻底理解 Android Binder 通信架构

roid 6.0的源码剖析, 本文深度剖析Binder IPC过程, 这绝对是一篇匠心巨作,从Java framework到Native,再到Linux Ker...

1262
来自专栏程序猿DD

【译】Spring 官方教程:创建批处理服务

原文:Creating a Batch Service 译者:Mr.lzc 校对:lexburner 本指南将引导你完成创建基本的批处理驱动解决方案的过程。 你...

4577
来自专栏微服务生态

玩转Flume之核心架构深入解析

前段时间我们分享过玩转Flume+Kafka原来也就那点事儿和Flume-NG源码分析-整体结构及配置载入分析这二篇文章,主要介绍了flume的简单使用和配置文...

723
来自专栏函数式编程语言及工具

Akka(20): Stream:异步运算,压力缓冲-Async, batching backpressure and buffering

   akka-stream原则上是一种推式(push-model)的数据流。push-model和pull-model的区别在于它们解决问题倾向性:push模...

2307
来自专栏along的开发之旅

OkHttp使用完全教程

上一节我们讲述了Http请求的过程, 这一节我们就讲述下OkHttp是怎么完成Http请求的. 为了更好的理解OKHttp,强烈推荐先看一下http的整个...

1443
来自专栏逸鹏说道

避免在ASP.NET Core中使用服务定位器模式

题记:服务定位器(Service Locator)作为一种反模式,一般情况下应该避免使用,在ASP.NET Core更是需要如此。 Scott Allen在其博...

2778
来自专栏Flutter&Dart

DartVM服务器开发(第十七天)--Jaguar_websocket结合Flutter搭建简单聊天室

我们这里定义了一个ChatMessageData,如果你想需要更多字段,可以再添加

1661
来自专栏软件开发

Spring MVC 学习总结(十一)——IDEA+Maven+多模块实现SSM框架集成

与SSH(Struts/Spring/Hibernate/)一样,Spring+SpringMVC+MyBatis也有一个简称SSM,Spring实现业务对象管...

952

扫码关注云+社区