密码学系列——数字签名(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;
}