CryptoPro / libcore

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Проверка сертификата из отсоединенной подписи под Linux

d1maxa opened this issue · comments

commented

Столкнулся с парой багов при использовании 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;
commented

В самоподписанном серте нет же цепочки?
Мой серт получен на 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();
    }
}
commented

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 после построения

commented
//тут 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 сертификатах на вашей машине?