服务器之家:专注于VPS、云服务器配置技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|VB|R语言|JavaScript|易语言|vb.net|

服务器之家 - 编程语言 - C# - c# RSA非对称加解密及XML&PEM格式互换方案

c# RSA非对称加解密及XML&PEM格式互换方案

2022-10-26 12:20梦在旅途 C#

这篇文章主要介绍了c# RSA非对称加解密及XML&PEM格式互换方案,帮助大家更好的理解和使用c#,感兴趣的朋友可以了解下

最近因考虑接口安全问题,有实现给WEB API实现统一的参数鉴权功能,以防止请求参数被篡改或重复执行,参数鉴权方法基本与常见的鉴权思路相同,采用(timestamp+sign),而我为了防止timestamp被更改,sign算法(timestamp+相关参数排序、格式化后拼接再MD5)也因为在前端是不安全的,故对timestamp采取使用非对称加解密,以尽可能的保证生成的sign不易被破解或替换;

RSA加解密(即:非对称加解密)

生成公钥、私钥对方法(C#),生成出来后默认都是XML格式:

?
1
2
3
4
5
6
7
8
9
10
public static Tuple<string, string> GeneratePublicAndPrivateKeyPair()
{
  using (RSACryptoServiceProvider rsa = new RSACryptoServiceProvider())
  {
    string publicKey = rsa.ToXmlString(false); // 公钥
    string privateKey = rsa.ToXmlString(true); // 私钥
 
    return Tuple.Create(publicKey, privateKey);
  }
}

使用公钥加密:(支持分段加密,普通单次加密可能会因为内容过长而报错)

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
public static string RSAEncrypt(string publicKey, string rawInput)
    {
      if (string.IsNullOrEmpty(rawInput))
      {
        return string.Empty;
      }
 
      if (string.IsNullOrWhiteSpace(publicKey))
      {
        throw new ArgumentException("Invalid Public Key");
      }
 
      using (var rsaProvider = new RSACryptoServiceProvider())
      {
        var inputBytes = Encoding.UTF8.GetBytes(rawInput);//有含义的字符串转化为字节流
        rsaProvider.FromXmlString(publicKey);//载入公钥
        int bufferSize = (rsaProvider.KeySize / 8) - 11;//单块最大长度
        var buffer = new byte[bufferSize];
        using (MemoryStream inputStream = new MemoryStream(inputBytes),
           outputStream = new MemoryStream())
        {
          while (true)
          { //分段加密
            int readSize = inputStream.Read(buffer, 0, bufferSize);
            if (readSize <= 0)
            {
              break;
            }
 
            var temp = new byte[readSize];
            Array.Copy(buffer, 0, temp, 0, readSize);
            var encryptedBytes = rsaProvider.Encrypt(temp, false);
            outputStream.Write(encryptedBytes, 0, encryptedBytes.Length);
          }
          return Convert.ToBase64String(outputStream.ToArray());//转化为字节流方便传输
        }
      }
    }

使用私钥解密:(支持分段解密,普通单次解密可能会因为密文过长而报错)

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
public static string RSADecrypt(string privateKey,string encryptedInput)
   {
     if (string.IsNullOrEmpty(encryptedInput))
     {
       return string.Empty;
     }
 
     if (string.IsNullOrWhiteSpace(privateKey))
     {
       throw new ArgumentException("Invalid Private Key");
     }
 
     using (var rsaProvider = new RSACryptoServiceProvider())
     {
       var inputBytes = Convert.FromBase64String(encryptedInput);
       rsaProvider.FromXmlString(privateKey);
       int bufferSize = rsaProvider.KeySize / 8;
       var buffer = new byte[bufferSize];
       using (MemoryStream inputStream = new MemoryStream(inputBytes),
          outputStream = new MemoryStream())
       {
         while (true)
         {
           int readSize = inputStream.Read(buffer, 0, bufferSize);
           if (readSize <= 0)
           {
             break;
           }
 
           var temp = new byte[readSize];
           Array.Copy(buffer, 0, temp, 0, readSize);
           var rawBytes = rsaProvider.Decrypt(temp, false);
           outputStream.Write(rawBytes, 0, rawBytes.Length);
         }
         return Encoding.UTF8.GetString(outputStream.ToArray());
       }
     }
   }

如果都是C#项目可能如上两个方法就可以了,但如果需要与WEB前端、JAVA等其它编程语言协同交互处理时(比如:WEB前端用公钥加密,后端C#私钥解密),则可能因为公钥与私钥的格式不相同而导致无法正常的进行对接【前端、JAVA 等语言使用的是PEM格式的,而C#使用的是XML格式】,网上查XML转PEM格式方案时,都是复制自:https://www.cnblogs.com/micenote/p/7862989.html 这篇文章,但其实这篇文章也只是写了私钥XML转PEM格式,并没有说明公钥XML如何转PEM格式,而且只写了支持从文件中获取内容再转换,方案不全,但是给了我思路,我经过各种验证,最终实现了比较友好的PEM与XML格式的相互转换方式,且经过单元测试验证通过,在此分享给大家。

如下是完整的XML与PEM格式转换器类代码;(注意需引入BouncyCastle nuget包)

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Math;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
 
namespace Zuowj.Common
{
  /// <summary>
  /// RSA公钥、私钥对格式(XML与PEM)转换器
  /// author:zuowenjun
  /// date:2020-12-29
  /// </summary>
  public static class RsaKeysFormatConverter
  {
    /// <summary>
    /// XML公钥转成Pem公钥
    /// </summary>
    /// <param name="xmlPublicKey"></param>
    /// <returns></returns>
    public static string XmlPublicKeyToPem(string xmlPublicKey)
    {
      RSAParameters rsaParam;
      using (RSACryptoServiceProvider rsa = new RSACryptoServiceProvider())
      {
        rsa.FromXmlString(xmlPublicKey);
        rsaParam = rsa.ExportParameters(false);
      }
      RsaKeyParameters param = new RsaKeyParameters(false, new BigInteger(1, rsaParam.Modulus), new BigInteger(1, rsaParam.Exponent));
 
      string pemPublicKeyStr = null;
      using (var ms = new MemoryStream())
      {
        using (var sw = new StreamWriter(ms))
        {
          var pemWriter = new Org.BouncyCastle.OpenSsl.PemWriter(sw);
          pemWriter.WriteObject(param);
          sw.Flush();
 
          byte[] buffer = new byte[ms.Length];
          ms.Position = 0;
          ms.Read(buffer, 0, (int)ms.Length);
          pemPublicKeyStr = Encoding.UTF8.GetString(buffer);
        }
      }
 
      return pemPublicKeyStr;
    }
 
    /// <summary>
    /// Pem公钥转成XML公钥
    /// </summary>
    /// <param name="pemPublicKeyStr"></param>
    /// <returns></returns>
    public static string PemPublicKeyToXml(string pemPublicKeyStr)
    {
      RsaKeyParameters pemPublicKey;
      using (var ms = new MemoryStream(Encoding.UTF8.GetBytes(pemPublicKeyStr)))
      {
        using (var sr = new StreamReader(ms))
        {
          var pemReader = new Org.BouncyCastle.OpenSsl.PemReader(sr);
          pemPublicKey = (RsaKeyParameters)pemReader.ReadObject();
        }
      }
 
      var p = new RSAParameters
      {
        Modulus = pemPublicKey.Modulus.ToByteArrayUnsigned(),
        Exponent = pemPublicKey.Exponent.ToByteArrayUnsigned()
      };
 
      string xmlPublicKeyStr;
      using (var rsa = new RSACryptoServiceProvider())
      {
        rsa.ImportParameters(p);
        xmlPublicKeyStr = rsa.ToXmlString(false);
      }
 
      return xmlPublicKeyStr;
    }
 
    /// <summary>
    /// XML私钥转成PEM私钥
    /// </summary>
    /// <param name="xmlPrivateKey"></param>
    /// <returns></returns>
    public static string XmlPrivateKeyToPem(string xmlPrivateKey)
    {
      RSAParameters rsaParam;
      using (RSACryptoServiceProvider rsa = new RSACryptoServiceProvider())
      {
        rsa.FromXmlString(xmlPrivateKey);
        rsaParam = rsa.ExportParameters(true);
      }
 
      var param = new RsaPrivateCrtKeyParameters(
        new BigInteger(1, rsaParam.Modulus), new BigInteger(1, rsaParam.Exponent), new BigInteger(1, rsaParam.D),
        new BigInteger(1, rsaParam.P), new BigInteger(1, rsaParam.Q), new BigInteger(1, rsaParam.DP), new BigInteger(1, rsaParam.DQ),
        new BigInteger(1, rsaParam.InverseQ));
 
      string pemPrivateKeyStr = null;
      using (var ms = new MemoryStream())
      {
        using (var sw = new StreamWriter(ms))
        {
          var pemWriter = new Org.BouncyCastle.OpenSsl.PemWriter(sw);
          pemWriter.WriteObject(param);
          sw.Flush();
 
          byte[] buffer = new byte[ms.Length];
          ms.Position = 0;
          ms.Read(buffer, 0, (int)ms.Length);
          pemPrivateKeyStr = Encoding.UTF8.GetString(buffer);
        }
      }
 
      return pemPrivateKeyStr;
    }
 
    /// <summary>
    /// Pem私钥转成XML私钥
    /// </summary>
    /// <param name="pemPrivateKeyStr"></param>
    /// <returns></returns>
    public static string PemPrivateKeyToXml(string pemPrivateKeyStr)
    {
      RsaPrivateCrtKeyParameters pemPrivateKey;
      using (var ms = new MemoryStream(Encoding.UTF8.GetBytes(pemPrivateKeyStr)))
      {
        using (var sr = new StreamReader(ms))
        {
          var pemReader = new Org.BouncyCastle.OpenSsl.PemReader(sr);
          var keyPair = (AsymmetricCipherKeyPair)pemReader.ReadObject();
          pemPrivateKey = (RsaPrivateCrtKeyParameters)keyPair.Private;
        }
      }
 
      var p = new RSAParameters
      {
        Modulus = pemPrivateKey.Modulus.ToByteArrayUnsigned(),
        Exponent = pemPrivateKey.PublicExponent.ToByteArrayUnsigned(),
        D = pemPrivateKey.Exponent.ToByteArrayUnsigned(),
        P = pemPrivateKey.P.ToByteArrayUnsigned(),
        Q = pemPrivateKey.Q.ToByteArrayUnsigned(),
        DP = pemPrivateKey.DP.ToByteArrayUnsigned(),
        DQ = pemPrivateKey.DQ.ToByteArrayUnsigned(),
        InverseQ = pemPrivateKey.QInv.ToByteArrayUnsigned(),
      };
 
      string xmlPrivateKeyStr;
      using (var rsa = new RSACryptoServiceProvider())
      {
        rsa.ImportParameters(p);
        xmlPrivateKeyStr = rsa.ToXmlString(true);
      }
 
      return xmlPrivateKeyStr;
    }
 
  }
}

如下是单元测试代码:

?
1
2
3
4
5
//公钥(XML、PEM格式互)测试
string srcPublicKey = “具体的XML Public Key”;
      string pemPublicKeyStr= RsaKeysFormatConverter.XmlPublicKeyToPem(publicKey);
      string xmlPublicKeyStr= RsaKeysFormatConverter.PemPublicKeyToXml(pemPublicKeyStr);
      Assert.AreEqual(srcPublicKey, xmlPublicKeyStr);
?
1
2
3
4
5
//私钥(XML、PEM格式互)测试
string srcPrivateKey = “具体的XML Private Key”;
      string pemPrivateKeyStr = RsaKeysFormatConverter.XmlPrivateKeyToPem(srcPrivateKey);
      string xmlPrivateKeyStr = RsaKeysFormatConverter.PemPrivateKeyToXml(pemPrivateKeyStr);
      Assert.AreEqual(privateKey,xmlPrivateKeyStr)

当然也可以不用这么费劲自己实现格式转换,可以使用在线网站直接转换:https://the-x.cn/certificate/XmlToPem.aspx ,另外也有一篇文章实现了类似的功能,但生成的PEM格式并非完整的格式,缺少注释头尾:https://www.cnblogs.com/datous/p/RSAKeyConvert.html

以上就是c# RSA非对称加解密及XML&PEM格式互换方案的详细内容,更多关于c# RSA非对称加解密的资料请关注服务器之家其它相关文章!

原文链接:https://www.cnblogs.com/zuowj/archive/2020/12/29/14207740.html

延伸 · 阅读

精彩推荐
  • C#C#多线程基础知识汇总

    C#多线程基础知识汇总

    这篇文章主要介绍了C#多线程基础知识的相关资料,文中示例代码非常详细,帮助大家更好的理解和学习,感兴趣的朋友可以了解下...

    萌萌丶小魔王7332022-09-21
  • C#C#中的TemplateMethod模式问题分析

    C#中的TemplateMethod模式问题分析

    这篇文章主要介绍了C#中的TemplateMethod模式,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考...

    伤之哀霜10232022-09-08
  • C#Winform应用程序如何使用自定义的鼠标图片

    Winform应用程序如何使用自定义的鼠标图片

    这篇文章主要介绍了Winform应用程序如何使用自定义的鼠标图片,在window系统中,自带的鼠标外观可能看起来比较小,因此我们需要使用自己的鼠标图片外观...

    一只独行的猿6222022-10-14
  • C#再聊一次C#中值类型和引用类型

    再聊一次C#中值类型和引用类型

    C#值类型,作为函数形参,形参被修改,不影响原值。这是我们在初始学习编程时需要记住的内容,我们也是一直这样践行的。...

    精益码农9842021-08-27
  • C#C#实现排序的代码详解

    C#实现排序的代码详解

    在本篇文章里小编给大家整理的是关于C#实现排序的代码以及相关知识点,需要的朋友们参考下。...

    萧静默7332022-08-07
  • C#C#编程获取IP地址的方法示例

    C#编程获取IP地址的方法示例

    这篇文章主要介绍了C#编程获取IP地址的方法,结合实例形式分析了C#获取客户端IP地址的具体实现技巧,需要的朋友可以参考下...

    pan_junbiao3762021-12-21
  • C#详解c# 中的DateTime

    详解c# 中的DateTime

    这篇文章主要介绍了c# 中的DateTime的相关资料,文中讲解非常细致,代码帮助大家更好的理解和学习,感兴趣的朋友可以了解下...

    Tiger.wang8252022-09-26
  • C#C#获取动态生成的CheckBox值

    C#获取动态生成的CheckBox值

    checkbox是VS2012的常用控件之一,可以方便的为某些功能取消或启用,下面教你如何简单使用checkbox。本文通过两种方法给大家介绍,需要的朋友一起看看吧...

    C#教程网9022021-10-27