C#RSA加密解密

本文不详细阐述RSA原理,而是直接展示在C#项目中如何使用.pem格式的公钥和私钥进行加密解密操作。通过读取.pem文件内容,实例化RSA对象并调用加密接口完成加密过程。提供了相关源码和参考git库。
摘要由CSDN通过智能技术生成

开门见山
网上关于RSA的介绍很多,这里就不再赘述。直接列我们在项目中的应用。
一般导出的公钥私钥格式都是xml或者.pem
我们使用的是.pem格式。
1、读取出.pem的内容;
2、用.pem内容实例化RSA对象;
3、调用RSA对象的加密接口,参数是要加密的内容。
源码如下:
Pem类

using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Numerics;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
using UnityEngine;

namespace Utils.StandardUtils.RSA
{
   
	[Serializable]
	public class RSA_PEM 
	{
   
		/// <summary>
		/// modulus 模数n,公钥、私钥都有
		/// </summary>
		public byte[] Key_Modulus;
		/// <summary>
		/// publicExponent 公钥指数e,公钥、私钥都有
		/// </summary>
		public byte[] Key_Exponent;
		/// <summary>
		/// privateExponent 私钥指数d,只有私钥的时候才有
		/// </summary>
		public byte[] Key_D;

		//以下参数只有私钥才有
		/// <summary>
		/// prime1
		/// </summary>
		public byte[] Val_P;
		/// <summary>
		/// prime2
		/// </summary>
		public byte[] Val_Q;
		/// <summary>
		/// exponent1
		/// </summary>
		public byte[] Val_DP;
		/// <summary>
		/// exponent2
		/// </summary>
		public byte[] Val_DQ;
		/// <summary>
		/// coefficient
		/// </summary>
		public byte[] Val_InverseQ;

		private RSA_PEM() {
    }

		/// <summary>
		/// 通过RSA中的公钥和私钥构造一个PEM,如果convertToPublic含私钥的RSA将只读取公钥,仅含公钥的RSA不受影响
		/// </summary>
		public RSA_PEM(RSACryptoServiceProvider rsa, bool convertToPublic = false) {
   
			var isPublic = convertToPublic || rsa.PublicOnly;
			var param = rsa.ExportParameters(!isPublic);

			Key_Modulus = param.Modulus;
			Key_Exponent = param.Exponent;

			if (!isPublic) {
   
				Key_D = param.D;

				Val_P = param.P;
				Val_Q = param.Q;
				Val_DP = param.DP;
				Val_DQ = param.DQ;
				Val_InverseQ = param.InverseQ;
			}
		}
		/// <summary>
		/// 通过全量的PEM字段数据构造一个PEM,除了模数modulus和公钥指数exponent必须提供外,其他私钥指数信息要么全部提供,要么全部不提供(导出的PEM就只包含公钥)
		/// 注意:所有参数首字节如果是0,必须先去掉
		/// </summary>
		public RSA_PEM(byte[] modulus, byte[] exponent, byte[] d, byte[] p, byte[] q, byte[] dp, byte[] dq, byte[] inverseQ) {
   
			Key_Modulus = modulus;
			Key_Exponent = exponent;
			Key_D = BigL(d, modulus.Length);

			int keyLen = modulus.Length / 2;
			Val_P = BigL(p, keyLen);
			Val_Q = BigL(q, keyLen);
			Val_DP = BigL(dp, keyLen);
			Val_DQ = BigL(dq, keyLen);
			Val_InverseQ = BigL(inverseQ, keyLen);
		}
		/// <summary>
		/// 通过公钥指数和私钥指数构造一个PEM,会反推计算出P、Q但和原始生成密钥的P、Q极小可能相同
		/// 注意:所有参数首字节如果是0,必须先去掉
		/// 出错将会抛出异常
		/// </summary>
		/// <param name="modulus">必须提供模数</param>
		/// <param name="exponent">必须提供公钥指数</param>
		/// <param name="dOrNull">私钥指数可以不提供,导出的PEM就只包含公钥</param>
		public RSA_PEM(byte[] modulus, byte[] exponent, byte[] dOrNull) {
   
			Key_Modulus = modulus;//modulus
			Key_Exponent = exponent;//publicExponent

			if (dOrNull != null) {
   
				Key_D = BigL(dOrNull, modulus.Length);//privateExponent

				//反推P、Q
				BigInteger n = BigX(modulus);
				BigInteger e = BigX(exponent);
				BigInteger d = BigX(dOrNull);
				BigInteger p = FindFactor(e, d, n);
				BigInteger q = n / p;
				if (p.CompareTo(q) > 0) {
   
					BigInteger t = p;
					p = q;
					q = t;
				}
				BigInteger exp1 = d % (p - BigInteger.One);
				BigInteger exp2 = d % (q - BigInteger.One);
				BigInteger coeff = BigInteger.ModPow(q, p - 2, p);

				int keyLen = modulus.Length / 2;
				Val_P = BigL(BigB(p), keyLen);//prime1
				Val_Q = BigL(BigB(q), keyLen);//prime2
				Val_DP = BigL(BigB(exp1), keyLen);//exponent1
				Val_DQ = BigL(BigB(exp2), keyLen);//exponent2
				Val_InverseQ = BigL(BigB(coeff), keyLen);//coefficient
			}
		}

