diff --git a/Hcs.Broker/Api/Request/GostSigningMessageInspector.cs b/Hcs.Broker/Api/Request/GostSigningMessageInspector.cs
index dc1b7ae..439a456 100644
--- a/Hcs.Broker/Api/Request/GostSigningMessageInspector.cs
+++ b/Hcs.Broker/Api/Request/GostSigningMessageInspector.cs
@@ -116,7 +116,7 @@ namespace Hcs.Broker.Api.Request
var keyInfo = new CpKeyInfo();
keyInfo.AddClause(new CpKeyInfoX509Data(certificate));
- var signedXml = new CpXadesSignedXml(doc)
+ var signedXml = new SignedXml(doc)
{
SigningKey = key,
KeyInfo = keyInfo
@@ -131,9 +131,6 @@ namespace Hcs.Broker.Api.Request
var signTransform = new CpXmlDsigEnvelopedSignatureTransform();
reference.AddTransform(signTransform);
- var c14nTransform = new CpXmlDsigC14NTransform();
- reference.AddTransform(c14nTransform);
-
signedXml.AddReference(reference);
if (useDsPrefix)
@@ -147,7 +144,7 @@ namespace Hcs.Broker.Api.Request
var element = doc.SelectNodes("//*[@Id='" + Constants.SIGNED_XML_ELEMENT_ID + "']")[0];
var xmlDigitalSignature = signedXml.GetXml();
- element.AppendChild(doc.ImportNode(xmlDigitalSignature, true));
+ element.PrependChild(doc.ImportNode(xmlDigitalSignature, true));
if (doc.FirstChild is XmlDeclaration)
{
diff --git a/Hcs.Broker/Api/Request/SignedXml.cs b/Hcs.Broker/Api/Request/SignedXml.cs
new file mode 100644
index 0000000..0dcb8e5
--- /dev/null
+++ b/Hcs.Broker/Api/Request/SignedXml.cs
@@ -0,0 +1,240 @@
+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=");
+ }
+ }
+}