Files
hcs/Hcs.Broker/Api/Request/GostSigningMessageInspector.cs
2025-09-28 16:43:36 +09:00

176 lines
5.5 KiB
C#

using CryptoPro.Security.Cryptography;
using CryptoPro.Security.Cryptography.X509Certificates;
using CryptoPro.Security.Cryptography.Xml;
using Hcs.Broker.Internal;
using System.Security;
using System.Security.Cryptography;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Dispatcher;
using System.Text;
using System.Xml;
namespace Hcs.Broker.Api.Request
{
/// <summary>
/// Фильтр сообщений добавляет в XML-сообщение электронную подпись XADES/GOST
/// </summary>
internal class GostSigningMessageInspector(Client client) : IClientMessageInspector
{
private readonly Client client = client;
public object BeforeSendRequest(ref Message request, IClientChannel channel)
{
try
{
var filterHeader = "[Message Inspector]";
PurgeDebuggerHeaders(ref request);
var messageBody = GetMessageBodyString(ref request, Encoding.UTF8);
if (!messageBody.Contains(Constants.SIGNED_XML_ELEMENT_ID))
{
client.TryCaptureMessage(true, messageBody);
}
else
{
client.TryLog($"{filterHeader} Signing message with key [{client.Certificate}]...");
var stopwatch = System.Diagnostics.Stopwatch.StartNew();
var xmlDocument = new XmlDocument();
xmlDocument.LoadXml(messageBody);
var provider = client.Certificate.PrivateKey as Gost3410_2012_256CryptoServiceProvider;
provider.SetContainerPassword(client.CertificatePin);
var signedXml = SignXmlFileXades(
xmlDocument,
client.Certificate,
provider,
CpSignedXml.XmlDsigGost3411_2012_256Url,
false);
stopwatch.Stop();
client.TryLog($"{filterHeader} Message signed in {stopwatch.ElapsedMilliseconds} ms");
client.TryCaptureMessage(true, signedXml);
request = Message.CreateMessage(
XmlReaderFromString(signedXml), int.MaxValue, request.Version);
}
}
catch (System.Exception ex)
{
throw new System.Exception($"Exception occured in {GetType().Name}", ex);
}
return null;
}
private void PurgeDebuggerHeaders(ref Message request)
{
var limit = request.Headers.Count;
for (var i = 0; i < limit; ++i)
{
if (request.Headers[i].Name.Equals("VsDebuggerCausalityData"))
{
request.Headers.RemoveAt(i);
break;
}
}
}
private string GetMessageBodyString(ref Message request, Encoding encoding)
{
var mb = request.CreateBufferedCopy(int.MaxValue);
request = mb.CreateMessage();
var s = new MemoryStream();
var xw = XmlWriter.Create(s);
mb.CreateMessage().WriteMessage(xw);
xw.Flush();
s.Position = 0;
var bXML = new byte[s.Length];
s.Read(bXML, 0, (int)s.Length);
if (bXML[0] != (byte)'<')
{
return encoding.GetString(bXML, 3, bXML.Length - 3);
}
else
{
return encoding.GetString(bXML, 0, bXML.Length);
}
}
private string SignXmlFileXades(
XmlDocument doc,
CpX509Certificate certificate,
AsymmetricAlgorithm key,
string digestMethod,
bool useDsPrefix)
{
var keyInfo = new CpKeyInfo();
keyInfo.AddClause(new CpKeyInfoX509Data(certificate));
var signedXml = new CpXadesSignedXml(doc)
{
SigningKey = key,
KeyInfo = keyInfo
};
var reference = new CpReference()
{
Uri = "",
DigestMethod = digestMethod
};
var signTransform = new CpXmlDsigEnvelopedSignatureTransform();
reference.AddTransform(signTransform);
var c14nTransform = new CpXmlDsigC14NTransform();
reference.AddTransform(c14nTransform);
signedXml.AddReference(reference);
if (useDsPrefix)
{
signedXml.SignatureNodePrefix = "ds";
}
signedXml.ComputeXadesSignature();
var xmlDigitalSignature = signedXml.GetXml();
doc.DocumentElement.AppendChild(doc.ImportNode(xmlDigitalSignature, true));
if (doc.FirstChild is XmlDeclaration)
{
doc.RemoveChild(doc.FirstChild);
}
return doc.OuterXml;
}
private XmlReader XmlReaderFromString(string xml)
{
var stream = new MemoryStream();
var writer = new StreamWriter(stream);
writer.Write(xml);
writer.Flush();
stream.Position = 0;
return XmlReader.Create(stream);
}
public void AfterReceiveReply(ref Message reply, object correlationState)
{
client.TryCaptureMessage(false, reply.ToString());
}
}
}