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

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

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

        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);
}
}

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

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());//转化为字节流方便传输
}
}
}

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

 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格式,而且只写了支持从文件中获取内容再转换,方案不全,但是给了我(梦在旅途,http://www.zuowenjun.cn or zuowj.cnblogs.com.cn)思路,我经过各种验证,最终实现了比较友好的PEM与XML格式的相互转换方式,且经过单元测试验证通过,在此分享给大家。

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

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;
} }
}

如下是单元测试代码:

//公钥(XML、PEM格式互)测试
string srcPublicKey = “具体的XML Public Key”;
string pemPublicKeyStr= RsaKeysFormatConverter.XmlPublicKeyToPem(publicKey);
string xmlPublicKeyStr= RsaKeysFormatConverter.PemPublicKeyToXml(pemPublicKeyStr);
Assert.AreEqual(srcPublicKey, xmlPublicKeyStr);
//私钥(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

【实用小技巧】RSA非对称加解密及XML&PEM格式互换方案的更多相关文章

  1. php rsa 非对称加解密类

    <?php header("Content-Type: text/html;charset=utf-8"); /* 生成公钥.私钥对,私钥加密的内容能通过公钥解密(反过来亦可 ...

  2. ios php RSA 非对称加密解密 der 和pem生成

    ios 使用public_key.der加密 php 使用 private_key.pem解密 openssl req -x509 -out public_key.der -outform der - ...

  3. [Python3] RSA的加解密和签名/验签实现 -- 使用pycrytodome

    Crypto 包介绍: pycrypto,pycrytodome 和 crypto 是一个东西,crypto 在 python 上面的名字是 pycrypto 它是一个第三方库,但是已经停止更新,所以 ...

  4. VC6.0实用小技巧

    VC6.0的若干实用小技巧 .检测程序中的括号是否匹配 把光标移动到需要检测的括号(如大括号{}.方括号[].圆括号()和尖括号<>)前面,键入快捷键 “Ctrl+]”.如果括号匹配正确, ...

  5. 非对称加解密 Asymmetric encryption 对称加密和非对称加密的区别

    考虑这样一个问题:一切的装备文件都存储在 Git 长途库房,RAR密码破解装备文件中的一些信息又是比较灵敏的.所以,我们需求对这些灵敏信息进行加密处理.首要的加密方法分为两种:一种是同享密钥加 密(对 ...

  6. PHP 常用函数库和一些实用小技巧

    PHP 常用函数库和一些实用小技巧 作者: 字体:[增加 减小] 类型:转载   包括文件读取函式,文件写入函式,静态页面生成函式,目录删除函式等   文件读取函式 //文件读取函式 function ...

  7. Vim实用小技巧

    Vim实用小技巧 一些网络上质量较高的Vim资料 从我07年接触Vim以来,已经过去了8个年头,期间看过很多的Vim文章,我自己觉得非常不错,而且创作时间也比较近的文章有如下这些. Vim入门 目前为 ...

  8. svn checkout 实用小技巧

    svn checkout 实用小技巧 by:授客 QQ:1033553122   问题描述: 用svn小乌龟软件,进行update,commit之前,先要把svn工作目录checkout到本地,那么问 ...

  9. 实用小技巧(一):UIScrollView中上下左右滚动方向的判断

    https://www.jianshu.com/p/93e8459b6dae 2017.06.01 01:13* 字数 674 阅读 1201评论 0喜欢 1 2017.06.01 01:13* 字数 ...

  10. SM2的非对称加解密java工具类

    maven依赖 <dependency> <groupId>org.bouncycastle</groupId> <artifactId>bcprov- ...

随机推荐

  1. 入门篇-其之十一-流程控制之break和continue关键字

    本文中使用到的工具是Intellij IDEA和JDK 8,需要安装两款工具的请查看这两篇教程:点我查看安装JDK8/11/17教程.点我查看安装Intellij IDEA教程. 一.循环的嵌套 和前 ...

  2. 深度学习(三)——Transforms的使用

    一.Transforms的结构及用法 导入transforms from torchvision import transforms 作用:图片输入transforms后,可以得到一些预期的变换 1. ...

  3. 电机控制和Linux驱动开发哪个方向更好呢?

    电机控制和Linux驱动开发哪个方向更好呢? 先说结论:任何一个领域,就像世间的五行,阴阳结合,虚实结合,利弊结合.对于哪个更好,不能一概而论,最重要的是要搞清楚,你更适合哪个? 1.共鸣 当我看到这 ...

  4. JVM大页内存的学习与使用

    JVM大页内存的学习与使用 原理和背景 操作系统是计算机的重要组成部分. 现代的操作系统一般都采用 段页式内存管理. 段一般是为了管理和权限 页主要是为了虚拟内存和物理内存的映射. 分页管理可以让物理 ...

  5. [转帖]Jmeter性能测试:高并发分布式性能测试

    一.为什么要进行分布式性能测试 当进行高并发性能测试的时候,受限于Jmeter工具本身和电脑硬件的原因,无法满足我们对大并发性能测试的要求.基于这种场景下,我们就需要采用分布式的方式来实现我们高并发的 ...

  6. [转帖]金仓数据库KingbaseES数据目录结构

    KingbaseES数据库结构 [kingbase@postgresV8]$ tree -LP2data/ . ├── data │ ├── base # 存储用户创建的数据库文件及隶属于用户数据库的 ...

  7. [转帖]Python安装模块(包/库)的方法

    这里写目录标题 通过pip安装 正常在线安装 pip命令补全 更改下载镜像 离线包安装 库的下载 库的安装 whl的安装 .tar.gz的安装 源码安装 本地安装报错(依赖) Pycharm中安装 手 ...

  8. [转帖]Unixbench的使用(综合性能测试、2D测试)和问题解决(跑不出多线程分数,调不出窗口,报错等)

    一.Unixbench简介 Unixbench一个基于系统的基准测试工具,不单纯是CPU 内存 或者磁盘测试工具.测试结果不仅仅取决于硬件,也取决于系统.开发库.甚至是编译器.Unixbench是一个 ...

  9. [转帖]焱融全闪系列科普| 为什么 SSD 需要 NVMe?

    https://xie.infoq.cn/article/7026237b455c7d62f33afc4a9 NVMe 的由来 目前机械硬盘大多数使用 SATA (Serial ATA Advance ...

  10. [转帖]linux学习:sed与awk与tr用法整理

    https://www.cnblogs.com/LO-gin/p/6882490.html 流编辑器:sed 语法:sed [-hnV][-e<script>][-f<script文 ...