Add new Hcs.Broker to communicate with ГИС ЖКХ via CryptoPro LibCore
This commit is contained in:
170
Hcs.Broker/Api/Request/GostSigningMessageInspector.cs
Normal file
170
Hcs.Broker/Api/Request/GostSigningMessageInspector.cs
Normal file
@ -0,0 +1,170 @@
|
||||
using CryptoPro.Security.Cryptography;
|
||||
using CryptoPro.Security.Cryptography.X509Certificates;
|
||||
using CryptoPro.Security.Cryptography.Xml;
|
||||
using Hcs.Broker.Internal;
|
||||
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.FriendlyName}]...");
|
||||
|
||||
var stopwatch = System.Diagnostics.Stopwatch.StartNew();
|
||||
|
||||
var xmlDocument = new XmlDocument();
|
||||
xmlDocument.LoadXml(messageBody);
|
||||
|
||||
var signedXml = SignXmlFileXades(
|
||||
xmlDocument,
|
||||
client.Certificate,
|
||||
client.Certificate.PrivateKey as Gost3410_2012_256CryptoServiceProvider,
|
||||
CpSignedXml.XmlDsigGost3411_2012_256Url);
|
||||
|
||||
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 = false)
|
||||
{
|
||||
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.ToString();
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user