Closed AlekseyDev closed 8 months ago
Здравствуйте!
Я могу ошибаться, но мне кажется, что проблема в формате сообщения, которое подписывается и отправляется (переменная message
). Попробуйте посмотреть рабочие примеры интеграции, если доступны. Или запросить описание причин ошибки у вызваемой стороны.
Чтобы убедиться, что на вашей стороне все ОК, попробуйте проверить свою подпись на своей же стороне, как это сделано в тесте. Или с помощью иного иструмента.
Спасибо огромное за ответ!
Проверка проходит.
Попробовал сделать message следующим образом, передать в него вместо string сертификата, List
Вместо:
JwtHeader header = new JwtHeader()
{
{ "alg", alg },
{ "x5c", Convert.ToBase64String(cert.GetRawCertData()) },
};
Теперь передаю:
var arr = new List<string>();
arr.Add(Convert.ToBase64String(cert.GetRawCertData()));'
JwtHeader header = new JwtHeader()
{
{ "alg", alg },
{ "x5c", arr },
};
Токен генерится, обращаюсь к внешней системе, получаю:
400 Request Header Or Cookie Too Large
В документации (от внешней системы) имеется
Подпись токена (signature) должна быть сформирована в формате pkcs#7. При этом для подписи необходимо оставить только блок SignerInfo и убрать из нее данные сертификата;
Т.е. остается вопрос, как убрать из нее данные сертификата;.
Воспользовался другой библиотекой (но сразу оговорюсь, она на Net6, и на данный момент нам не подходит), но попробовал на ней. Библиотека: LibCore (https://github.com/CryptoPro/libcore)
using LibCore.Security.Cryptography.X509Certificates;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Cryptography.Pkcs;
using System.Security.Cryptography.X509Certificates;
using System.Text;
LibCore.Initializer.Initialize();
var certificate = GetCertificate(serialNumber);
var arr = new List<string>();
arr.Add(Convert.ToBase64String(certificate.GetRawCertData()));
JwtHeader header = new JwtHeader()
{
{ "alg", "ECGOST3410-2012" },
{ "x5c", arr }
};
JwtPayload payload = new JwtPayload()
{
{ "sub", "конкретный_гуид" },
{ "aud", "http адрес" },
{ "iat", DateTimeOffset.UtcNow.ToUnixTimeSeconds() },
{ "exp", DateTimeOffset.UtcNow.Add(new TimeSpan(0, 10, 0)).ToUnixTimeSeconds() }
};
JwtSecurityToken token = new JwtSecurityToken(header, payload);
string message = token.EncodedHeader + "." + token.EncodedPayload;
var contentInfo = new ContentInfo(Encoding.UTF8.GetBytes(message));
var signedCms = new SignedCms(contentInfo, true);
CmsSigner cmsSigner = new CmsSigner(certificate)
{
IncludeOption = X509IncludeOption.EndCertOnly,
};
signedCms.ComputeSignature(cmsSigner);
signedCms.RemoveCertificate(certificate);
var signature = signedCms.Encode();
var encodedSignature = Microsoft.IdentityModel.Tokens.Base64UrlEncoder.Encode(signature);
var jwtToken = $"{message}.{encodedSignature}";
При данном подходе, токен отработал и Успешно осуществился вызов API внешнего сервиса! Но, повторюсь, данная библиотека нам не подходит.
И как раз тут имеется: signedCms.RemoveCertificate(certificate);
Просьба, вопрос, получится ли добавить к библиотеке GostCryptography К классу: GostCryptography.Pkcs.GostSignedCms метод RemoveCertificate(certificate);
т.е. чтоб можно было бы вызвать аналогично: signedCms.RemoveCertificate(certificate);
Огромная просьба, пожалуйста, сообщите, реально ли, получится ли это сделать?
Метод удаления сертификата SignedCms.RemoveCertificate()
имеется только в .NET/.NET Core, но отсутствует в .NET Framework. В последних версиях реализация многих классов переработана так, что многие операции осущетсвляются самим .NET в managed-коде, в то время, как .NET Framework в большинстве своем использует Windows Crypto API. Насколько я понимаю, в этом причина, по которой аналогичная функциональность отсутствует в .NET Framework. В Windows Crypto API сертификат можно удалить только по его индексу в структуре сообщения. Чтобы в будущем не ломать обратную совместимость я добавил метод GostSignedCms.RemoveCertificates()
. Эту операцию можно реализовать как в старом .NET Framework, так и в новом. Данный метод удалят из сообщения все сертификаты.
Я пока не вливал ветку, т.к. у меня сейчас нет возможности протестировать данную функциональность. Если у вас есть возможность, переключитесь на ветку remove-certs-from-signedcms
и попробуйте повторить эксперимент. Дадите обратную связь, вольем изменения/выпустим новую версию или попробуем доработать метод. ;)
Скачал исходники. Собрал, подключил напрямую библиотеку, но не запустился.
Следующая ошибка:
Attempt by method 'GostCryptography.Reflection.SignedCmsHelper.RemoveCertificates(System.Security.Cryptography.Pkcs.SignedCms)' to access method 'GostCryptography.Reflection.SignedCmsHelper.GetMessageHandle(System.Security.Cryptography.Pkcs.SignedCms)' failed.
Спасибо. Попробуйте обновиться на последний коммит в ветке remove-certs-from-signedcms
и повторить попытку.
Спасибо огромное! Всё заработало!
День добрый!
Используем Вашу библиотеку, наверное единственное что удалось найти на просторах интернета для подписи с использованием сертификата GOST 3410 в среде Windows. Задача: осуществить взаимодействие с фед. сервисами. В описании указано:
В итоге получаем при любом раскладе ошибку "GW-059: Неверное значение закодированной формы токена". Более детальной информации, к сожалению не удается узнать, что не так в формируемом токене.
Окружение:
За основу взят пример https://github.com/AlexMAS/GostCryptography/blob/master/Source/GostCryptography.Tests/Pkcs/SignedCmsSignTest.cs
Исходный код (для облегчение и сокращения кода, часть кода убрана, суть остается):
using System;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Cryptography;
using System.Security.Cryptography.Pkcs;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using GostCryptography.Base;
using GostCryptography.Gost_R3410;
var certStore = new X509Store(StoreLocation.CurrentUser);
certStore.Open(OpenFlags.ReadOnly);
var collection = certStore.Certificates;
var searchRes = collection.Find(X509FindType.FindBySerialNumber, serialNumber, false);
var cert = searchRes[0];
var alg = string.Empty;
switch (cert.PublicKey.Oid.Value)
{
case "1.2.643.2.2.19":
alg = "GOST3410-2001";
break;
case "1.2.643.7.1.1.1.1":
alg = "ECGOST3410-2012";
break;
case "1.2.643.7.1.1.1.2":
alg = "ECGOST3410-2012";
break;
}
JwtHeader header = new JwtHeader()
{
{ "alg", alg },
{ "x5c", Convert.ToBase64String(cert.GetRawCertData()) },
};
JwtPayload payload = new JwtPayload()
{
{ "sub", "CD28DB17-8A5E-4DDC-8C0B-0F6C10C9B041" }, /* произвольный гуид */
{ "aud", "http://ya.ru" }, /* произвольный адрес */
{ "iat", DateTimeOffset.UtcNow.ToUnixTimeSeconds() },
{ "exp", DateTimeOffset.UtcNow.Add(new TimeSpan(0, 10, 0)).ToUnixTimeSeconds() }
};
JwtSecurityToken token = new JwtSecurityToken(header, payload);
string message = token.EncodedHeader + "." + token.EncodedPayload;
// Создание объекта для подписи сообщения
ContentInfo contentInfo = new ContentInfo(Encoding.UTF8.GetBytes(message));
GostCryptography.Pkcs.GostSignedCms signedCms = new GostCryptography.Pkcs.GostSignedCms(contentInfo);
// Создание объект с информацией о подписчике
CmsSigner cmsSigner = new CmsSigner(cert);
// Включение информации только о конечном сертификате (только для теста)
cmsSigner.IncludeOption = X509IncludeOption.EndCertOnly;
// Создание подписи для сообщения CMS/PKCS#7
signedCms.ComputeSignature(cmsSigner);
// Создание сообщения CMS/PKCS#7
byte[] signature = signedCms.Encode();
var encodedSignature = Microsoft.IdentityModel.Tokens.Base64UrlEncoder.Encode(signature);
var jwtToken = $"{message}.{encodedSignature}";
/ в итоге получаем токен из трех составляющих token.EncodedHeader + "." + token.EncodedPayload + "." + encodedSignature /
И при обращении, при любом раскладе получаем ошибку: "GW-059: Неверное значение закодированной формы токена". Что конкретно не так в токене, выяснить не удается.
В описании имеется фраза: "и убрать из нее данные сертификата;" - пробовали делать signedCms.Certificates.Clear(); перед вызовом signedCms.Encode();, не помогает.
Не можем понять что может быть. Может у нас что-то не так недонастроено, или какой-то строки кода не хватает. Или всё же пытаться выяснять что не так в формате.
Подскажите пожалуйста, что может быть, по какому пути идти ?