Files
hcs/Hcs.ClientNet/GostXades/CertificateMatcher.cs

130 lines
5.3 KiB
C#
Raw 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.Collections.Generic;
using System.Linq;
using System.Security.Cryptography.X509Certificates;
using System.Security.Cryptography.Xml;
namespace GostXades
{
public class CertificateMatcher : ICertificateMatcher
{
public IIssuerComparer IssuerComparer { get; set; } = new IssuerComparer();
public CertificateMatcher(ICryptoProvider cryptoProvider)
{
if (cryptoProvider == null)
{
throw new ArgumentNullException(nameof(cryptoProvider));
}
_cryptoProvider = cryptoProvider;
}
public X509Certificate2 GetSignatureCertificate(XadesSignedXml signedXml)
{
var candidates = GetCandidateCertificates(signedXml).ToArray();
if (!candidates.Any())
{
throw new InvalidOperationException("Не найдено ни одного сертификата, обозначенного в xades:SigningCertificate");
}
return FindMatchedCertificate(signedXml, candidates);
}
private IEnumerable<X509Certificate2> GetCandidateCertificates(XadesSignedXml signedXml)
{
var certificates = ExtractCertificates(signedXml).ToArray();
if (certificates.Length == 0)
{
throw new InvalidOperationException("Элемент KeyInfo.X509Data.X509Certificate не заполнен или отсутствует");
}
var certInfosCollection = signedXml.SignedSignatureProperties.SigningCertificate.CertCollection;
if (certInfosCollection == null)
{
throw new InvalidOperationException("Элемент xades:SigningCertificate не заполнен или отсутствует");
}
var certInfos = certInfosCollection.OfType<Cert>().ToArray();
foreach (var certificate in certificates)
{
// TODO: Проверить комментарий
var isCertificateMatch = true; //certInfos.Any(certInfo => IsCertificateMatchCertInfo(certificate, certInfo));
if (isCertificateMatch)
{
yield return certificate;
}
}
}
private IEnumerable<X509Certificate2> ExtractCertificates(XadesSignedXml signedXml)
{
var keyInfo = signedXml.KeyInfo;
if (keyInfo == null || keyInfo.Count == 0)
{
throw new InvalidOperationException("Элемент KeyInfo не заполнен или отсутствует");
}
if (keyInfo.Count > 1)
{
throw new InvalidOperationException("Найдено более одного элемента KeyInfo");
}
var x509Data = keyInfo.OfType<KeyInfoX509Data>().FirstOrDefault();
if (x509Data == null)
{
throw new InvalidOperationException("Элемент X509Data не заполнен или отсутствует");
}
return x509Data.Certificates.OfType<X509Certificate2>();
}
private readonly ICryptoProvider _cryptoProvider;
#region Certificate match
private bool IsCertificateMatchCertInfo(X509Certificate2 certificate, Cert certInfo)
{
var isSerialMatch = IsSerialMatch(certificate, certInfo);
var issuerNameMatch = IsNameMatch(certificate, certInfo);
var isCertHashMatch = IsCertHashMatch(certificate, certInfo);
return isSerialMatch && issuerNameMatch && isCertHashMatch;
}
private bool IsSerialMatch(X509Certificate2 certificate, Cert certInfo)
{
var issuerSerial = certInfo.IssuerSerial.X509SerialNumber;
string certInfoSerialNumberHex;
try
{
certInfoSerialNumberHex = ConvertHelper.BigIntegerToHex(issuerSerial);
}
catch (FormatException)
{
certInfoSerialNumberHex = issuerSerial;
}
return certificate.SerialNumber == certInfoSerialNumberHex;
}
private bool IsCertHashMatch(X509Certificate2 certificate, Cert certInfo)
{
var certDigest = certInfo.CertDigest;
var pkHash = _cryptoProvider.GetHashAlgorithm(certDigest.DigestMethod.Algorithm);
if (pkHash == null)
{
throw new XadesBesValidationException($"Алгоритм {certDigest.DigestMethod.Algorithm} не поддерживается");
}
var hashValue = pkHash.ComputeHash(certificate.RawData);
return ArrayHelper.AreEquals(hashValue, certDigest.DigestValue);
}
private bool IsNameMatch(X509Certificate2 certificate, Cert certInfo)
{
return IssuerComparer.AreSameIssuer(certificate.Issuer, certInfo.IssuerSerial.X509IssuerName);
}
private X509Certificate2 FindMatchedCertificate(SignedXml signedXml, IEnumerable<X509Certificate2> candidates)
{
return candidates.FirstOrDefault(candidate => signedXml.CheckSignature(candidate, true));
}
#endregion
}
}