Форум программистов, компьютерный форум, киберфорум
Наши страницы
C#: Веб-сервисы и WCF
Войти
Регистрация
Восстановить пароль
 
 
Рейтинг 4.93/14: Рейтинг темы: голосов - 14, средняя оценка - 4.93
Koyen
0 / 0 / 0
Регистрация: 08.12.2015
Сообщений: 26
1

SOAP сервис ФСС. Подписывание запросов сертификатом

09.10.2017, 15:10. Просмотров 2874. Ответов 35
Метки нет (Все метки)

Доброго дня всем.
Есть задача наладить интеграцию с сервисом ФСС по Электронным больничным листам.
Сам сервис находится тут https://docs-test.fss.ru/eln.html
WSDL тут https://docs-test.fss.ru/FSSWSLn/FileOperationsLnPort?WSDL
По спецификации используется стандарт безопасности OASIS Web Service Security.
Не могу разобраться как подписывать запросы сертификатом.
Если добавить стандартную ссылку на Веб-службу и работать ней, то не принимает сертификат. Или я его не туда указываю.
C#
1
2
3
            FileOperationsLnImplService service = new FileOperationsLnImplService();
            service.ClientCertificates.Add(x509Certificate2);
            FileOperationsLnUser_getNewLNNum_Out outDoc = service.getNewLNNum("ОГРН ОГРАНИЗАЦИИ");
Либо же формировать запрос вручную и подписывать самому? Если да, то как это можно сделать?

Если кто то уже интегрировался с этим сервисом, скиньте пожалуйста код хотя бы получения номера больничного, дальше я уже думаю разберусь.

Заранее благодарен.
0
Similar
Эксперт
41792 / 34177 / 6122
Регистрация: 12.04.2006
Сообщений: 57,940
09.10.2017, 15:10
Ответы с готовыми решениями:

Сервис с самозаверенным сертификатом
Добрый день, у меня возникла проблема когда я пробую запустить службу в iis6,...

Сервис с методами SOAP и REST
Написать веб-сервис, который принимает строку и возвращает кол-во слов и...

SOAP-сервис из WSDL - как добавить WS-Adressing
Есть веб-сервис (не мой, менять не могу), с помощью WSDL-файла сгенерил...

Подписывание сборок (sign with a strong name) - кто использует
В каких случаях вы используете подписывание сборок (sign with a strong name)?

SOAP сервис на PHP
Добрый день, Есть SOUP сервис написанный на PHP, не могу понять, что нужно...

35
insite2012
Модератор
Эксперт .NET
4865 / 3818 / 1096
Регистрация: 12.10.2013
Сообщений: 11,095
Записей в блоге: 2
09.10.2017, 21:59 2
Цитата Сообщение от Koyen Посмотреть сообщение
Или я его не туда указываю.
Скорее именно так.
Цитата Сообщение от Koyen Посмотреть сообщение
Если кто то уже интегрировался с этим сервисом
Абсолютно без понятия, но попробуйте такой подход. Сам сертификат должен быть рядом (в одной папке) с исполняемым файлом.
Тестовое открытие соединении проходит, так что думаю дальше проблем не возникнет.
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.ServiceModel.Security;
using System.Security.Cryptography.X509Certificates;
using FccTest.localhost;
 
namespace FccTest {
    class Program {
        static void Main(string[] args) {
            X509Certificate2 cert = new X509Certificate2(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "GUC_FSS_RF_2016.cer"));
            using (FileOperationsLnClient proxy = new FileOperationsLnClient()) {
                proxy.ClientCredentials.ClientCertificate.Certificate = cert;
                proxy.ClientCredentials.ServiceCertificate.Authentication.CertificateValidationMode = 
                    X509CertificateValidationMode.None;
                //Тестовое открытие соединения...
                proxy.Open();
 
                Console.ReadLine();
            }
        }
    }
}
0
DTri
11 / 11 / 1
Регистрация: 16.11.2011
Сообщений: 48
21.11.2017, 00:04 3
Цитата Сообщение от Koyen Посмотреть сообщение
Если кто то уже интегрировался с этим сервисом, скиньте пожалуйста код хотя бы получения номера больничного, дальше я уже думаю разберусь.
Да, выполнял интеграцию с этим монстром, если ещё интересно, могу описать эти танцы с бубном.
0
Koyen
0 / 0 / 0
Регистрация: 08.12.2015
Сообщений: 26
21.11.2017, 14:23  [ТС] 4
Цитата Сообщение от DTri Посмотреть сообщение
Да, выполнял интеграцию с этим монстром, если ещё интересно, могу описать эти танцы с бубном.
На С#? Если да, то очень интересно!

Первый вопрос: Используется стандартная ссылка на веб службу? Каким образом, в таком случае добавляется сертификат и подписывается запрос.
0
DTri
11 / 11 / 1
Регистрация: 16.11.2011
Сообщений: 48
21.11.2017, 15:22 5
Нет, используется собственный SoapInspecotor

C#
1
2
3
public class FssSoapInspecotor : IClientMessageInspector, IDispatchMessageInspector, IParameterInspector{
 
}
Добавлено через 12 секунд
Нет, используется собственный SoapInspecotor

