using CryptoPro.Security.Cryptography; using CryptoPro.Security.Cryptography.X509Certificates; using CryptoPro.Security.Cryptography.Xml; using System.Globalization; using System.Numerics; using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; using System.Xml; namespace Hcs.Broker.Api.Request { internal class SignedXml(XmlDocument document) : CpXadesSignedXml(document) { private const string XADES_141_NAMESPACE_URL = "http://uri.etsi.org/01903/v1.4.1#"; /// /// OID алгоритма открытого ключа/цифровой подписи ГОСТ Р 34.10-2001 /// private const string OID_CP_GOST_R3410EL = "1.2.643.2.2.19"; /// /// OID алгоритма открытого ключа/цифровой подписи ГОСТ Р 34.10-2012 256 /// private const string OID_CP_GOST_R3410_12_256 = "1.2.643.7.1.1.1.1"; /// /// OID алгоритма открытого ключа/цифровой подписи ГОСТ Р 34.10-2012 512 /// private const string OID_CP_GOST_R3410_12_512 = "1.2.643.7.1.1.1.2"; protected override void AddXadesProperties( CpX509Certificate2 signingCertificate, DateTime time, string signatureId, string objectId, string? xmlDSigPrefix, XadesPropertiesFlags xadesProperties) { if (_containingDocument == null) { throw new InvalidOperationException("XmlDocument is null"); } var useCertificateV2 = xadesProperties.HasFlag( XadesPropertiesFlags.UseSigningCertificateV2 ); var useCertificateV1 = xadesProperties.HasFlag( XadesPropertiesFlags.UseSigningCertificateV1 ); if ((useCertificateV2 && useCertificateV1) || (!useCertificateV2 && !useCertificateV1)) { throw new InvalidOperationException("Invalid flags"); } var document = _containingDocument; var parametersSignature = new CpReference { Uri = $"#{objectId}", Type = XmlDsigSignatureProperties, DigestMethod = XmlDsigGost3411_2012_256Url, }; parametersSignature.AddTransform(new CpXmlDsigExcC14NTransform()); AddReference(parametersSignature); var qualifyingPropertiesNode = document.CreateElement( XadesPrefix, "QualifyingProperties", XadesNamespaceUrl ); qualifyingPropertiesNode.SetAttribute("xmlns:xades141", XADES_141_NAMESPACE_URL); qualifyingPropertiesNode.SetAttribute("Target", $"#{signatureId}"); var signedPropertiesNode = document.CreateElement( XadesPrefix, "SignedProperties", XadesNamespaceUrl ); signedPropertiesNode.SetAttribute("Id", objectId); qualifyingPropertiesNode.AppendChild(signedPropertiesNode); var signedSignaturePropertiesNode = document.CreateElement( XadesPrefix, "SignedSignatureProperties", XadesNamespaceUrl ); signedPropertiesNode.AppendChild(signedSignaturePropertiesNode); var signingTime = document.CreateElement(XadesPrefix, "SigningTime", XadesNamespaceUrl); signingTime.InnerText = time.ToString("yyyy-MM-ddTHH:mm:ss.fffZ"); signedSignaturePropertiesNode.AppendChild(signingTime); var signingCertificateNodeName = useCertificateV2 ? "SigningCertificateV2" : "SigningCertificate"; var signingCertificateNode = document.CreateElement( XadesPrefix, signingCertificateNodeName, XadesNamespaceUrl ); signedSignaturePropertiesNode.AppendChild(signingCertificateNode); var certNode = document.CreateElement(XadesPrefix, "Cert", XadesNamespaceUrl); signingCertificateNode.AppendChild(certNode); var certDigestNode = document.CreateElement( XadesPrefix, "CertDigest", XadesNamespaceUrl ); certNode.AppendChild(certDigestNode); var algIdString = signingCertificate.GetKeyAlgorithm(); (bool isGost, string? gostDigestAlgorithm) = algIdString switch { OID_CP_GOST_R3410EL => (true, XmlDsigGost3411Url), OID_CP_GOST_R3410_12_256 => ( true, XmlDsigGost3411_2012_256Url ), OID_CP_GOST_R3410_12_512 => ( true, XmlDsigGost3411_2012_512Url ), _ => (false, null), }; var digestMethodAlgorithmAtribute = document.CreateAttribute("Algorithm"); if (isGost) { digestMethodAlgorithmAtribute.InnerText = gostDigestAlgorithm!; } else { digestMethodAlgorithmAtribute.InnerText = XmlDsigSHA1Url; } var digestMethod = document.CreateElement( xmlDSigPrefix, "DigestMethod", XmlDsigNamespaceUrl ); digestMethod.Attributes.Append(digestMethodAlgorithmAtribute); certDigestNode.AppendChild(digestMethod); var digestValue = document.CreateElement( xmlDSigPrefix, "DigestValue", XmlDsigNamespaceUrl ); if (isGost) { var hash = CpCryptoConfig.CreateFromName(gostDigestAlgorithm!) as HashAlgorithm ?? throw new System.Exception( $"Cannot find [{gostDigestAlgorithm}] alg in CpCryptoConfig!" ); using (hash) { digestValue.InnerText = Convert.ToBase64String( hash.ComputeHash(signingCertificate.RawData) ); } } else { digestValue.InnerText = Convert.ToBase64String(signingCertificate.GetCertHash()); } certDigestNode.AppendChild(digestValue); if (useCertificateV2) { var issuerSerialNode = document.CreateElement( XadesPrefix, "IssuerSerialV2", XadesNamespaceUrl ); issuerSerialNode.InnerText = Convert.ToBase64String( X509EncodeIssuerAndSerialHelper.EncodeIssuerAndSerial(signingCertificate) ); certNode.AppendChild(issuerSerialNode); } else { var issuerName = signingCertificate.IssuerName.Decode(X500DistinguishedNameFlags.DoNotUseQuotes); var x509IssuerNameNode = document.CreateElement( xmlDSigPrefix, "X509IssuerName", XmlDsigNamespaceUrl ); x509IssuerNameNode.InnerText = GetOidRepresentation(issuerName); var issuerSerialNode = document.CreateElement( XadesPrefix, "IssuerSerial", XadesNamespaceUrl ); issuerSerialNode.AppendChild(x509IssuerNameNode); var x509SerialNumber = document.CreateElement( xmlDSigPrefix, "X509SerialNumber", XmlDsigNamespaceUrl ); x509SerialNumber.InnerText = BigInteger.Parse(signingCertificate.SerialNumber, NumberStyles.HexNumber).ToString(); issuerSerialNode.AppendChild(x509SerialNumber); certNode.AppendChild(issuerSerialNode); } var dataObject = new CpDataObject(); dataObject.Data = qualifyingPropertiesNode.SelectNodes("."); AddObject(dataObject); } private string GetOidRepresentation(string issuerName) { return issuerName .Replace("\"", "\\\"") .Replace("E=", "1.2.840.113549.1.9.1=") .Replace("unstructuredName=", "1.2.840.113549.1.9.2=") .Replace("ОГРН=", "1.2.643.100.1=") .Replace("ИНН ЮЛ=", "1.2.643.100.4=") .Replace("ИНН ФЛ=", "1.2.643.3.131.1.1=") .Replace("ИНН=", "1.2.643.3.131.1.1=") .Replace("СНИЛС=", "1.2.643.100.3=") .Replace("ОГРНИП=", "1.2.643.100.5=") .Replace("S=", "ST="); } } }