Проверка сертификата из отсоединенной подписи под Linux
d1maxa opened this issue · comments
Столкнулся с парой багов при использовании libcore. Вот первый:
//метод для проверки отсоединенной подписи
public VerifySignatureResult VerifyDataSignatureRequest(byte[] data, byte[] signature)
{
var contentInfo = new ContentInfo(data);
var verifyCms = new SignedCms(contentInfo, true);
verifyCms.Decode(signature);
//вытаскиваем инфу из подписи каким сертификатом подписано
var cert = verifyCms.SignerInfos[0].Certificate;
//проверяем сертификат (такой вызов вернет False под linux)
var verified = cert.Verify();
if (!verified)
{
//под Linux нужно загрузить серт повторно, тогда проверка заработает как надо
//или так
//var cert2 = new X509Certificate2(cert.Export(X509ContentType.Cert));
//или так
var cert2 = new X509Certificate2(cert.RawData);
//сейчас уже возвращает True
verified = cert2.Verify();
}
if (!verified)
{
return new VerifySignatureResult(VerifySignatureStatus.NotValideCertificate, cert);
}
Под Windows уже первая проверка var verified = cert.Verify();
возвращает True
Пробовали воспроизвести на следующем примере. Сертификат самоподписанный, установлен в Root и My текущего пользователя. Воспроизвести не получилось. Обе проверки возвращают true.
Можете пожалуйста сообщить версию csp, LibCore, по возможности приложить сертификат, данные и подпись.
Самостоятельно можно попробовать вызвать следующий код (именно он вызывается в Verify()
):
using (var chain = new X509Chain())
{
// Use the default vales of chain.ChainPolicy including:
// RevocationMode = X509RevocationMode.Online
// RevocationFlag = X509RevocationFlag.ExcludeRoot
// VerificationFlags = X509VerificationFlags.NoFlag
// VerificationTime = DateTime.Now
// UrlRetrievalTimeout = new TimeSpan(0, 0, 0)
// throwOnException: false в изначальном методе, интересно, какая ошибка тут
bool verified = chain.Build(cert , throwOnException: true);
for (int i = 0; i < chain.ChainElements.Count; i++)
{
chain.ChainElements[i].Certificate.Dispose();
}
}
Код на котором пробовали воспроизвести ошибку:
LibCore.Initializer.Initialize();
using (var store = new CpX509Store(StoreName.My, StoreLocation.CurrentUser))
{
store.Open(OpenFlags.ReadOnly);
var certForSgn = store.Certificates.Find(X509FindType.FindBySubjectName, "CliServ 2001 512", false)[0];
var contentInfo = new ContentInfo(new byte[] { 0 });
var signedCms = new SignedCms(contentInfo, false);
CmsSigner cmsSigner = new CmsSigner(certForSgn);
signedCms.ComputeSignature(cmsSigner);
var signature = signedCms.Encode();
Console.WriteLine($"CMS Sign: {Convert.ToBase64String(signature)}");
var verifyCms = new SignedCms(contentInfo, true);
verifyCms.Decode(signature);
//вытаскиваем инфу из подписи каким сертификатом подписано
var certFromCms = verifyCms.SignerInfos[0].Certificate;
//проверяем сертификат (такой вызов вернет False под linux)
var verified = certFromCms.Verify();
if (!verified)
{
//под Linux нужно загрузить серт повторно, тогда проверка заработает как надо
//или так
//var cert2 = new X509Certificate2(cert.Export(X509ContentType.Cert));
//или так
var cert2 = new X509Certificate2(certFromCms.RawData);
//сейчас уже возвращает True
verified = cert2.Verify();
}
}
return;
В самоподписанном серте нет же цепочки?
Мой серт получен на http://testgost2012.cryptopro.ru/certsrv/certrqma.asp
Импортирован из винды командой certmgr -install -file /tmp/certs/smev_test.pfx -pfx -pin 123 -silent
Родительский добавлен в доверенные (чтоб проверка проходила) командой certmgr -install -store uRoot -file /tmp/certs/uc_root.cer
Залил этот тестовый серт сюда
Установил сертификат и корневой (тестового уц, отпечаток 927...
), ошибку воспроизвести не получается.
Не получил всех запрашиваемых данных.
Можете пожалуйста сообщить версию csp, LibCore, по возможности приложить данные и подпись, которые пытаетесь проверить.
Также попробуйте у себя локально запустить следующий код, посмотрите какая ошибка при проверке цепочки. 'cert' полученный из verifyCms.SignerInfos[0].Certificate;
.
using (var chain = new X509Chain())
{
// Use the default vales of chain.ChainPolicy including:
// RevocationMode = X509RevocationMode.Online
// RevocationFlag = X509RevocationFlag.ExcludeRoot
// VerificationFlags = X509VerificationFlags.NoFlag
// VerificationTime = DateTime.Now
// UrlRetrievalTimeout = new TimeSpan(0, 0, 0)
// throwOnException: false в изначальном методе, интересно, какая ошибка тут
bool verified = chain.Build(cert , throwOnException: true);
for (int i = 0; i < chain.ChainElements.Count; i++)
{
chain.ChainElements[i].Certificate.Dispose();
}
}
Csp 5.0.12600 КС1 (Linux DEB x64), LibCore.Linux.2023.2.14.1
Данные, подпись тут
Мне просто показалось такое поведение странным, что из подписи проверка сертификата всегда false возвращает, но стоит создать новый объект X509Certificate2 и для него уже true
код для проверки
using (var chain = new X509Chain())
{
// Use the default vales of chain.ChainPolicy including:
// RevocationMode = X509RevocationMode.Online
// RevocationFlag = X509RevocationFlag.ExcludeRoot
// VerificationFlags = X509VerificationFlags.NoFlag
// VerificationTime = DateTime.Now
// UrlRetrievalTimeout = new TimeSpan(0, 0, 0)
// throwOnException: false в изначальном методе, интересно, какая ошибка тут
// этот метод недоступен: internal bool Build(X509Certificate2 certificate, bool throwOnException)
// bool verified = chain.Build(cert , throwOnException: true);
// поэтому юзаю public bool Build(X509Certificate2 certificate) => this.Build(certificate, true);
// никаких исключений не происходит, просто возвращает False
bool verified = chain.Build(cert);
for (int i = 0; i < chain.ChainElements.Count; i++)
{
chain.ChainElements[i].Certificate.Dispose();
}
}
Проверяю на ваших данных - всё ещё не воспроизводим.
Попробуйте позвать internal X509Chain.build так
dynamic chainReflector = chain.ToReflector();
bool verifiedChain = chainReflector.Build(certFromCms, throwOnException: true).OriginalObject;
Ещё можно посмотреть chain.ChainStatus после построения
//тут false, chain.ChainStatus = PartialChain "unable to get local issuer certificate"
verified = chain.Build(cert);
dynamic chainReflector = chain.ToReflector();
//тут false, chainReflector.ChainStatus = PartialChain "unable to get local issuer certificate"
bool verifiedChain = chainReflector.Build(cert, throwOnException: true).OriginalObject;
Странная ошибка "unable to get local issuer certificate" - похоже на сетевую из tls.
Возможно связанная проблема (но вроде как тестовый уц не должен делать редирект на https) - dotnet/runtime#71715
Меняет ли что-то, если выставить?
chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck;
Воспроизводится ли эта ошибка на RSA сертификатах на вашей машине?