前言

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

代码完整,可复制运行。

正文

代码如下:

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# 代码实操)的更多相关文章

  1. 网络编程:多进程实现TCP服务端并发、互斥锁代码实操、线程理论、创建线程的两种方式、线程的诸多特性、GIL全局解释器锁、验证GIL的存在

    目录 多进程实现TCP服务端并发 互斥锁代码实操 线程理论 创建线程的两种方式 线程的诸多特性 GIL全局解释器锁 验证GIL的存在 GIL与普通互斥锁 python多线程是否有用 死锁现象 多进程实 ...

  2. 密码学系列——常见的加密方式(c#代码实操)

    前言 说起加密方式,其实密码学的角度ASCII编码其实本身就是一种加密解密. 由于其公开,现在用于数字与字符的转换. 查看ASCII表可以去官网查查. 转换代码如下: static void Main ...

  3. 密码学系列——消息摘要(c#代码实操)

    前言 简介: 消息摘要(Message Digest)又称为数字摘要(Digital Digest) 它是一个唯一对应一个消息或文本的固定长度的值,它由一个单向Hash加密函数对消息进行作用而产生 使 ...

  4. mPaaS 小程序架构解析 | 实操演示小程序如何实现多端开发

    对于 mPaaS 小程序开发框架,想必读者们并不陌生.它源自于支付宝小程序框架,继承了易开发性.跨平台性及 Native 性能,不仅帮助开发者实现面向自有 App 投放小程序,还可快速构建打包,覆盖支 ...

  5. ABP入门系列(1)——学习Abp框架之实操演练

    作为.Net工地搬砖长工一名,一直致力于挖坑(Bug)填坑(Debug),但技术却不见长进.也曾热情于新技术的学习,憧憬过成为技术大拿.从前端到后端,从bootstrap到javascript,从py ...

  6. .net基础学java系列(四)Console实操

    上一篇文章 .net基础学java系列(三)徘徊反思 本章节没啥营养,请绕路! 看视频,不实操,对于上了年龄的人来说,是记不住的!我已经看了几遍IDEA的教学视频: https://edu.51cto ...

  7. Istio的流量管理(实操二)(istio 系列四)

    Istio的流量管理(实操二)(istio 系列四) 涵盖官方文档Traffic Management章节中的inrgess部分. 目录 Istio的流量管理(实操二)(istio 系列四) Ingr ...

  8. Istio的流量管理(实操一)(istio 系列三)

    Istio的流量管理(实操一)(istio 系列三) 使用官方的Bookinfo应用进行测试.涵盖官方文档Traffic Management章节中的请求路由,故障注入,流量迁移,TCP流量迁移,请求 ...

  9. 动手实操:如何用 Python 实现人脸识别,证明这个杨幂是那个杨幂?

    当前,人脸识别应用于许多领域,如支付宝的用户认证,许多的能识别人心情的 AI,也就是人的面部表情,还有能分析人的年龄等等,而这里面有着许多的难度,在这里我想要分享的是一个利用七牛 SDK 简单的实现人 ...

  10. 72 个网络应用安全实操要点,全方位保护 Web 应用的安全

    原文地址:Web Application Security Checklist 原文作者:Teo Selenius(已授权) 译者 & 校正:HelloGitHub-小熊熊 & 卤蛋 ...

随机推荐

  1. 13 Codeforces Round 886 (Div. 4)G. The Morning Star(简单容斥)

    G. The Morning Star 思路:用map记录x,y,以及y-x.y+x 从前往后统计一遍答案即可 公式\(ans+=cnt[x]+cnt[y]-2 * cnt[x,y]+cnt[y+x] ...

  2. MYSQL索引-下

    前言 丁奇老师的 MYSQL45讲 的关于索引的的两节 深入浅出索引,做个笔记 覆盖索引 插入一个实例表 mysql> create table T ( ID int primary key, ...

  3. snipaste 截屏工具快捷键 alt + A 还有 Ctrl + Shift + A

    snipaste 截屏工具快捷键 alt + A 还有 Ctrl + Shift + A 因为有时候 alt 会取消右键等菜单

  4. iview viewDesign table 单选 Radio

    columns.js export default [ { title: '选择', slot: 'choose', align: 'center', width: 100 }, table里面 &l ...

  5. Harris/Shi-Tomasi角点检测

    机器视觉--角点检测 什么是角点检测 在几何学里,我们会看到各种各样的三角形.多边形等,它们都有一个显著的特征:包含了角点信息.比如在三角形里,我们有三个角:在矩形里,我们有四个角.我们将找到这些图像 ...

  6. 「AntV」基于众源轨迹数据的三维路网生成与L7可视化

    1. 引言 L7 地理空间数据可视分析引擎是一种基于 WebGL 技术的地理空间数据可视化引擎,可以用于实现各种地理空间数据可视化应用.L7 引擎支持多种数据源和数据格式,包括 GeoJSON.CSV ...

  7. 爬虫实战:探索XPath爬虫技巧之热榜新闻

    之前我们已经详细讨论了如何使用BeautifulSoup这个强大的工具来解析HTML页面,另外还介绍了利用在线工具来抓取HTTP请求以获取数据的方法.在今天的学习中,我们将继续探讨另一种常见的网络爬虫 ...

  8. kali 2018.2镜像安装

    本文链接来源 Kali Linux 前身是著名渗透测试系统BackTrack ,是一个基于 Debian 的 Linux 发行版,包含很多安全和取证方面的相关工具.此次通过VMware虚拟机安装201 ...

  9. base64格式上传图片后台写入

    前台 var reader = new FileReader(); reader.readAsDataURL(file); reader.onload = function(e){//回调 } 后台 ...

  10. linux安装/切换不同版本c/c++

    查看ubuntu系统上g++的版本: ls /usr/bin/g++* 安装指定版本gcc和g++ # 以version == 4.9为例 sudo apt-get install gcc-4.9 g ...