XML шифрование на асимметричном ключе.
/// <summary>
/// Вспомогательный прокси метод для тестирования шифрования на асимметричном ключе
/// </summary>
/// <param name="gostKey"></param>
private void Encrypt(AsymmetricAlgorithm gostKey)
{
if (gostKey is Gost3410 gost3410)
{
Console.WriteLine("Секретный ключ получен.");
// и получаем открытый ключ.
Gost3410Parameters publicKey = gost3410.ExportParameters();
Console.WriteLine("На стороне отправителя...");
// Полученный открытый ключ передаем отправителю сообщения.
using (Gost3410CryptoServiceProvider pubKey = new Gost3410CryptoServiceProvider())
{
pubKey.ImportParameters(publicKey);
Console.WriteLine("Открытый ключ получен.");
// Создаем Xml файл для зашифрования.
CreateSomeXml("ato_encrypt_2001.xml");
Console.WriteLine("Создан новый XML файл.");
// Зашифровываем узел, заданный xpath выражением, XML документа
// ato_encrypt.xml в документ a_encrypted.xml
// Для зашифрования используется открытый ключ pubKey.
Encrypt("ato_encrypt_2001.xml", "a_encrypted_2001.xml",
"//SomeNode[@ToEncrypt='true']",
"EncryptedElement1", pubKey, "KeyAlias");
Console.WriteLine("Узел XML файла зашифрован.");
Console.WriteLine("На стороне получателя...");
Decrypt("a_encrypted_2001.xml", "a_decrypted_2001.xml", gost3410, "KeyAlias");
}
}
else if (gostKey is Gost3410_2012_256 gost3410_2012_256)
{
Console.WriteLine("Секретный ключ получен.");
// и получаем открытый ключ.
Gost3410Parameters publicKey = gost3410_2012_256.ExportParameters();
Console.WriteLine("На стороне отправителя...");
// Полученный открытый ключ передаем отправителю сообщения.
using (Gost3410_2012_256CryptoServiceProvider pubKey = new Gost3410_2012_256CryptoServiceProvider())
{
pubKey.ImportParameters(publicKey);
Console.WriteLine("Открытый ключ получен.");
// Создаем Xml файл для зашифрования.
CreateSomeXml("ato_encrypt_2012_256.xml");
Console.WriteLine("Создан новый XML файл.");
// Зашифровываем узел, заданный xpath выражением, XML документа
// ato_encrypt.xml в документ a_encrypted.xml
// Для зашифрования используется открытый ключ pubKey.
Encrypt("ato_encrypt_2012_256.xml", "a_encrypted_2012_256.xml",
"//SomeNode[@ToEncrypt='true']",
"EncryptedElement1", pubKey, "KeyAlias");
Console.WriteLine("Узел XML файла зашифрован.");
Console.WriteLine("На стороне получателя...");
Decrypt("a_encrypted_2012_256.xml", "a_decrypted_2012_256.xml", gost3410_2012_256, "KeyAlias");
}
}
else if (gostKey is Gost3410_2012_512 gost3410_2012_512)
{
Console.WriteLine("Секретный ключ получен.");
// и получаем открытый ключ.
Gost3410Parameters publicKey = gost3410_2012_512.ExportParameters();
Console.WriteLine("На стороне отправителя...");
// Полученный открытый ключ передаем отправителю сообщения.
using (Gost3410_2012_512CryptoServiceProvider pubKey = new Gost3410_2012_512CryptoServiceProvider())
{
pubKey.ImportParameters(publicKey);
Console.WriteLine("Открытый ключ получен.");
// Создаем Xml файл для зашифрования.
CreateSomeXml("ato_encrypt_2012_512.xml");
Console.WriteLine("Создан новый XML файл.");
// Зашифровываем узел, заданный xpath выражением, XML документа
// ato_encrypt.xml в документ a_encrypted.xml
// Для зашифрования используется открытый ключ pubKey.
Encrypt("ato_encrypt_2012_512.xml", "a_encrypted_2012_512.xml",
"//SomeNode[@ToEncrypt='true']",
"EncryptedElement1", pubKey, "KeyAlias");
Console.WriteLine("Узел XML файла зашифрован.");
Console.WriteLine("На стороне получателя...");
Decrypt("a_encrypted_2012_512.xml", "a_decrypted_2012_512bui.xml", gost3410_2012_512, "KeyAlias");
}
}
else
{
throw new NotSupportedException();
}
Console.WriteLine("XML документ расшифрован.");
}
Расшифрование XML на асимметричном ключе.
// Расшифрование узла XML документа на асимметричном ключе
private static void Decrypt(string srcName, string destName,
AsymmetricAlgorithm alg, string KeyName)
{
// Создаем новый объект xml документа.
XmlDocument xmlDoc = new XmlDocument();
// Пробельные символы участвуют в вычислении подписи и должны быть сохранены для совместимости с другими реализациями
xmlDoc.PreserveWhitespace = true;
// Загружаем в объект созданный XML документ.
xmlDoc.Load(srcName);
// Создаем объект CpEncryptedXml.
CpEncryptedXml exml = new CpEncryptedXml(xmlDoc);
// Добавляем отображение имен в ключи.
// Нижеследующий метод сможет расшифровать
// только те документы, для которых будет
// найдены соответсвующие ключи.
if (alg is Gost3410 gost3410)
{
exml.AddKeyNameMapping(KeyName, gost3410);
}
else if (alg is Gost3410_2012_256 gost3410_2012_256)
{
exml.AddKeyNameMapping(KeyName, gost3410_2012_256);
}
else if (alg is Gost3410_2012_512 gost3410_2012_512)
{
exml.AddKeyNameMapping(KeyName, gost3410_2012_512);
}
else
{
throw new NotSupportedException();
}
// Расшифровываем зашифрованные элементы.
exml.DecryptDocument();
// Сохраняем расшифрованный документ.
xmlDoc.Save(destName);
}
XML шифрование на сертификате
/// <summary>
/// Вспомогательный прокси метод для тестирования шифрования на сертификате
/// </summary>
private void Encrypt(CpX509Certificate2 cert)
{
// Создаем тестовый XML документ.
CreateSomeXml($"doc_to_encrypt_{cert.SubjectName}.xml");
// Создаем объект XmlDocument.
XmlDocument xmlDoc = new XmlDocument();
// Загружаем XML файл в объект XmlDocument.
xmlDoc.PreserveWhitespace = true;
xmlDoc.Load($"doc_to_encrypt_{cert.SubjectName}.xml");
// Шифруем узел SomeNode, который содержит атрибут ToEncrypt со значением true
Encrypt(xmlDoc, "//SomeNode[@ToEncrypt='true']", cert);
// Сохраняем XML документ.
xmlDoc.Save($"d_encrypted_{cert.SubjectName}.xml");
}
Расшифрование XML на сертификате
/// <summary>
/// Расшифрование на сертификате
/// </summary>
/// <param name="gostKey"></param>
static void Decrypt(string srcName, string destName, CpX509Certificate2 cert)
{
// Создаем новый объект xml документа.
XmlDocument xmlDoc = new XmlDocument();
// Пробельные символы участвуют в вычислении подписи и должны быть сохранены для совместимости с другими реализациями
xmlDoc.PreserveWhitespace = true;
// Загружаем в объект созданный XML документ.
xmlDoc.Load(srcName);
// Создаем новый объект CpEncryptedXml по XML документу.
CpEncryptedXml exml = new CpEncryptedXml(xmlDoc);
// Небольшие хаки, чтобы не устанавливать серт в хранилище
{
var ns = new XmlNamespaceManager(xmlDoc.NameTable);
ns.AddNamespace("ki", "http://www.w3.org/2000/09/xmldsig#");
ns.AddNamespace("ek", "http://www.w3.org/2001/04/xmlenc#");
var keyName = Convert.ToBase64String(cert.Export(X509ContentType.Cert));
var keyInfoNode = xmlDoc.SelectSingleNode("//ki:KeyInfo/ek:EncryptedKey/ki:KeyInfo", ns);
if (keyInfoNode == null)
{
throw new InvalidOperationException("Неверный формат зашифрованного XML-документа.");
}
if (keyInfoNode.InnerText.Equals(keyName, StringComparison.InvariantCultureIgnoreCase))
{
keyInfoNode.InnerXml = $"<KeyName>{keyName}</KeyName>";
}
exml.AddKeyNameMapping(keyName, cert.PrivateKey);
exml.Recipient = keyName;
}
// Расшифровываем зашифрованные узлы XML документа.
exml.DecryptDocument();
// Сохраняем расшифрованный документ.
xmlDoc.Save(destName);
}
XML шифрование на симметричном ключе
// Зашифрование узла XML документа на симметричном ключе
private static void Encrypt(string srcName, string destName,
string xpath, SymmetricAlgorithm Key)
{
// Создаем новый объект xml документа.
XmlDocument xmlDoc = new XmlDocument();
// Пробельные символы участвуют в вычислении подписи и должны быть сохранены для совместимости с другими реализациями.
xmlDoc.PreserveWhitespace = true;
// Загружаем в объект созданный XML документ.
xmlDoc.Load(srcName);
// Ищем заданный элемент для зашифрования.
XmlElement elementToEncrypt = xmlDoc.SelectSingleNode(xpath)
as XmlElement;
if (elementToEncrypt == null)
throw new XmlException("Узел не найден");
// Создаем объект класса EncryptedXml и используем
// его для зашифрования узла на симметричном ключе.
CpEncryptedXml eXml = new CpEncryptedXml();
byte[] encryptedElement = eXml.EncryptData(
elementToEncrypt, Key, false);
// Создаем объект EncryptedData и заполняем его
// необходимой информацией.
CpEncryptedData edElement = new CpEncryptedData();
edElement.Type = EncryptedXml.XmlEncElementUrl;
// Заполняем алгоритм зашифрования данных.
// Он будет использован при расшифровании.
edElement.EncryptionMethod = new CpEncryptionMethod(
CpEncryptedXml.XmlEncGost28147Url);
// Добавляем зашифрованные данные
// к объекту EncryptedData.
edElement.CipherData.CipherValue = encryptedElement;
// Заменяем исходный узел на зашифрованный.
CpEncryptedXml.ReplaceElement(elementToEncrypt, edElement, false);
// Сохраняем зашифрованный документ.
xmlDoc.Save(destName);
}
Расшифрование XML на симметричном ключе.
// Расшифрование узла XML документа на симметричном ключе
private static void Decrypt(string srcName, string destName, SymmetricAlgorithm Alg)
{
// Создаем новый объект xml документа.
XmlDocument xmlDoc = new XmlDocument();
// Пробельные символы участвуют в вычислении подписи и должны быть сохранены для совместимости с другими реализациями.
xmlDoc.PreserveWhitespace = true;
// Загружаем в объект созданный XML документ.
xmlDoc.Load(srcName);
// Ищем узел для расшифрования.
XmlElement encryptedElement = xmlDoc.GetElementsByTagName(
"EncryptedData")[0] as XmlElement;
if (encryptedElement == null)
throw new XmlException("Узел EncryptedData не найден");
// Создаем объект EncryptedData.
CpEncryptedData edElement = new CpEncryptedData();
// и загружаем в него зашифрованный узел
edElement.LoadXml(encryptedElement);
// Создаем объект CpEncryptedXml
CpEncryptedXml exml = new CpEncryptedXml();
// Расшифровываем элемент используя
// симметричный ключ.
byte[] rgbOutput = exml.DecryptData(edElement, Alg);
// Заменяем зашифрованный узел расшифрованным
exml.ReplaceData(encryptedElement, rgbOutput);
// Сохраняем расшифрованный документ.
xmlDoc.Save(destName);
}
Вспомогательные функции
// Зашифрование узла XML документа на асимметричном ключе
private static void Encrypt(string srcName, string destName,
string xpath, string EncryptionElementID, AsymmetricAlgorithm alg,
string KeyName)
{
// Создаем новый объект xml документа.
XmlDocument xmlDoc = new XmlDocument();
// Пробельные символы участвуют в вычислении подписи и должны быть сохранены для совместимости с другими реализациями
xmlDoc.PreserveWhitespace = true;
// Загружаем в объект созданный XML документ.
xmlDoc.Load(srcName);
// Ищем заданный элемент для зашифрования.
XmlElement elementToEncrypt = xmlDoc.SelectSingleNode(xpath)
as XmlElement;
if (elementToEncrypt == null)
throw new XmlException("Узел не найден");
// Создаем случайный симметричный ключ.
// В целях безопасности удаляем ключ из памяти после использования.
using (Gost28147CryptoServiceProvider sessionKey =
new Gost28147CryptoServiceProvider())
{
// Создаем объект класса CpEncryptedXml и используем
// его для зашифрования узла на случайной симметричном ключе.
CpEncryptedXml eXml = new CpEncryptedXml();
// Зашифровываем элемент на сессионном ключе.
byte[] encryptedElement = eXml.EncryptData(elementToEncrypt,
sessionKey, false);
// Создаем объект EncryptedData и заполняем его
// необходимой информацией.
CpEncryptedData edElement = new CpEncryptedData();
// Тип элемента зашифрованный узел
edElement.Type = CpEncryptedXml.XmlEncElementUrl;
// Созданный элемент помечаем EncryptionElementID
edElement.Id = EncryptionElementID;
// Заполняем алгоритм зашифрования данных.
// Он будет использован при расшифровании.
edElement.EncryptionMethod = new CpEncryptionMethod(
CpEncryptedXml.XmlEncGost28147Url);
// Зашифровываем сессионный ключ и добавляем эти зашифрованные данные
// к узлу EncryptedKey.
CpEncryptedKey ek = new CpEncryptedKey();
byte[] encryptedKey;
if (alg is Gost3410 gost3410)
{
encryptedKey = CpEncryptedXml.EncryptKey(sessionKey, gost3410);
}
else if (alg is Gost3410_2012_256 gost3410_2012_256)
{
encryptedKey = CpEncryptedXml.EncryptKey(sessionKey, gost3410_2012_256);
}
else if (alg is Gost3410_2012_512 gost3410_2012_512)
{
encryptedKey = CpEncryptedXml.EncryptKey(sessionKey, gost3410_2012_512);
}
else
{
throw new NotSupportedException();
}
ek.CipherData = new CpCipherData(encryptedKey);
ek.EncryptionMethod = new CpEncryptionMethod(
CpEncryptedXml.XmlEncGostKeyTransportUrl);
// Создаем элемент DataReference для KeyInfo.
// Эта необязательная операция позволяет указать
// какие данные используют данный ключ.
// XML документ может содержать несколько
// элементов EncryptedData с различными ключами.
CpDataReference dRef = new CpDataReference();
// Указываем URI EncryptedData.
// Для этого используем ранее проставленную ссылку
// EncryptionElementID
dRef.Uri = "#" + EncryptionElementID;
// Добавляем к EncryptedKey ссылку на зашифрованные
// данные.
ek.AddReference(dRef);
// Создаем новую ссылку на ключ.
edElement.KeyInfo = new CpKeyInfo();
// Добавляем ссылку на зашифрованный ключ к
// зашифрованным данным.
edElement.KeyInfo.AddClause(new CpKeyInfoEncryptedKey(ek));
// Указываем имя асимметричного ключа.
// Создаем новый элемент KeyInfoName
CpKeyInfoName kin = new CpKeyInfoName();
// Указываем имя асимметричного ключа.
kin.Value = KeyName;
// Добавляем элемент KeyInfoName к
// объекту EncryptedKey.
ek.KeyInfo.AddClause(kin);
// Добавляем зашифрованные данные
// к объекту EncryptedData.
edElement.CipherData.CipherValue = encryptedElement;
// Заменяем исходный узел на зашифрованный.
CpEncryptedXml.ReplaceElement(elementToEncrypt,
edElement, false);
// Сохраняем зашифрованный документ.
xmlDoc.Save(destName);
}
}
// Зашифрование узла в адрес абонента, заданного сертификатом
// получателя.
static void Encrypt(XmlDocument Doc, string xpath,
CpX509Certificate2 Cert)
{
// Ищем заданный элемент для заширования.
XmlElement elementToEncrypt = Doc.SelectSingleNode(xpath) as XmlElement;
if (elementToEncrypt == null)
throw new XmlException("Узел не найден");
// Создаем объект CpEncryptedXml для
// шифрования XmlElement
CpEncryptedXml eXml = new CpEncryptedXml();
// Шифруем элемент на сертификате.
CpEncryptedData edElement = eXml.Encrypt(elementToEncrypt, Cert);
// Заменяем исходный элемент XML документа зашифрованным.
CpEncryptedXml.ReplaceElement(elementToEncrypt, edElement, false);
}
// Тестовый документ для зашифрования / расшифрования.
private static string SourceDocument = "" +
"<MyXML Encrypted=\"false\">" +
" <SomeNode ToEncrypt=\"false\">" +
"Here is some public data.</SomeNode>" +
" <SomeNode ToEncrypt=\"true\">" +
"Here is some data to encrypt.</SomeNode>" +
"</MyXML>";
// Создание тестового XML документа.
static void CreateSomeXml(string FileName)
{
// Создать документ по строке
XmlDocument document = new XmlDocument();
document.LoadXml(SourceDocument);
// Сохранить шифруемый документ в файле.
using (XmlTextWriter xmltw = new XmlTextWriter(FileName,
new UTF8Encoding(false)))
{
xmltw.WriteStartDocument();
document.WriteTo(xmltw);
}
}