C#
1
2
3
public class FssSoapInspecotor : IClientMessageInspector, IDispatchMessageInspector, IParameterInspector{
 
}
0
Koyen
0 / 0 / 0
Регистрация: 08.12.2015
Сообщений: 26
21.11.2017, 15:30  [ТС] 6
Цитата Сообщение от DTri Посмотреть сообщение
Нет, используется собственный SoapInspecotor
А можно еще немного про него подробнее?
0
DTri
11 / 11 / 1
Регистрация: 16.11.2011
Сообщений: 48
21.11.2017, 15:37 7
Я могу привести код своего инспектора, но у меня структура взаимодействия с ФСС несколько отличается, у меня не медицинская организация взаимодействует с ФСС, а сервис региональной системы принимает подписанный soap шифрует его и отправляет в ФСС, а полученный ответ передаётся в медицинскую организацию на расшифровку, десериализует и возвращает ответ. Это сделано по причине того, что у врача не должно быть интернета на рабочем месте.

Всё сложно, так что лучше задавайте вопросы, а я постараюсь выдёргивать кусочки кода из проектов.
0
Koyen
0 / 0 / 0
Регистрация: 08.12.2015
Сообщений: 26
21.11.2017, 16:33  [ТС] 8
Цитата Сообщение от DTri Посмотреть сообщение
Я могу привести код своего инспектора, но у меня структура взаимодействия с ФСС несколько отличается, у меня не медицинская организация взаимодействует с ФСС, а сервис региональной системы принимает подписанный soap шифрует его и отправляет в ФСС, а полученный ответ передаётся в медицинскую организацию на расшифровку, десериализует и возвращает ответ. Это сделано по причине того, что у врача не должно быть интернета на рабочем месте.
Всё сложно, так что лучше задавайте вопросы, а я постараюсь выдёргивать кусочки кода из проектов.
На стороне мед.организации формирует soap тоже ваш клиент?
Можно мне пример формирования запроса на получение нового номера больничного на стороне мед.организации, ну и механизм который его шифрует и отправляет в ФСС.
0
DTri
11 / 11 / 1
Регистрация: 16.11.2011
Сообщений: 48
21.11.2017, 17:58 9
Вот повырезал из всех проектов, может что-то упустил

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
public const string xmlns_soapenv = "http://schemas.xmlsoap.org/soap/envelope/";
public const string xmlns_xsd = "http://www.w3.org/2001/XMLSchema";
public const string xmlns_xsi = "http://www.w3.org/2001/XMLSchema-instance";
public const string xmlns_wsse = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd";
public const string xmlns_wsu = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd";
public const string xmlns_ds = "http://www.w3.org/2000/09/xmldsig#";
public const string xmlns_eln = "http://ru/ibs/fss/ln/ws/FileOperationsLn.wsdl";
public const string xmlns_xenc = "http://www.w3.org/2001/04/xmlenc#";
public const string xmlns_sch = "http://gost34.ibs.ru/WrapperService/Schema";
 
 
public string GetSickListID(){
    XmlDocument doc = new XmlDocument();
    XmlNamespaceManager ns = new XmlNamespaceManager(doc.NameTable);
    ns.AddNamespace("soapenv", Vars.xmlns_soapenv);
    ns.AddNamespace("ds", Vars.xmlns_ds);
    ns.AddNamespace("wsse", Vars.xmlns_wsse);
    ns.AddNamespace("wsu", Vars.xmlns_wsu);
    ns.AddNamespace("xsd", Vars.xmlns_xsd);
    ns.AddNamespace("xsi", Vars.xmlns_xsi);
    doc.LoadXml(Encoding.UTF8.GetString(Convert.FromBase64String(Vars.TemplateSoapMessageForSickListNewNumber)));
    XmlNodeList ogrnNode = doc.GetElementsByTagName("ogrn", "http://ru/ibs/fss/ln/ws/FileOperationsLn.wsdl");
    if (ogrnNode != null && ogrnNode.Count == 1)
    {
        ogrnNode[0].InnerText = Vars.OGRN;
    }
    XmlNodeList bodyNode = doc.GetElementsByTagName("Body", Vars.xmlns_soapenv);
    if (bodyNode != null && bodyNode.Count == 1)
    {
        XmlElement body = bodyNode[0] as XmlElement;
        body.SetAttribute("xmlns:wsu", Vars.xmlns_wsu);
        body.SetAttribute("Id", Vars.xmlns_wsu, $"OGRN_{Vars.OGRN}");
        CryptoTools.GenerateSecurity(doc, Vars.CertificateMO, "OGRN", Vars.OGRN);
    }
    else
    {
        throw new Exception($"Не удалось найти подписываемый сегмент {$"OGRN_{Vars.OGRN}"} в сообщении.");
    }
 
    using (Вызов сервиса региональной системы)
    {
        sickListID = отправка soap сообщения региональному сервису;
 
    }
    return sickListID;
}
 
// Собственная реализация поиска подписываемого узла
public class FSSSignedXml : SignedXml
{
 
    public FSSSignedXml(XmlDocument document) : base(document)
    {
    }
 
