Это скорее пример, т. к. н-р File.ReadAllBytes ну так-себе затея, но суть видна.
1 подготовительные работы.
Создаем самоподписанный p12/pfx сертификат (содержит и публичный сертификат и закрытый ключ)
(в реальности нужно получить сертификат у публичного доверенного центра и т. д.)
testpass - пароль для доступа к приватному ключу
makecrt -r -n "CN=Test CA" -p12 TestCA.p12 testpass
2 Из p12 извлекаем crt (публичный сертификат). Наверное из командной строки это можно было сделать через openssl, но встроеннй просмотрщик сертификатов позволял экспортировать.
3 Добавляем crt сертификат в доверенные корневые. В Mono желательно добавить и в CA и в Trusted (в Винд есть certmgr,msc)
certmgr -add -c CA TestCA.crt
certmgr -add -c Trust TestCA.crt
Код:
using System;
using System.IO;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
public class Test
{
public static void Main() {
//Имя подопытного файла
const string fileName = "/путь_к_/Test.txt";
//Имя файла в котором будет подпись
const string fileSign = "/путь_к_/Test.sgn";
//Сертификат с приватным ключом (p12 он-же pfx)
const string fileCertPriv = "/путь_к_/TestCA.p12";
//Сертификат с публичным ключом
const string fileCertPub = "/путь_к_/TestCA.crt";
//--- Лень разбивать на функции - создание подопытного файла, чтение сертификата с приватным ключом, подпись файла
{
X509Certificate2 cert = new X509Certificate2(fileCertPriv, "testpass");//, X509KeyStorageFlags.Exportable | X509KeyStorageFlags.PersistKeySet);
if (!cert.HasPrivateKey) {
Console.WriteLine ("Certificate haven't a private key");
return;
}
Console.WriteLine ("Creating file");
File.WriteAllText(fileName, "hello world");
RSACryptoServiceProvider rsa = (RSACryptoServiceProvider)cert.PrivateKey;
var sr = new StreamReader(fileName);
var sign = rsa.SignData(sr.BaseStream, CryptoConfig.MapNameToOID("MD5"));
sr.Close ();
Console.WriteLine("Sign : {0}", BitConverter.ToString(sign));
File.WriteAllBytes(fileSign, sign);
}
//--- Тут обратная работа с проверкой сертиф.
{
//Обратите внимание! Используем уже публичный сертификат
X509Certificate2 cert = new X509Certificate2(fileCertPub);
//Проверяем цепочку сертификатов
X509Chain chain = new X509Chain();
X509ChainPolicy chainPolicy = new X509ChainPolicy()
{
RevocationMode = X509RevocationMode.Online,
RevocationFlag = X509RevocationFlag.EntireChain
};
chain.ChainPolicy = chainPolicy;
if (!chain.Build(cert)) {
Console.WriteLine("WARNING Certificate error {");
foreach (var chainElement in chain.ChainElements)
foreach (X509ChainStatus chainStatus in chainElement.ChainElementStatus)
Console.WriteLine(chainStatus.StatusInformation);
Console.WriteLine("}");
//Не будем выходить при ошибках проверки, а так-то в реалиях на выход =)
//return;
}
//Используем уже публичный ключ!
RSACryptoServiceProvider rsa = (RSACryptoServiceProvider)cert.PublicKey.Key;
Console.WriteLine ("Checking file before changing...");
if (!rsa.VerifyData (File.ReadAllBytes(fileName), CryptoConfig.MapNameToOID("MD5"), File.ReadAllBytes(fileSign))) {
Console.WriteLine ("Uh-oh... Something wrong! File is not ok");
return;
}
Console.WriteLine ("File is ok");
Console.WriteLine ("Changing file...");
File.WriteAllText (fileName, "bad ^_^"); //Портим файл =)
if (rsa.VerifyData (File.ReadAllBytes (fileName), CryptoConfig.MapNameToOID ("MD5"), File.ReadAllBytes (fileSign))) {
Console.WriteLine ("Uh-oh... Something wrong! File is ok");
return;
}
Console.WriteLine ("File is not ok =)");
}
}
}
Скрин:

Можно использовать хранилище сертификатов (.NET/Mono это позволяет), но зависит от задачи, показалось что с файлами будет нагляднее и подальше от привязок к хранилищам.