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 { /// /// Фильтр сообщений добавляет в XML-сообщение электронную подпись XADES/GOST /// 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, true); 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 = "#signed-data-container", 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.XadesProperties = XadesPropertiesFlags.UseSigningCertificateV1; signedXml.ComputeXadesSignature(); var element = doc.SelectNodes("//*[@Id='" + Constants.SIGNED_XML_ELEMENT_ID + "']")[0]; var xmlDigitalSignature = signedXml.GetXml(); element.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()); } } }