    public override XmlElement GetIdElement(XmlDocument document, string idValue)
    {
        XmlNamespaceManager nsmgr = new XmlNamespaceManager(document.NameTable);
        nsmgr.AddNamespace("wsu", Vars.xmlns_wsu);
        var el = document.SelectSingleNode($"//*[@wsu:Id=\"{idValue}\"]", nsmgr);
        return el as XmlElement;
    }
}
 
 
// CryptoTools
public static XmlElement GenerateSecurity(XmlDocument document, X509Certificate2 Certificate, string Prefix, string Id, string Iteration = "")
{
    XmlNodeList nodeList = null;
    XmlElement elemSecurity = null;
    FSSSignedXml signer = new FSSSignedXml(document);
    signer.SigningKey = Certificate.PrivateKey;
 
    KeyInfo keyInfo = new KeyInfo();
    XmlElement keySecurityTokenReference = document.CreateElement("wsse", "SecurityTokenReference", Vars.xmlns_wsse);
    XmlElement keyReference = document.CreateElement("wsse", "Reference", Vars.xmlns_wsse);
    keyReference.SetAttribute("URI", $"#http://eln.fss.ru/actor/mo/{Vars.OGRN}");
    keySecurityTokenReference.AppendChild(keyReference);
    var keyInfoData = new KeyInfoNode(keySecurityTokenReference);
    keyInfo.AddClause(keyInfoData);
    signer.KeyInfo = keyInfo;
 
    // Создаем ссылку на подписываемый узел XML. В данном примере и в методических рекомендациях СМЭВ подписываемый узел soapenv:Body помечен идентификатором "body".
    Reference reference = new Reference($"#{Prefix}_{Id}{(String.IsNullOrEmpty(Iteration) ? "" : $"_{Iteration}")}");
    // Задаём алгоритм хэширования подписываемого узла - ГОСТ Р 34.11-94. Необходимо использовать устаревший идентификатор данного алгоритма, т.к. именно такой идентификатор используется в СМЭВ.
    reference.DigestMethod = "http://www.w3.org/2001/04/xmldsig-more#gostr3411";
    // Добавляем преобразование для приведения подписываемого узла к каноническому виду  по алгоритму http://www.w3.org/2001/10/xml-exc-c14n# в соответствии с методическими рекомендациями СМЭВ.
    reference.AddTransform(new XmlDsigExcC14NTransform());
    // Добавляем ссылку на подписываемый узел.
    signer.AddReference(reference);
    signer.SignedInfo.CanonicalizationMethod = SignedXml.XmlDsigExcC14NTransformUrl;
#pragma warning disable CS0612 // Type or member is obsolete
    signer.SignedInfo.SignatureMethod = CPSignedXml.XmlDsigGost3410UrlObsolete;
#pragma warning restore CS0612 // Type or member is obsolete
                      // Вычисляем подпись.
    signer.ComputeSignature();
    // Получаем представление подписи в виде XML.
    XmlElement xmlDigitalSignature = signer.GetXml();
    // Находим Header
    nodeList = document.GetElementsByTagName("Header", Vars.xmlns_soapenv);
    if (nodeList != null && nodeList.Count == 1)
    {
        // Добавление тега Security
        elemSecurity = document.CreateElement("wsse", "Security", Vars.xmlns_wsse);
        elemSecurity.SetAttribute("actor", Vars.xmlns_soapenv, $"http://eln.fss.ru/actor/mo/{Vars.OGRN}");
        //elemSecurity.SetAttribute("xmlns:xsd", xmlns_xsd);
        elemSecurity.SetAttribute("xmlns:wsu", Vars.xmlns_wsu);
        elemSecurity.SetAttribute("xmlns:ds", Vars.xmlns_ds);
        nodeList[0].AppendChild(elemSecurity);
    }
    XmlNode nodeSecurity = (XmlNode)elemSecurity;
    // Находим Security
 
    //// Добавление тега Signature
    //XmlElement elemSignature = document.CreateElement("ds", "Signature", xmlns_ds);
    nodeSecurity.AppendChild(xmlDigitalSignature);
    XmlElement elemBinarySecurityToken = document.CreateElement("wsse", "BinarySecurityToken", Vars.xmlns_wsse);
    elemBinarySecurityToken.SetAttribute("EncodingType", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary");
    elemBinarySecurityToken.SetAttribute("ValueType", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3");
    elemBinarySecurityToken.SetAttribute("Id", Vars.xmlns_wsu, $"http://eln.fss.ru/actor/mo/{Vars.OGRN}");
    elemBinarySecurityToken.InnerText = Convert.ToBase64String(Certificate.Export(X509ContentType.Cert));
    nodeSecurity.AppendChild(elemBinarySecurityToken);
 
    return elemSecurity;
}
 
// Реализация подписи в региональной системе 
public class FssSoapInspecotor : IClientMessageInspector, IDispatchMessageInspector, IParameterInspector
{
....
    public object BeforeSendRequest(ref Message request, IClientChannel channel)
    {
        try
        {
            // _soapRequest у меня приходит от сервиса мед.организации (sickListID = отправка soap сообщения региональному сервису;)
            XmlDocument doc = new XmlDocument() { PreserveWhitespace = true };
            doc.LoadXml(_soapRequest);
            // _hospital.Certificate - открытый ключ сертификата медицинской организации (МО) с помощью этого сертификата ФСС зашифрует и отправит ответ на действие
            // Vars.CertificateFss - сертификат ФСС, с его помощью мы шифрует soap сообщение
            XmlDocument encryptionXML = CryptoTools.EncryptionXML(doc, Vars.CertificateFss, _hospital.Certificate);
            request = CreateMessageFromString(encryptionXML.OuterXml, request.Version);
            return null;
        }
        catch (Exception ex)
        {
            AddLog($"Ошибка при шифровании soap запроса: {ex.Message}", EventLogEntryType.Error, ServiceEventCategory.Initialization);
            throw ex;
        }
    }
    ....
}
 
Message CreateMessageFromString(String xml, MessageVersion ver)
{
    return Message.CreateMessage(XmlReaderFromString(xml), int.MaxValue, ver);
}
 
 
// CryptoTools
public static XmlDocument EncryptionXML(XmlDocument document, X509Certificate2 CertificateEncryption, X509Certificate2 CertificateOpen)
{
    XmlNode elementBody = document.GetElementsByTagName("Envelope", xmlns_soapenv)[0];
    // Создаем новый XML документ.
    XmlDocument doc = new XmlDocument();
    XmlNamespaceManager ns = new XmlNamespaceManager(doc.NameTable);
    ns.AddNamespace("soapenv", xmlns_soapenv);
    ns.AddNamespace("xenc", xmlns_xenc);
    ns.AddNamespace("ds", xmlns_ds);
    ns.AddNamespace("sch", xmlns_sch);
    ns.AddNamespace("wsse", xmlns_wsse);
    ns.AddNamespace("wsu", xmlns_wsu);
 
    MemoryStream newRequestStream = new MemoryStream();
    XmlWriter writer = XmlWriter.Create(newRequestStream, new XmlWriterSettings { Encoding = Encoding.UTF8 });
    writer.WriteStartDocument();
    /* Envelope */
    writer.WriteStartElement("soapenv", "Envelope", xmlns_soapenv);
    /* Header */
    writer.WriteStartElement("soapenv", "Header", xmlns_soapenv);
    writer.WriteEndElement(); // Header
    writer.WriteStartElement("soapenv", "Body", xmlns_soapenv); // Body
    /************************************************************** EncryptedData **************************************************************************/
    writer.WriteRaw(elementBody.OuterXml);
    /*******************************************************************************************************************************************************/
    writer.WriteEndElement(); // Body
    writer.WriteEndElement(); // Envelope
    writer.WriteEndDocument();
    writer.Flush();
 
    string xmlText = Encoding.GetEncoding("UTF-8").GetString(newRequestStream.ToArray());
    XmlDocument xml = new XmlDocument();
    newRequestStream.Position = 0;
    xml.Load(newRequestStream);
    writer.Close();
    // Ищем заданный элемент для заширования. Envelope
    XmlElement elementToEncrypt = xml.GetElementsByTagName("Envelope", xmlns_soapenv)[1] as XmlElement;
    // Создаем объект EncryptedData и заполняем его необходимой информацией.
    EncryptedData edElement = new EncryptedData();
    edElement.Type = EncryptedXml.XmlEncElementUrl;
    // Созданный элемент помечаем EncryptedElement1
    //edElement.Id = "EncryptedElement1";
    // Заполняем алгоритм зашифрования данных.  Он будет использован при расшифровании.
    edElement.EncryptionMethod = new EncryptionMethod(CPEncryptedXml.XmlEncGost28147Url);
    // Создаем новую ссылку на ключ.
    edElement.KeyInfo = new KeyInfo();
    // Создаем случайный симметричный ключ.
    // В целях безопасности удаляем ключ из памяти после использования.
    using (Gost28147CryptoServiceProvider sessionKey = new Gost28147CryptoServiceProvider())
    {
        // Создаем объект класса EncryptedXml
        EncryptedXml eXml = new EncryptedXml();
        // Зашифроваем узел на симметричном ключе.
        byte[] encryptedElement = eXml.EncryptData(elementToEncrypt, sessionKey, false);
        // Зашифровываем сессионный ключ и добавляем эти зашифрованные данные к узлу EncryptedKey.
        EncryptedKey ek = new EncryptedKey();
        byte[] encryptedKey = CPEncryptedXml.EncryptKey(sessionKey, (Gost3410)CertificateEncryption.PublicKey.Key);
        ek.CipherData = new CipherData(encryptedKey);
        ek.EncryptionMethod = new EncryptionMethod(CPEncryptedXml.XmlEncGostKeyTransportUrl);
        KeyInfoX509Data data = new KeyInfoX509Data(CertificateOpen);
        ek.KeyInfo.AddClause(data);
        // Добавляем ссылку на зашифрованный ключ к зашифрованным данным.
        edElement.KeyInfo.AddClause(new KeyInfoEncryptedKey(ek));
        // Добавляем зашифрованные данные к объекту EncryptedData.
        edElement.CipherData.CipherValue = encryptedElement;
    }
 
    // Заменяем исходный узел на зашифрованный.
    EncryptedXml.ReplaceElement(elementToEncrypt, edElement, false);
 
    return xml;
}
Чуть не забыл, Vars.TemplateSoapMessageForSickListNewNumber это болванка сообщения для удобства хранения в конфигурационном файле я её преобразовал в base64: Convert.FromBase64String("PHM6RW52ZWxvcGUgeG1sbnM6cz0iaHR0cDovL3NjaGVtYXMueG1sc29hcC5vcmcvc29hcC9lbnZlbG9wZS8iPg0KICA8cz pIZWFkZXI+DQogICAgPEFjdGlvbiBzOm11c3RVbmRlcnN0YW5kPSIxIiB4bWxucz0iaHR0cDovL3NjaGVtYXMubWljcm9zb2Z0LmNvbS93cy8yMDA1LzA1L2 FkZHJlc3Npbmcvbm9uZSI+aHR0cDovL3J1L2licy9mc3MvbG4vd3MvRmlsZU9wZXJhdGlvbnNMbi53c2RsL2dldE5ld0xOTnVtPC9BY3Rpb24+DQogIDwvcz pIZWFkZXI+DQogIDxzOkJvZHkgeG1sbnM6eHNpPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYS1pbnN0YW5jZSIgeG1sbnM6eHNkPSJodHRwOi 8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYSI+DQogICAgPGdldE5ld0xOTnVtIHhtbG5zPSJodHRwOi8vcnUvaWJzL2Zzcy9sbi93cy9GaWxlT3BlcmF0aW 9uc0xuLndzZGwiPg0KICAgICAgPG9ncm4+PC9vZ3JuPg0KICAgIDwvZ2V0TmV3TE5OdW0+DQogIDwvczpCb2R5Pg0KPC9zOkVudmVsb3BlPg==")
Будут вопросы, поясню.
1
astraling
0 / 0 / 0
Регистрация: 06.12.2017
Сообщений: 1
06.12.2017, 22:52 10
DTri, а вы не могли бы привести пример расшифровки сообщения, пришедшего со стороны ФСС? Вроде бы разобрался и с подписью, и с шифрованием, а вот расшифровку победить не могу
Спасибо
0
DTri
11 / 11 / 1
Регистрация: 16.11.2011
Сообщений: 48
07.12.2017, 09:29 11
Передаём зашифрованное soap сообщение
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
        public static string DecryptXml(string document)
        {
            // Создаем объект XmlDocument.
            XmlDocument xmlDoc = new XmlDocument();
 
            // Загружаем XML файл в объект XmlDocument.
            xmlDoc.PreserveWhitespace = true;
            xmlDoc.LoadXml(document);
 
            // Ищем все зашифрованные данные.
            XmlNamespaceManager nsmgr = new XmlNamespaceManager(xmlDoc.NameTable);
            nsmgr.AddNamespace("enc", "http://www.w3.org/2001/04/xmlenc#");
            XmlNodeList list = xmlDoc.SelectNodes("//enc:EncryptedData", nsmgr);
 
            // Создаем объект EncryptedXml.
            EncryptedXml exml = new EncryptedXml(xmlDoc);
 
            if (list != null)
            {
                // Для всех зашифрованных данных.
                foreach (XmlNode node in list)
                {
                    XmlElement element = node as XmlElement;
                    EncryptedData encryptedData = new EncryptedData();
                    encryptedData.LoadXml(element);
 
                    // Находим подходящий ключ для расшифрования.
                    SymmetricAlgorithm decryptionKey = GetDecryptionKey(exml, encryptedData);
                    if (decryptionKey == null)
                    {
                        throw new Exception("Ключ для расшифрования сообщения не найден.");
                    }
 
                    // И на нем расшифровываем данные.
                    byte[] decryptedData = exml.DecryptData(encryptedData, decryptionKey);
                    exml.ReplaceData(element, decryptedData);
                }
            }
            return xmlDoc.OuterXml;
            //xmlDoc.Save(destXmlFileName);
        }
 
        private static SymmetricAlgorithm GetDecryptionKey(EncryptedXml exml, EncryptedData encryptedData)
        {
            IEnumerator encryptedKeyEnumerator = encryptedData.KeyInfo.GetEnumerator();
            // Проходим по всем KeyInfo
            while (encryptedKeyEnumerator.MoveNext())
            {
                // пропускам все что неизвестно.
                KeyInfoEncryptedKey current = encryptedKeyEnumerator.Current as KeyInfoEncryptedKey;
                if (current == null) continue;
                // до первого EncryptedKey
                EncryptedKey encryptedKey = current.EncryptedKey;
                if (encryptedKey == null)
                    continue;
                KeyInfo keyinfo = encryptedKey.KeyInfo;
                // Проходим по всем KeyInfo зашифрования ключа.
                IEnumerator srcKeyEnumerator = keyinfo.GetEnumerator();
                while (srcKeyEnumerator.MoveNext())
                {
                    // пропускам все что неизвестно.
                    KeyInfoX509Data keyInfoCert = srcKeyEnumerator.Current
                        as KeyInfoX509Data;
                    if (keyInfoCert == null)
                        continue;
                    AsymmetricAlgorithm alg = Vars.CertificateMO.PrivateKey; // Приватный ключ, открытый ключ которого мы отправляли при шифровании запроса
                    Gost3410 myKey = alg as Gost3410;
                    if (myKey == null)
                        continue;
                    return CPEncryptedXml.DecryptKeyClass(encryptedKey.CipherData.CipherValue, myKey, encryptedData.EncryptionMethod.KeyAlgorithm);
                }
            }
            return null;
        }
1
cutecode
0 / 0 / 0
Регистрация: 30.12.2017
Сообщений: 8
30.12.2017, 22:17 12
а есть ли пример для шифрования XML на С++?
Я готов купить.

или хотя бы готовый exe-шник. чтоб на входе "подписанный" файл+сертификат, а на выходе "зашифрованный" файл
0
alextrof94
0 / 0 / 0
Регистрация: 01.07.2018
Сообщений: 1
01.07.2018, 14:54 13
Простите форумчане, что поднимаю старый топик в топ, но...

Никак не могу заставить ФСС шифровать нашим сертификатом.
Даже файл сгенерированный проверил - он с нашим сертификатом, но ФСС присылает ответ все равно зашифрованный их сертификатом. Т.е. даже если я генерирую зашифрованные данные неправильно (подписываю не так или еще что) - ответ ФСС все равно не могу прочитать, ибо он зашифрован.
0
DTri
11 / 11 / 1
Регистрация: 16.11.2011
Сообщений: 48
16.08.2018, 17:35 14
Отправляемые в ФСС данные шифруются открытым сертификатом ФСС, а Вам, возвращается ответ зашифрованный открытым сертификатом МО. Его же Вы и передаёте в зашифрованном SOAP сообщении:
XML
1
2
3
4
5
6
7
8
9
10
11
<soapenv:Body>
    <EncryptedData Type="http://www.w3.org/2001/04/xmlenc#Element" xmlns="http://www.w3.org/2001/04/xmlenc#">
        <EncryptionMethod Algorithm="urn:ietf:params:xml:ns:cpxmlsec:algorithms:gost28147" />
        <KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
            <EncryptedKey xmlns="http://www.w3.org/2001/04/xmlenc#">
                <EncryptionMethod Algorithm="urn:ietf:params:xml:ns:cpxmlsec:algorithms:transport-gost2001" />
                <KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
                    <X509Data>
                        <X509Certificate>Открфтфй ключ сертификата МО</X509Certificate>
                    </X509Data>
                </KeyInfo>
этим ключём сервис ФСС и шифрует ответное сообщение, расшифровать которое Вы можете только закрытым ключём сертификата МО
1
FcknMars
0 / 0 / 0
Регистрация: 10.03.2012
Сообщений: 22
25.10.2018, 16:23 15
Не расскажите что у вас здесь поподробней:
C#
1
Vars.TemplateSoapMessageForSickListNewNumber это болванка сообщения для удобства хранения в конфигурационном файле я её преобразовал в base64: Convert.FromBase64String("PHM6RW52ZWxvcGUgeG1sbnM6cz0iaHR0cDovL3NjaGVtYXMueG1sc29hcC5vcmcvc29hcC9lbnZlbG9wZS8iPg0KICA8cz pIZWFkZXI+DQogICAgPEFjdGlvbiBzOm11c3RVbmRlcnN0YW5kPSIxIiB4bWxucz0iaHR0cDovL3NjaGVtYXMubWljcm9zb2Z0LmNvbS93cy8yMDA1LzA1L2 FkZHJlc3Npbmcvbm9uZSI+aHR0cDovL3J1L2licy9mc3MvbG4vd3MvRmlsZU9wZXJhdGlvbnNMbi53c2RsL2dldE5ld0xOTnVtPC9BY3Rpb24+DQogIDwvcz pIZWFkZXI+DQogIDxzOkJvZHkgeG1sbnM6eHNpPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYS1pbnN0YW5jZSIgeG1sbnM6eHNkPSJodHRwOi 8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYSI+DQogICAgPGdldE5ld0xOTnVtIHhtbG5zPSJodHRwOi8vcnUvaWJzL2Zzcy9sbi93cy9GaWxlT3BlcmF0aW 9uc0xuLndzZGwiPg0KICAgICAgPG9ncm4+PC9vZ3JuPg0KICAgIDwvZ2V0TmV3TE5OdW0+DQogIDwvczpCb2R5Pg0KPC9zOkVudmVsb3BlPg==")

И второй вопрос:
Насколько я понял, чтобы получить элн с портала ФСС, нужно сформировать запрос такого типа:

XML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ds="http://www.w3.org/2000/09/xmldsig#" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" flagCert="spare">
<soapenv:Header>
<wsse:Security soapenv:actor="http://eln.fss.ru/actor/insurer/######">
<wsse:BinarySecurityToken EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary" ValueType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3" wsu:Id="http://eln.fss.ru/actor/insurer/######">
TOKEN
</wsse:BinarySecurityToken>
<ds:Signature>
<SignedInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
<CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
<SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#gostr34102001-gostr3411"/>
<Reference URI="#REGNO_######">
<Transforms>
<Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
</Transforms>
<DigestMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#gostr3411"/>
<DigestValue>#############</ds:SignatureValue>
<ds:KeyInfo><wsse:SecurityTokenReference>
<wsse:Reference URI="#http://eln.fss.ru/actor/insurer/######" ValueType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3"/>
</wsse:SecurityTokenReference></ds:KeyInfo></ds:Signature></wsse:Security>
</soapenv:Header>
<soapenv:Body wsu:Id="REGNO_######">
<getPrivateLNData xmlns="http://ru/ibs/fss/ln/ws/FileOperationsLn.wsdl">
<regNum>######</regNum>
<lnCode>######</lnCode>
<snils>######</snils>
</getPrivateLNData>
</soapenv:Body>
</soapenv:Envelope>
У вас это реализовано? Спасибо за ответ!
0
DTri
11 / 11 / 1
Регистрация: 16.11.2011
Сообщений: 48
29.10.2018, 16:57 16
Цитата Сообщение от FcknMars Посмотреть сообщение
Не расскажите что у вас здесь поподробней:
Это болванка SOAP сообщения закодированная в base64, для удобства работы, не более. загружаю в объект XmlDocument данный xml и далее работаю с ним
C#
1
doc.LoadXml(Encoding.UTF8.GetString(Convert.FromBase64String(Vars.TemplateSoapMessageForSickListDisable)));
Конечно, получение ЭЛН по номеру и СНИЛСу реализован.
2
DatCat
0 / 0 / 0
Регистрация: 30.10.2018
Сообщений: 2
30.10.2018, 13:26 17
Спасибо за код шифрования и дешифрования, всё работает отлично. Но возникают проблемы с сертификатами ГОСТ 2012(как ФСС, так и МО). Например:
1) Сертификат ФСС 2001, МО - 2012, шифруется нормально, ФСС запрос принимает и дает ответ, но не получается расшифровать, ошибка в строках
C#
1
2
3
4
Gost3410_2012_256CryptoServiceProvider myKey = alg as Gost3410_2012_256CryptoServiceProvider;
if (myKey == null)
  continue;
return CPEncryptedXml.DecryptKeyClass(encryptedKey.CipherData.CipherValue, myKey, encryptedData.EncryptionMethod.KeyAlgorithm);
Код
System.ArgumentException: spki.algorithm.parameters
   в CryptoPro.Sharpei.cpAsnUtils.UnpackPublicKeyInfo2012(SubjectPublicKeyInfo s
pki)
   в CryptoPro.Sharpei.cpAsnUtils.DecodeGostKeyTransport(Byte[] data, GostKeyTra
nsportObject transport)
   в CryptoPro.Sharpei.GostKeyTransport.Decode(Byte[] data)
   в CryptoPro.Sharpei.Gost2012_256KeyExchangeDeformatter.DecryptKeyExchangeData
(Byte[] data)
2) Сертификат ФСС 2012, МО - 2012, шифруется нормально, ФСС запрос не принимает.
Шифрую так:
C#
1
byte[] encryptedKey = CPEncryptedXml.EncryptKey(sessionKey, (Gost3410_2012_256CryptoServiceProvider)CertificateEncryption.PublicKey.Key);
Что делать с этим пока не понятно.
0
DTri
11 / 11 / 1
Регистрация: 16.11.2011
Сообщений: 48
30.10.2018, 13:55 18
Пока не занимался ни подписанием, ни шифрованием по ГОСТ 2012, поэтому подсказать не могу.
0
FcknMars
0 / 0 / 0
Регистрация: 10.03.2012
Сообщений: 22
30.10.2018, 15:25 19
Уже разобрался с болванкой, шаблон сформировал)
Не могли бы вы поделиться получением ЭЛН? Пишем на .net, застряли на подписании каркаса сообщения, никак не хочет принимать параметры для SignedXml, вываливается на ComputeSignature()..
0
DTri
11 / 11 / 1
Регистрация: 16.11.2011
Сообщений: 48
30.10.2018, 15:58 20
Что конкретно у Вас не принимает, там всё аналогично другим запросам

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
Vars.TemplateSoapMessageForGetSickList = "PFM6RW52ZWxvcGUgeG1sbnM6Uz0iaHR0cDovL3NjaGVtYXMueG1sc29hcC5vcmcvc29hcC9lbnZlbG9wZS8iIHhtbG5zOmRzPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjIiB4bWxuczp3c3NlPSJodHRwOi8vZG9jcy5vYXNpcy1vcGVuLm9yZy93c3MvMjAwNC8wMS9vYXNpcy0yMDA0MDEtd3NzLXdzc2VjdXJpdHktc2VjZXh0LTEuMC54c2QiIHhtbG5zOndzdT0iaHR0cDovL2RvY3Mub2FzaXMtb3Blbi5vcmcvd3NzLzIwMDQvMDEvb2FzaXMtMjAwNDAxLXdzcy13c3NlY3VyaXR5LXV0aWxpdHktMS4wLnhzZCI+PFM6SGVhZGVyPjwvUzpIZWFkZXI+PFM6Qm9keT48bnMxOmdldExORGF0YSB4bWxuczpuczE9Imh0dHA6Ly9ydS9pYnMvZnNzL2xuL3dzL0ZpbGVPcGVyYXRpb25zTG4ud3NkbCI+PG5zMTpvZ3JuLz48bnMxOmxuQ29kZS8+PG5zMTpzbmlscy8+PC9uczE6Z2V0TE5EYXRhPjwvUzpCb2R5PjwvUzpFbnZlbG9wZT4=";
 
