Files
hcs/Hcs.Client/GostXades/XadesBesSignedXml.cs

175 lines
7.7 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using GostXades.Abstractions;
using GostXades.Helpers;
using Microsoft.Xades;
using System;
using System.Linq;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Xml;
namespace GostXades
{
public class XadesBesSignedXml : XadesSignedXml
{
public ICertificateMatcher CertificateMatcher { get; set; }
public ICryptoProvider CryptoProvider { get; set; }
/// <param name="document">Документ, который необходимо подписать с помощью XAdES-BES</param>
public XadesBesSignedXml(XmlDocument document) : base(document) { }
/// <param name="document">Документ, подписанный с помощью XAdES-BES</param>
/// <param name="elementId">Id подписанного элемента</param>
public XadesBesSignedXml(XmlDocument document, string elementId) : base(document, elementId)
{
var elementToVerify = FindElement(elementId, document);
var signatureNodeList = FindSignatureNodes(elementToVerify);
if (!signatureNodeList.Any())
{
throw new InvalidOperationException($"Элемент с Id {elementId} не содержит подписи");
}
if (signatureNodeList.Length > 1)
{
throw new InvalidOperationException($"Элемент с {elementId} подписан более одного раза");
}
var signatureNode = (XmlElement)signatureNodeList[0];
var standart = GetSignatureStandart(signatureNode);
if (standart != KnownSignatureStandard.Xades)
{
throw new InvalidOperationException($"Элемент с Id {elementId} подписан не по стандарту XADES-BES");
}
LoadXml(signatureNode);
}
/// <summary>
/// Валидация XAdES-BES подписи
/// </summary>
public void Validate()
{
X509Certificate2 matchedCert;
try
{
matchedCert = CertificateMatcher.GetSignatureCertificate(this);
}
catch (InvalidOperationException ex)
{
throw new XadesBesValidationException(ex.Message, ex);
}
if (matchedCert == null)
{
throw new XadesBesValidationException("XML подпись неверна");
}
ValidateCertificate(matchedCert);
ValidateAdditionalProperties();
}
/// <summary>
/// Подписывает XML-документ c помощью XAdES-BES
/// </summary>
/// <param name="certificate">Сертификат, с помощью которого производится подпись</param>
/// <param name="privateKeyPassword">Пароль от контейнера закрытого ключа используемого сертификата</param>
public void ComputeSignature(X509Certificate2 certificate, string privateKeyPassword)
{
var signatureId = $"xmldsig-{Guid.NewGuid().ToString().ToLower()}";
SigningKey = CryptoProvider.GetAsymmetricAlgorithm(certificate, privateKeyPassword);
Signature.Id = signatureId;
SignatureValueId = $"{signatureId}-sigvalue";
var reference = CryptoProvider.GetReference(SignedElementId, signatureId);
AddReference(reference);
SignedInfo.CanonicalizationMethod = XmlDsigCanonicalizationUrl;
SignedInfo.SignatureMethod = CryptoProvider.SignatureMethod;
var xadesInfo = new XadesInfo(certificate);
KeyInfo = KeyInfoHelper.Create(certificate);
var xadesObject = CryptoProvider.GetXadesObject(xadesInfo, signatureId);
AddXadesObject(xadesObject);
ComputeSignature();
HashAlgorithm hashAlgorithm;
GetSignedInfoHash(out hashAlgorithm);
var formatter = CryptoProvider.GetSignatureFormatter(certificate);
var signedHash = formatter.CreateSignature(hashAlgorithm.Hash);
Signature.SignatureValue = signedHash;
}
public XmlElement FindElement(string elementId, XmlDocument xmlDocument)
{
var elementToVerify = GetIdElement(xmlDocument, elementId);
if (elementToVerify == null)
{
throw new InvalidOperationException($"Элемент с Id {elementId} не найден");
}
return elementToVerify;
}
private KnownSignatureStandard GetSignatureStandart(XmlElement signatureElement)
{
return GetXadesObjectElement(signatureElement) == null ? KnownSignatureStandard.XmlDsig : KnownSignatureStandard.Xades;
}
private static XmlNode[] FindSignatureNodes(XmlElement elementToVerify)
{
var items = elementToVerify.ChildNodes
.OfType<XmlNode>()
.Where(x => string.Equals(x.LocalName, SignatureTagName, StringComparison.InvariantCulture)
&& string.Equals(x.NamespaceURI, XmlDsigNamespaceUrl, StringComparison.InvariantCultureIgnoreCase))
.ToArray();
return items;
}
private void ValidateAdditionalProperties()
{
var signedSignatureProperties = SignedSignatureProperties;
var signedDataObjectProperties = SignedDataObjectProperties;
// XAdES 1.4.2 clause G.2.2.6 Checking SignaturePolicyIdentifier
ThrowIfTrue(signedSignatureProperties?.SignaturePolicyIdentifier != null, "SignaturePolicyIdentifier");
// XAdES 1.4.2 clause G.2.2.8 Checking DataObjectFormat
ThrowIfTrue(signedDataObjectProperties?.DataObjectFormatCollection.IsNotEmpty(), "DataObjectFormat");
// XAdES 1.4.2 clause G.2.2.9 Checking CommitmentTypeIndication
ThrowIfTrue(signedDataObjectProperties?.CommitmentTypeIndicationCollection.IsNotEmpty(), "CommitmentTypeIndication");
// XAdES 1.4.2 clause G.2.2.11 Checking SignerRole
var signerRole = signedSignatureProperties?.SignerRole;
ThrowIfTrue(signerRole?.ClaimedRoles != null || signerRole?.CertifiedRoles != null, "SignerRole");
// XAdES 1.4.2 clause G.2.2.16.1.1 Checking AllDataObjectsTimeStamp
ThrowIfTrue(signedDataObjectProperties?.AllDataObjectsTimeStampCollection.IsNotEmpty(), "AllDataObjectsTimeStamp");
// XAdES 1.4.2 clause G.2.2.16.1.2 Checking IndividualDataObjectsTimeStamp
ThrowIfTrue(signedDataObjectProperties?.IndividualDataObjectsTimeStampCollection.IsNotEmpty(), "IndividualDataObjectsTimeStamp");
}
private static void ValidateCertificate(X509Certificate2 certificate)
{
if (DateTime.Now > certificate.NotAfter || DateTime.Now < certificate.NotBefore)
{
throw new XadesBesValidationException("Срок действия сертификата истек или еще не наступил");
}
var isCertValid = certificate.Verify();
if (!isCertValid)
{
throw new XadesBesValidationException("Неверный сертификат");
}
}
private static void ThrowIfTrue(bool? condition, string tagName)
{
if (condition == true)
{
throw new XadesBesValidationException($"Свойство xades:{tagName} не поддерживается");
}
}
private const string SignatureTagName = "Signature";
}
}