zoukankan      html  css  js  c++  java
  • 密码学系列——数字签名(c# 代码实操)

    前言

    结合消息摘要、非对称加密、数字签名三篇,进行代码实操。

    代码完整,可复制运行。

    正文

    代码如下:

    public class SignatureHelper
    {
    	/// <summary>
    	/// RSA签名
    	/// </summary>
    	/// <param name="content">数据</param>
    	/// <param name="privateKey">RSA密钥</param>
    	/// <returns></returns>
    	public static string rsaSign(string content, string privateKey)
    	{ 
    		if (string.IsNullOrEmpty(content))
    		{
    			throw new ArgumentNullException(nameof(content));
    		}
    		var rsaParameters = privateKeyToRSAParameters(privateKey);
    		using (var rsa = RSA.Create())
    		{
    			rsa.ImportParameters(rsaParameters);
    			return Base64.ToBase64String(rsa.SignData(Encoding.UTF8.GetBytes(content), HashAlgorithmName.SHA1, RSASignaturePadding.Pkcs1));
    		}
    	}
    	private static RSAParameters privateKeyToRSAParameters(string key)
    	{
    		var rsaParameters = new RSAParameters();
    		using (BinaryReader binr = new BinaryReader(new MemoryStream(Convert.FromBase64String(key))))
    		{
    			byte bt = 0;
    			ushort twobytes = 0;
    			twobytes = binr.ReadUInt16();
    			if (twobytes == 0x8130)
    				binr.ReadByte();
    			else if (twobytes == 0x8230)
    				binr.ReadInt16();
    			else
    				throw new Exception("Unexpected value read binr.ReadUInt16()");
    
    			twobytes = binr.ReadUInt16();
    			if (twobytes != 0x0102)
    				throw new Exception("Unexpected version");
    
    			bt = binr.ReadByte();
    			if (bt != 0x00)
    				throw new Exception("Unexpected value read binr.ReadByte()");
    
    			rsaParameters.Modulus = binr.ReadBytes(GetIntegerSize(binr));
    			rsaParameters.Exponent = binr.ReadBytes(GetIntegerSize(binr));
    			rsaParameters.D = binr.ReadBytes(GetIntegerSize(binr));
    			rsaParameters.P = binr.ReadBytes(GetIntegerSize(binr));
    			rsaParameters.Q = binr.ReadBytes(GetIntegerSize(binr));
    			rsaParameters.DP = binr.ReadBytes(GetIntegerSize(binr));
    			rsaParameters.DQ = binr.ReadBytes(GetIntegerSize(binr));
    			rsaParameters.InverseQ = binr.ReadBytes(GetIntegerSize(binr));
    		}
    		return rsaParameters;
    	}
    
    	private static bool CompareBytearrays(byte[] a, byte[] b)
    	{
    		if (a.Length != b.Length)
    			return false;
    		int i = 0;
    		foreach (byte c in a)
    		{
    			if (c != b[i])
    				return false;
    			i++;
    		}
    		return true;
    	}
    	private static RSAParameters publicKeyToRSAParameters(string publicKeyString)
    	{
    		// encoded OID sequence for  PKCS #1 rsaEncryption szOID_RSA_RSA = "1.2.840.113549.1.1.1"
    		byte[] seqOid = { 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01, 0x05, 0x00 };
    		byte[] seq = new byte[15];
    
    		var x509Key = Convert.FromBase64String(publicKeyString);
    
    		// ---------  Set up stream to read the asn.1 encoded SubjectPublicKeyInfo blob  ------
    		using (MemoryStream mem = new MemoryStream(x509Key))
    		{
    			using (BinaryReader binr = new BinaryReader(mem))  //wrap Memory Stream with BinaryReader for easy reading
    			{
    				byte bt = 0;
    				ushort twobytes = 0;
    
    				twobytes = binr.ReadUInt16();
    				if (twobytes == 0x8130) //data read as little endian order (actual data order for Sequence is 30 81)
    					binr.ReadByte();    //advance 1 byte
    				else if (twobytes == 0x8230)
    					binr.ReadInt16();   //advance 2 bytes
    				else
    				   throw new Exception("Unexpected value read binr.ReadUInt16()");
    
    				seq = binr.ReadBytes(15);       //read the Sequence OID
    				if (!CompareBytearrays(seq, seqOid))    //make sure Sequence for OID is correct
    					throw new Exception("Unexpected value read binr.ReadUInt16()");
    
    				twobytes = binr.ReadUInt16();
    				if (twobytes == 0x8103) //data read as little endian order (actual data order for Bit String is 03 81)
    					binr.ReadByte();    //advance 1 byte
    				else if (twobytes == 0x8203)
    					binr.ReadInt16();   //advance 2 bytes
    				else
    					throw new Exception("Unexpected value read binr.ReadUInt16()");
    
    				bt = binr.ReadByte();
    				if (bt != 0x00)     //expect null byte next
    					throw new Exception("Unexpected value read binr.ReadUInt16()");
    
    				twobytes = binr.ReadUInt16();
    				if (twobytes == 0x8130) //data read as little endian order (actual data order for Sequence is 30 81)
    					binr.ReadByte();    //advance 1 byte
    				else if (twobytes == 0x8230)
    					binr.ReadInt16();   //advance 2 bytes
    				else
    					throw new Exception("Unexpected value read binr.ReadUInt16()");
    				twobytes = binr.ReadUInt16();
    				byte lowbyte = 0x00;
    				byte highbyte = 0x00;
    
    				if (twobytes == 0x8102) //data read as little endian order (actual data order for Integer is 02 81)
    					lowbyte = binr.ReadByte();  // read next bytes which is bytes in modulus
    				else if (twobytes == 0x8202)
    				{
    					highbyte = binr.ReadByte(); //advance 2 bytes
    					lowbyte = binr.ReadByte();
    				}
    				else
    					throw new Exception("Unexpected value read binr.ReadUInt16()");
    				byte[] modint = { lowbyte, highbyte, 0x00, 0x00 };   //reverse byte order since asn.1 key uses big endian order
    				int modsize = BitConverter.ToInt32(modint, 0);
    
    				int firstbyte = binr.PeekChar();
    				if (firstbyte == 0x00)
    				{   //if first byte (highest order) of modulus is zero, don't include it
    					binr.ReadByte();    //skip this null byte
    					modsize -= 1;   //reduce modulus buffer size by 1
    				}
    
    				byte[] modulus = binr.ReadBytes(modsize);   //read the modulus bytes
    
    				if (binr.ReadByte() != 0x02)            //expect an Integer for the exponent data
    					throw new Exception("Unexpected value read binr.ReadUInt16()");
    				int expbytes = (int)binr.ReadByte();        // should only need one byte for actual exponent data (for all useful values)
    				byte[] exponent = binr.ReadBytes(expbytes);
    
    				// ------- create RSACryptoServiceProvider instance and initialize with public key -----
    				var rsa = RSA.Create();
    				RSAParameters rsaKeyInfo = new RSAParameters
    				{
    					Modulus = modulus,
    					Exponent = exponent
    				};
    				return rsaKeyInfo;
    			}
    		}
    	}
    
    	private static int GetIntegerSize(BinaryReader binr)
    	{
    		byte bt = 0;
    		int count = 0;
    		bt = binr.ReadByte();
    		if (bt != 0x02)
    			return 0;
    		bt = binr.ReadByte();
    
    		if (bt == 0x81)
    			count = binr.ReadByte();
    		else
    		if (bt == 0x82)
    		{
    			var highbyte = binr.ReadByte();
    			var lowbyte = binr.ReadByte();
    			byte[] modint = { lowbyte, highbyte, 0x00, 0x00 };
    			count = BitConverter.ToInt32(modint, 0);
    		}
    		else
    		{
    			count = bt;
    		}
    
    		while (binr.ReadByte() == 0x00)
    		{
    			count -= 1;
    		}
    		binr.BaseStream.Seek(-1, SeekOrigin.Current);
    		return count;
    	}
    
    	/// <summary>
    	/// 验证数字签名
    	/// </summary>
    	/// <param name="plaintext">明文</param>
    	/// <param name="signedData">数字签名</param>
    	/// <param name="publicKey">公钥</param>
    	/// <returns></returns>
    	public static bool verifySigned(string plaintext, string signedData, string publicKey)
    	{
    		if (string.IsNullOrEmpty(plaintext))
    		{
    			throw new ArgumentNullException(nameof(plaintext));
    		}
    
    		if (string.IsNullOrEmpty(signedData))
    		{
    			throw new ArgumentNullException(nameof(signedData));
    		}
    
    		using (var rsa = RSA.Create())
    		{
    			var rsaParameters = publicKeyToRSAParameters(publicKey);
    			rsa.ImportParameters(rsaParameters);
    			return rsa.VerifyData(Encoding.UTF8.GetBytes(plaintext), Convert.FromBase64String(signedData), HashAlgorithmName.SHA1, RSASignaturePadding.Pkcs1);
    		}
    	}
    }
    

    测试:

    static void Main(string[] args)
    {
    	RSAKeyParameter rsa1=Pkcs1(1024);
    	Console.WriteLine("公钥为:"+rsa1.PublicKey+"私钥为:"+rsa1.PrivateKey);
    	string input = "奥氏的家园";
    	Console.WriteLine("内容:" + input);
    	var signData=SignatureHelper.rsaSign(input,rsa1.PrivateKey);
    	Console.WriteLine("数字签名为:" + signData);
    	var isSuccess=SignatureHelper.verifySigned(input, signData,rsa1.PublicKey);
    	Console.WriteLine("验证是否成功:" + isSuccess);
    	Console.ReadKey();
    }
    /// <summary>
    /// pkcs1 rsa 加密
    /// </summary>
    /// <param name="size">秘钥长度,一般为1024的倍数</param>
    /// <param name="pemFormat">是否转换成大小</param>
    /// <returns></returns>
    public static RSAKeyParameter Pkcs1(int size, bool pemFormat = false)
    {
    	var keyGenerator = GeneratorUtilities.GetKeyPairGenerator("RSA");
    	keyGenerator.Init(new KeyGenerationParameters(new SecureRandom(), size));
    	var keyPair = keyGenerator.GenerateKeyPair();
    	var subjectPublicKeyInfo=SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(keyPair.Public);
    	var privateKeyInfo= PrivateKeyInfoFactory.CreatePrivateKeyInfo(keyPair.Private);
    	if (!pemFormat)
    	{
    		return new RSAKeyParameter
    		{
    			PrivateKey = Base64.ToBase64String(privateKeyInfo.ParsePrivateKey().GetEncoded()),
    			PublicKey = Base64.ToBase64String(subjectPublicKeyInfo.GetEncoded())
    		};
    	}
    	var rsaKey = new RSAKeyParameter();
    	using (var sw=new StringWriter())
    	{
    		var pWrt = new PemWriter(sw);
    		pWrt.WriteObject(keyPair.Private);
    		pWrt.Writer.Close();
    		rsaKey.PrivateKey = sw.ToString();
    	}
    	using (var sw = new StringWriter())
    	{
    		var pWrt = new PemWriter(sw);
    		pWrt.WriteObject(keyPair.Public);
    		pWrt.Writer.Close();
    		rsaKey.PublicKey = sw.ToString();
    	}
    	return rsaKey;
    }
    
  • 相关阅读:
    小米范工具系列之二:小米范 web目录扫描器
    小米范工具系列之一:小米范 web查找器
    Layer子域名挖掘机
    unity第一人称
    浏览器的兼容大坑
    java掉系統进程
    java写的服务器,处理多个请求
    java网络练习一
    javaWeb开发原理
    unity传统GUI学习一.
  • 原文地址:https://www.cnblogs.com/aoximin/p/13503009.html
Copyright © 2011-2022 走看看