public static FileOperationsLnUser_getLNData_Out GetSickList(string SickListID, string PatientSNILS)
        {
            FileOperationsLnUser_getLNData_Out sickList;
            XmlDocument doc = new XmlDocument();
            XmlNamespaceManager ns = new XmlNamespaceManager(doc.NameTable);
            ns.AddNamespace("soapenv", Vars.xmlns_soapenv);
            ns.AddNamespace("ds", Vars.xmlns_ds);
            ns.AddNamespace("wsse", Vars.xmlns_wsse);
            ns.AddNamespace("wsu", Vars.xmlns_wsu);
            ns.AddNamespace("xsd", Vars.xmlns_xsd);
            ns.AddNamespace("xsi", Vars.xmlns_xsi);
            doc.LoadXml(Encoding.UTF8.GetString(Convert.FromBase64String(Vars.TemplateSoapMessageForGetSickList)));
            XmlNodeList ogrnNode = doc.GetElementsByTagName("ogrn", Vars.xmlns_eln);
            if (ogrnNode.Count == 1)
            {
                ogrnNode[0].InnerText = Vars.OGRN;
            }
            XmlNodeList nodeList = doc.GetElementsByTagName("lnCode", Vars.xmlns_eln);
            if (nodeList.Count == 1)
            {
                nodeList[0].InnerText = SickListID;
            }
            nodeList = doc.GetElementsByTagName("snils", Vars.xmlns_eln);
            if (nodeList.Count == 1)
            {
                nodeList[0].InnerText = PatientSNILS;
            }
 
            XmlNodeList bodyNode = doc.GetElementsByTagName("Body", Vars.xmlns_soapenv);
            if (bodyNode.Count == 1)
            {
                XmlElement body = bodyNode[0] as XmlElement;
                body.SetAttribute("xmlns:wsu", Vars.xmlns_wsu);
                body.SetAttribute("Id", Vars.xmlns_wsu, $"OGRN_{Vars.OGRN}");
                try
                {
                    CryptoTools.GenerateSecurity(doc, Vars.CertificateMO, "OGRN_"+ Vars.OGRN);
                }
                catch (Exception ex)
                {
                    throw new Exception($"Не удалось подписать сообщение. {ex.Message}. {ex.InnerException?.Message ?? ""}");
                }
            }
            else
            {
                throw new Exception($"Не удалось найти подписываемый сегмент {$"OGRN_{Vars.OGRN}"} в сообщении.");
            }
 
            return sickList;
        }
