密码学系列——数字签名(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;
}
密码学系列——数字签名(c# 代码实操)的更多相关文章
- 网络编程:多进程实现TCP服务端并发、互斥锁代码实操、线程理论、创建线程的两种方式、线程的诸多特性、GIL全局解释器锁、验证GIL的存在
目录 多进程实现TCP服务端并发 互斥锁代码实操 线程理论 创建线程的两种方式 线程的诸多特性 GIL全局解释器锁 验证GIL的存在 GIL与普通互斥锁 python多线程是否有用 死锁现象 多进程实 ...
- 密码学系列——常见的加密方式(c#代码实操)
前言 说起加密方式,其实密码学的角度ASCII编码其实本身就是一种加密解密. 由于其公开,现在用于数字与字符的转换. 查看ASCII表可以去官网查查. 转换代码如下: static void Main ...
- 密码学系列——消息摘要(c#代码实操)
前言 简介: 消息摘要(Message Digest)又称为数字摘要(Digital Digest) 它是一个唯一对应一个消息或文本的固定长度的值,它由一个单向Hash加密函数对消息进行作用而产生 使 ...
- mPaaS 小程序架构解析 | 实操演示小程序如何实现多端开发
对于 mPaaS 小程序开发框架,想必读者们并不陌生.它源自于支付宝小程序框架,继承了易开发性.跨平台性及 Native 性能,不仅帮助开发者实现面向自有 App 投放小程序,还可快速构建打包,覆盖支 ...
- ABP入门系列(1)——学习Abp框架之实操演练
作为.Net工地搬砖长工一名,一直致力于挖坑(Bug)填坑(Debug),但技术却不见长进.也曾热情于新技术的学习,憧憬过成为技术大拿.从前端到后端,从bootstrap到javascript,从py ...
- .net基础学java系列(四)Console实操
上一篇文章 .net基础学java系列(三)徘徊反思 本章节没啥营养,请绕路! 看视频,不实操,对于上了年龄的人来说,是记不住的!我已经看了几遍IDEA的教学视频: https://edu.51cto ...
- Istio的流量管理(实操二)(istio 系列四)
Istio的流量管理(实操二)(istio 系列四) 涵盖官方文档Traffic Management章节中的inrgess部分. 目录 Istio的流量管理(实操二)(istio 系列四) Ingr ...
- Istio的流量管理(实操一)(istio 系列三)
Istio的流量管理(实操一)(istio 系列三) 使用官方的Bookinfo应用进行测试.涵盖官方文档Traffic Management章节中的请求路由,故障注入,流量迁移,TCP流量迁移,请求 ...
- 动手实操:如何用 Python 实现人脸识别,证明这个杨幂是那个杨幂?
当前,人脸识别应用于许多领域,如支付宝的用户认证,许多的能识别人心情的 AI,也就是人的面部表情,还有能分析人的年龄等等,而这里面有着许多的难度,在这里我想要分享的是一个利用七牛 SDK 简单的实现人 ...
- 72 个网络应用安全实操要点,全方位保护 Web 应用的安全
原文地址:Web Application Security Checklist 原文作者:Teo Selenius(已授权) 译者 & 校正:HelloGitHub-小熊熊 & 卤蛋 ...
随机推荐
- WPF --- 重写圆角DataGrid样式
引言 因要符合UI设计, 需要一个圆角的 DataGrid 样式,其需要一个,所以需要重写DataGrid的样式, 代码 具体样式代码如下: <ResourceDictionary xmlns= ...
- 学习ASP.NET MVC 编程系列文章目录
学习ASP.NET MVC(一)--我的第一个ASP.NET MVC应用程序 学习ASP.NET MVC(二)--我的第一个ASP.NET MVC 控制器 学习ASP.NET MVC(三)--我的第一 ...
- AutoFill Chrome插件 影响 Vue接口读取,导致页面卡死,caution: request is not finished yet!
今天页面突然卡死了,也不知道是因为什么,直连服务器,能行,自己本机nginx的,系统访问某个特定的api就会卡死. 经过尝试,发现今天测试的AutoFill影响的.
- 候捷-C++程序设计(Ⅱ)兼谈对象模型
目录 笔记参考 学习目标 转换函数与explicit pointer-like classes function-like classes 模板template 模板特化与偏特化 模板模板参数 引用( ...
- arch签名出现问题时,无法修复时
sudo rm -rf /etc/pacman.d/gnupgsudo pacman-key --init sudo pacman-key --populate archlinux && ...
- Web安全前端基础
Web安全前端基础 1.Web前端介绍 2.前端代码语言简单学习 一.Web前端介绍 web前端就是前端网络编程,也被认为是用户端编程,是为了网页或者网页应用,而编写HTML,CSS以及JS代码,所以 ...
- day11-实现Spring底层机制-01
实现Spring底层机制-01 主要实现:初始化IOC容器+依赖注入+BeanPostProcessor机制+AOP 前面我们实际上已经使用代码简单实现了: Spring XML 注入 bean (S ...
- 虚拟现实(VR)在医疗保健中的5种应用
医疗保健中的VR虚拟现实 虚拟现实的由来已久,18世纪,法国的医生使用布制的分娩模拟器向助产师和外科医生教授医学技术.在20世纪60年代初,医生一边对心肺复苏学员口述心肺复苏的技巧,一边使用一家塑料玩 ...
- InfluxDB、Grafana、node_exporter、Prometheus搭建压测平台
InfluxDB.Grafana.node_exporter.Prometheus搭建压测平台 我们的压测平台的架构图如下: 配置docker环境 1)yum 包更新到最新 sudo yum upda ...
- 恶意软件开发(五)Linux shellcoding
什么是shellcode? Shellcode通常指的是一段用于攻击的机器码(二进制代码),可以被注入到目标计算机中并在其中执行.Shellcode 的目的是利用目标系统的漏洞或弱点,以获取系统控制权 ...