说明:使用的私钥格式必须为RSA Key格式,如果为PKCS8 Key格式可以使用以下网址进行转换
https://www.chinassl.net/ssltools/convert-key.html
生成签名:
SafeUtil su = new SafeUtil();
string privateKeyPath = Application.StartupPath + @"\Files\rsa_private_key.pem";
string fileName = "d:\\xxx.file";
RSACryptoServiceProvider rsaCsp = su.LoadCertificateFile(privateKeyPath);
byte[] data = File.ReadAllBytes(fileName);
byte[] sign = rsaCsp.SignData(data, CryptoConfig.CreateFromName("SHA256"));
string str_SignedData = Convert.ToBase64String(sign);
验证签名:
RSACryptoServiceProvider rsaClear = new RSACryptoServiceProvider();
SafeUtil su = new SafeUtil();
string keyFile = Application.StartupPath + @"\Files\rsa_public_key.pem";
rsaClear = su.CreateRsaProviderFromPublicKey(keyFile);
bool result = rsaClear.VerifyData(data, CryptoConfig.CreateFromName("SHA256"), sign);
if (result)
{
MessageBox.Show("签名合法:" + name);
}
else
{
MessageBox.Show("签名非法:" + name);
}
签名算法实现:
using System;
using System.Text;
using System.Security.Cryptography;
using System.IO;
namespace RSASignature
{
public class SafeUtil
{
/// <summary>
/// 验证签名
/// </summary>
/// <param name="OriginalString">原文:UTF8编码</param>
/// <param name="SignatureString">签名:base64编码的字节</param>
/// <param name="publicKeyPath">公钥路径</param>
/// <returns> 验签结果</returns>
public bool Verify(String OriginalString, String SignatureString, String publicKeyPath)
{
//将base64签名数据转码为字节
byte[] signedBase64 = Convert.FromBase64String(SignatureString);
byte[] orgin = Encoding.UTF8.GetBytes(OriginalString);
//X509Certificate2 x509_Cer1 = new X509Certificate2(publicKeyPath);
RSACryptoServiceProvider oRSA = new RSACryptoServiceProvider();
//oRSA.FromXmlString(x509_Cer1.PublicKey.Key.ToXmlString(false));
oRSA = CreateRsaProviderFromPublicKey(publicKeyPath);
bool bVerify = oRSA.VerifyData(orgin, "SHA256", signedBase64);
return bVerify;
}
public bool Verify(byte[] orginData, String SignatureString, String publicKeyPath)
{
//将base64签名数据转码为字节
byte[] signedBase64 = Convert.FromBase64String(SignatureString);
//X509Certificate2 x509_Cer1 = new X509Certificate2(publicKeyPath);
RSACryptoServiceProvider oRSA = new RSACryptoServiceProvider();
//oRSA.FromXmlString(x509_Cer1.PublicKey.Key.ToXmlString(false));
byte[] base64 = File.ReadAllBytes(publicKeyPath);
string publickey = GetPemStr("PUBLIC KEY", base64);
oRSA = CreateRsaProviderFromPublicKey(publickey);
bool bVerify = oRSA.VerifyData(orginData, "SHA256", signedBase64);
return bVerify;
}
/// <summary>
/// 验证签名
/// </summary>
/// <param name="data">原文:UTF8编码</param>
/// <param name="privateKeyPath">证书路径:D:/certs/mycert.key</param>
/// <returns> 验签</returns>
public string Sign(string data, string privateKeyPath)
{
RSACryptoServiceProvider rsaCsp = LoadCertificateFile(privateKeyPath);
byte[] dataBytes = Encoding.UTF8.GetBytes(data);
byte[] signatureBytes = rsaCsp.SignData(dataBytes, "SHA256");
return Convert.ToBase64String(signatureBytes);
}
public string Sign(byte[] dataBytes, string privateKeyPath)
{
RSACryptoServiceProvider rsaCsp = LoadCertificateFile(privateKeyPath);
byte[] signatureBytes = rsaCsp.SignData(dataBytes, "SHA256");
return Convert.ToBase64String(signatureBytes);
}
private byte[] GetPem(string type, byte[] data)
{
string pem = Encoding.UTF8.GetString(data);
string header = String.Format("-----BEGIN {0}-----", type);
string footer = String.Format("-----END {0}-----", type);
int start = pem.IndexOf(header) + header.Length;
int end = pem.IndexOf(footer, start);
string base64 = pem.Substring(start, (end - start));
return Convert.FromBase64String(base64);
}
private string GetPemStr(string type, byte[] data)
{
string pem = Encoding.UTF8.GetString(data);
string header = String.Format("-----BEGIN {0}-----", type);
string footer = String.Format("-----END {0}-----", type);
int start = pem.IndexOf(header) + header.Length;
int end = pem.IndexOf(footer, start);
string base64 = pem.Substring(start, (end - start));
return base64;
}
public RSACryptoServiceProvider CreateRsaProviderFromPublicKey(string publicKeyPath)
{
// 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[] x509key;
byte[] seq = new byte[15];
int x509size;
byte[] base64 = File.ReadAllBytes(publicKeyPath);
string publickey = GetPemStr("PUBLIC KEY", base64);
x509key = Convert.FromBase64String(publickey);
x509size = x509key.Length;
// --------- 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
return null;
seq = binr.ReadBytes(15); //read the Sequence OID
if (!CompareBytearrays(seq, SeqOID)) //make sure Sequence for OID is correct
return null;
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
return null;
bt = binr.ReadByte();
if (bt != 0x00) //expect null byte next
return null;
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
return null;
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
return null;
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
return null;
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 -----
RSACryptoServiceProvider RSA = new RSACryptoServiceProvider();
RSAParameters RSAKeyInfo = new RSAParameters();
RSAKeyInfo.Modulus = modulus;
RSAKeyInfo.Exponent = exponent;
RSA.ImportParameters(RSAKeyInfo);
return RSA;
}
}
}
private 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;
}
public RSACryptoServiceProvider LoadCertificateFile(string filename)
{
using (System.IO.FileStream fs = System.IO.File.OpenRead(filename))
{
byte[] data = new byte[fs.Length];
byte[] res = null;
fs.Read(data, 0, data.Length);
if (data[0] != 0x30)
{
res = GetPem("RSA PRIVATE KEY", data);
}
try
{
RSACryptoServiceProvider rsa = DecodeRSAPrivateKey(res);
return rsa;
}
catch (Exception ex)
{
}
return null;
}
}
private RSACryptoServiceProvider DecodeRSAPrivateKey(byte[] privkey)
{
byte[] MODULUS, E, D, P, Q, DP, DQ, IQ;
// --------- Set up stream to decode the asn.1 encoded RSA private key ------
MemoryStream mem = new MemoryStream(privkey);
BinaryReader binr = new BinaryReader(mem); //wrap Memory Stream with BinaryReader for easy reading
byte bt = 0;
ushort twobytes = 0;
int elems = 0;
try
{
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
return null;
twobytes = binr.ReadUInt16();
if (twobytes != 0x0102) //version number
return null;
bt = binr.ReadByte();
if (bt != 0x00)
return null;
//------ all private key components are Integer sequences ----
elems = GetIntegerSize(binr);
MODULUS = binr.ReadBytes(elems);
elems = GetIntegerSize(binr);
E = binr.ReadBytes(elems);
elems = GetIntegerSize(binr);
D = binr.ReadBytes(elems);
elems = GetIntegerSize(binr);
P = binr.ReadBytes(elems);
elems = GetIntegerSize(binr);
Q = binr.ReadBytes(elems);
elems = GetIntegerSize(binr);
DP = binr.ReadBytes(elems);
elems = GetIntegerSize(binr);
DQ = binr.ReadBytes(elems);
elems = GetIntegerSize(binr);
IQ = binr.ReadBytes(elems);
// ------- create RSACryptoServiceProvider instance and initialize with public key -----
CspParameters CspParameters = new CspParameters();
CspParameters.Flags = CspProviderFlags.UseMachineKeyStore;
RSACryptoServiceProvider RSA = new RSACryptoServiceProvider(1024, CspParameters);
RSAParameters RSAparams = new RSAParameters();
RSAparams.Modulus = MODULUS;
RSAparams.Exponent = E;
RSAparams.D = D;
RSAparams.P = P;
RSAparams.Q = Q;
RSAparams.DP = DP;
RSAparams.DQ = DQ;
RSAparams.InverseQ = IQ;
RSA.ImportParameters(RSAparams);
return RSA;
}
catch (Exception ex)
{
return null;
}
finally
{
binr.Close();
}
}
private int GetIntegerSize(BinaryReader binr)
{
byte bt = 0;
byte lowbyte = 0x00;
byte highbyte = 0x00;
int count = 0;
bt = binr.ReadByte();
if (bt != 0x02) //expect integer
return 0;
bt = binr.ReadByte();
if (bt == 0x81)
count = binr.ReadByte(); // data size in next byte
else
if (bt == 0x82)
{
highbyte = binr.ReadByte(); // data size in next 2 bytes
lowbyte = binr.ReadByte();
byte[] modint = { lowbyte, highbyte, 0x00, 0x00 };
count = BitConverter.ToInt32(modint, 0);
}
else
{
count = bt; // we already have the data size
}
while (binr.ReadByte() == 0x00)
{ //remove high order zeros in data
count -= 1;
}
binr.BaseStream.Seek(-1, SeekOrigin.Current); //last ReadByte wasn't a removed zero, so back up a byte
return count;
}
}
//openssl genrsa -out d:\privkey.pem 1024
//openssl rsa -in d:\privkey.pem -pubout -out d:\pubkey.pem
}