CryptoTools.cs
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
/// <summary>
        /// Генерация подписей данных в xml
        /// </summary>
        /// <param name="document">Сформированный soap запрос</param>
        /// <param name="Certificate">Сертификат, с помощью которого будет выполняться подпись</param>
        /// <param name="Uri">Uri подписываемого узла</param>
        /// <returns></returns>
        public static XmlElement GenerateSecurity(XmlDocument document, X509Certificate2 Certificate, string Uri)
        {
            XmlNodeList nodeList = null;
            XmlElement elSecurity = null;
            FSSSignedXml signer = new FSSSignedXml(document)
            {
                SigningKey = Certificate.PrivateKey
            };
            KeyInfo keyInfo = new KeyInfo();
            XmlElement keySecurityTokenReference = document.CreateElement("wsse", "SecurityTokenReference", Vars.xmlns_wsse);
            XmlElement keyReference = document.CreateElement("wsse", "Reference", Vars.xmlns_wsse);
            keyReference.SetAttribute("URI", $"#http://eln.fss.ru/actor/mo/{Vars.OGRN}");
            keySecurityTokenReference.AppendChild(keyReference);
            var keyInfoData = new KeyInfoNode(keySecurityTokenReference);
            keyInfo.AddClause(keyInfoData);
            signer.KeyInfo = keyInfo;
            // Создаем ссылку на подписываемый узел XML. В данном примере и в методических рекомендациях СМЭВ подписываемый узел soapenv:Body помечен идентификатором "body".
            Reference reference = new Reference($"#{Uri}");
            // Задаём алгоритм хэширования подписываемого узла - ГОСТ Р 34.11-94.
            reference.DigestMethod = "http://www.w3.org/2001/04/xmldsig-more#gostr3411";
            // Добавляем преобразование для приведения подписываемого узла к каноническому виду  по алгоритму http://www.w3.org/2001/10/xml-exc-c14n# в соответствии с методическими рекомендациями СМЭВ.
            reference.AddTransform(new XmlDsigExcC14NTransform());
            // Добавляем ссылку на подписываемый узел.
            signer.AddReference(reference);
            signer.SignedInfo.CanonicalizationMethod = SignedXml.XmlDsigExcC14NTransformUrl;
#pragma warning disable CS0612 // Type or member is obsolete
            signer.SignedInfo.SignatureMethod = CPSignedXml.XmlDsigGost3410UrlObsolete;
#pragma warning restore CS0612 // Type or member is obsolete
            // Вычисляем подпись.
            signer.ComputeSignature();
            // Получаем представление подписи в виде XML.
            XmlElement xmlDigitalSignature = signer.GetXml();
            // Находим Header
            nodeList = document.GetElementsByTagName("Header", Vars.xmlns_soapenv);
            if (nodeList.Count == 1)
            {
                // Добавление тега Security
                elSecurity = document.CreateElement("wsse", "Security", Vars.xmlns_wsse);
                elSecurity.SetAttribute("actor", Vars.xmlns_soapenv, $"http://eln.fss.ru/actor/mo/{Vars.OGRN}");
                elSecurity.SetAttribute("xmlns:wsu", Vars.xmlns_wsu);
                elSecurity.SetAttribute("xmlns:ds", Vars.xmlns_ds);
                nodeList[0].AppendChild(elSecurity);
            }
            XmlNode nodeSecurity = (XmlNode)elSecurity;
            nodeSecurity.AppendChild(xmlDigitalSignature);
            XmlElement elBinarySecurityToken = document.CreateElement("wsse", "BinarySecurityToken", Vars.xmlns_wsse);
            elBinarySecurityToken.SetAttribute("EncodingType", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary");
            elBinarySecurityToken.SetAttribute("ValueType", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3");
            elBinarySecurityToken.SetAttribute("Id", Vars.xmlns_wsu, $"http://eln.fss.ru/actor/mo/{Vars.OGRN}");
            elBinarySecurityToken.InnerText = Convert.ToBase64String(Certificate.Export(X509ContentType.Cert));
            nodeSecurity.AppendChild(elBinarySecurityToken);
 
            return elSecurity;
        }
0
30.10.2018, 15:58
MoreAnswers
Эксперт
37091 / 29110 / 5898
Регистрация: 17.06.2006
Сообщений: 43,301
30.10.2018, 15:58

SOAP запрос на ws сервис
Есть типовой документооборот, запущен веб сервис /doc_corp/ws/DmService...

Правка SOAP запросов через PHP
Возможно ли на php поймать SOAP запрос, поправить его и пробросить дальше? если...

Нужно на Spring написать сервис логирования запросов Hibernate сучностей
Добрый день, Нужно на Spring написать сервис логирования запросов Hibernate...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
20
Ответ Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2018, vBulletin Solutions, Inc.
Рейтинг@Mail.ru