		/// <summary>
		/// 密钥位数
		/// </summary>
		public int KeySize {
   
			get {
   
				return Key_Modulus.Length * 8;
			}
		}
		/// <summary>
		/// 是否包含私钥
		/// </summary>
		public bool HasPrivate {
   
			get {
   
				return Key_D != null;
			}
		}
		/// <summary>
		/// 将PEM中的公钥私钥转成RSA对象,如果未提供私钥,RSA中就只包含公钥
		/// </summary>
		public RSACryptoServiceProvider GetRSA() {
   
			var rsaParams = new CspParameters();
			rsaParams.Flags = CspProviderFlags.UseMachineKeyStore;
			var rsa = new RSACryptoServiceProvider(rsaParams);

			var param = new RSAParameters();
			param.Modulus = Key_Modulus;
			param.Exponent = Key_Exponent;
			if (Key_D != null) {
   
				param.D = Key_D;
				param.P = Val_P;
				param.Q = Val_Q;
				param.DP = Val_DP;
				param.DQ = Val_DQ;
				param.InverseQ = Val_InverseQ;
			}
			rsa.ImportParameters(param);
			return rsa;
		}
		/// <summary>
		/// 转成正整数,如果是负数,需要加前导0转成正整数
		/// </summary>
		static public BigInteger BigX(byte[] bigb) {
   
			if (bigb[0] > 0x7F) {
   
				byte[] c = new byte[bigb.Length + 1];
				Array.Copy(bigb, 0, c, 1, bigb.Length);
				bigb = c;
			}
			return new BigInteger(bigb.Reverse().ToArray());//C#的二进制是反的
		}
		/// <summary>
		/// BigInt导出byte整数首字节>0x7F的会加0前导,保证正整数,因此需要去掉0
		/// </summary>
		static public byte[] BigB(BigInteger bigx) {
   
			byte[] val = bigx.ToByteArray().Reverse().ToArray();//C#的二进制是反的
			if (val[0] == 0) {
   
				byte[] c = new byte[val.Length - 1];
				Array.Copy(val, 1, c, 0, c.Length);
				val = c;
			}
			return val;
		}
		/// <summary>
		/// 某些密钥参数可能会少一位(32个byte只有31个,目测是密钥生成器的问题,只在c#生成的密钥中发现这种参数,java中生成的密钥没有这种现象),直接修正一下就行;这个问题与BigB有本质区别,不能动BigB
		/// </summary>
		static public byte[] BigL(byte[] bytes, int keyLen) {
   
			if (keyLen - bytes.Length == 1) {
   
				byte[] c = new byte[bytes.Length + 1];
				Array.Copy(bytes, 0, c, 1, bytes.Length);
				bytes = c;
			}
			return bytes;
		}
  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值