CryptoPro / corefx

This repo contains the .NET Core foundational libraries, called CoreFX. It includes classes for collections, file systems, console, XML, async and many others. We welcome contributions.
https://github.com/dotnet/core
MIT License
27 stars 7 forks source link

Сломался Gost EnvelopedCMS на win, не работал на Linux #56

Open Fasjeit opened 2 years ago

Fasjeit commented 2 years ago

Поломался EnvelopedCms, скорее всего после очередного вливания из upstream. Тестов не было....

При вызове envelopedCms.Encrypt(recip1);

вываливается ASN1 bad tag value met

Проблема в том, что после одного из вливаний появился код в PkcsPalWindows.Encrypt.cs

 using (SafeCertContextHandle hCertContext = recipient.Certificate.CreateCertContextHandle())
 {
    CERT_CONTEXT* pCertContext = hCertContext.DangerousGetCertContext();
    CERT_INFO* pCertInfo = pCertContext->pCertInfo;

    CMSG_KEY_TRANS_RECIPIENT_ENCODE_INFO* pEncodeInfo = (CMSG_KEY_TRANS_RECIPIENT_ENCODE_INFO*)(hb.Alloc(sizeof(CMSG_KEY_TRANS_RECIPIENT_ENCODE_INFO)));

    pEncodeInfo->cbSize = sizeof(CMSG_KEY_TRANS_RECIPIENT_ENCODE_INFO);

    RSAEncryptionPadding padding = recipient.RSAEncryptionPadding;

    if (padding is null)
    {
        if (recipient.Certificate.GetKeyAlgorithm() == Oids.RsaOaep)
        {
            byte[] parameters = recipient.Certificate.GetKeyAlgorithmParameters();

            if (parameters == null || parameters.Length == 0)
            {
                padding = RSAEncryptionPadding.OaepSHA1;
            }
            else if (!PkcsHelpers.TryGetRsaOaepEncryptionPadding(parameters, out padding, out _))
            {
                throw ErrorCode.CRYPT_E_UNKNOWN_ALGO.ToCryptographicException();
            }
        }
        else
        {
            padding = RSAEncryptionPadding.Pkcs1;
        }
    }

    if (padding == RSAEncryptionPadding.Pkcs1)
    {
        // ГОСТ ПОПАДАЕТ СЮДА!
        // в результате в параметры и OID попадает RSA, а не гост
        pEncodeInfo->KeyEncryptionAlgorithm.pszObjId = hb.AllocAsciiString(Oids.Rsa);
        pEncodeInfo->KeyEncryptionAlgorithm.Parameters.cbData = (uint)s_rsaPkcsParameters.Length;
        pEncodeInfo->KeyEncryptionAlgorithm.Parameters.pbData = hb.AllocBytes(s_rsaPkcsParameters);
    }
    else if (padding == RSAEncryptionPadding.OaepSHA1)
    {
        pEncodeInfo->KeyEncryptionAlgorithm.pszObjId = hb.AllocAsciiString(Oids.RsaOaep);
        pEncodeInfo->KeyEncryptionAlgorithm.Parameters.cbData = (uint)s_rsaOaepSha1Parameters.Length;
        pEncodeInfo->KeyEncryptionAlgorithm.Parameters.pbData = hb.AllocBytes(s_rsaOaepSha1Parameters);
    }
    else if (padding == RSAEncryptionPadding.OaepSHA256)
    {
        pEncodeInfo->KeyEncryptionAlgorithm.pszObjId = hb.AllocAsciiString(Oids.RsaOaep);
        pEncodeInfo->KeyEncryptionAlgorithm.Parameters.cbData = (uint)s_rsaOaepSha256Parameters.Length;
        pEncodeInfo->KeyEncryptionAlgorithm.Parameters.pbData = hb.AllocBytes(s_rsaOaepSha256Parameters);
    }
    else if (padding == RSAEncryptionPadding.OaepSHA384)
    {
        pEncodeInfo->KeyEncryptionAlgorithm.pszObjId = hb.AllocAsciiString(Oids.RsaOaep);
        pEncodeInfo->KeyEncryptionAlgorithm.Parameters.cbData = (uint)s_rsaOaepSha384Parameters.Length;
        pEncodeInfo->KeyEncryptionAlgorithm.Parameters.pbData = hb.AllocBytes(s_rsaOaepSha384Parameters);
    }
    else if (padding == RSAEncryptionPadding.OaepSHA512)
    {
        pEncodeInfo->KeyEncryptionAlgorithm.pszObjId = hb.AllocAsciiString(Oids.RsaOaep);
        pEncodeInfo->KeyEncryptionAlgorithm.Parameters.cbData = (uint)s_rsaOaepSha512Parameters.Length;
        pEncodeInfo->KeyEncryptionAlgorithm.Parameters.pbData = hb.AllocBytes(s_rsaOaepSha512Parameters);
    }
    else
    {
        throw ErrorCode.CRYPT_E_UNKNOWN_ALGO.ToCryptographicException();
    }

    pEncodeInfo->pvKeyEncryptionAuxInfo = IntPtr.Zero;
    pEncodeInfo->hCryptProv = IntPtr.Zero;

    pEncodeInfo->RecipientPublicKey = pCertInfo->SubjectPublicKeyInfo.PublicKey;

    pEncodeInfo->RecipientId = EncodeRecipientId(recipient, hCertContext, pCertContext, pCertInfo, hb);

    return pEncodeInfo;
}

когда раньше было явное копирование

// KeyEncryptionAlgorithm.pszObjId
IntPtr pszObjId = new IntPtr((long) pKeyEncryptionAlgorithm + (long) Marshal.OffsetOf(typeof(CAPI.CRYPT_ALGORITHM_IDENTIFIER), "pszObjId"));
Marshal.WriteIntPtr(pszObjId, encryptParam.rgszObjId[index].DangerousGetHandle());

// KeyEncryptionAlgorithm.Parameters
IntPtr pParameters = new IntPtr((long) pKeyEncryptionAlgorithm + (long) Marshal.OffsetOf(typeof(CAPI.CRYPT_ALGORITHM_IDENTIFIER), "Parameters"));

// KeyEncryptionAlgorithm.Parameters.cbData
IntPtr pcbData = new IntPtr((long) pParameters + (long) Marshal.OffsetOf(typeof(CAPI.CRYPTOAPI_BLOB), "cbData"));
Marshal.WriteInt32(pcbData, (int) certInfo.SubjectPublicKeyInfo.Algorithm.Parameters.cbData);

// KeyEncryptionAlgorithm.Parameters.pbData
IntPtr ppbData = new IntPtr((long) pParameters + (long) Marshal.OffsetOf(typeof(CAPI.CRYPTOAPI_BLOB), "pbData"));
Marshal.WriteIntPtr(ppbData, certInfo.SubjectPublicKeyInfo.Algorithm.Parameters.pbData);
Fasjeit commented 2 years ago

Чиним явным копированием для ГОСТа

if (padding == RSAEncryptionPadding.Pkcs1)
  {
  // begin: gost
  switch (recipient.Certificate.GetKeyAlgorithm())
  {                            
      case Oids.Gost3410:
      case Oids.Gost3410_2012_256:
      case Oids.Gost3410_2012_512:
      {
          // copy from cert info explicitly
          pEncodeInfo->KeyEncryptionAlgorithm.pszObjId = hb.AllocAsciiString(recipient.Certificate.GetKeyAlgorithm());

          // uint, копируем
          pEncodeInfo->KeyEncryptionAlgorithm.Parameters.cbData = pCertInfo->SubjectPublicKeyInfo.Algorithm.Parameters.cbData;

          // копируем из памяти и записываем
          var pbDataBytes = new byte[pCertInfo->SubjectPublicKeyInfo.Algorithm.Parameters.cbData];
          Marshal.Copy(pCertInfo->SubjectPublicKeyInfo.Algorithm.Parameters.pbData, pbDataBytes, 0, pbDataBytes.Length);
          pEncodeInfo->KeyEncryptionAlgorithm.Parameters.pbData = hb.AllocBytes(pbDataBytes);
          break;

      }
      default:
      {
          // end: gost
          pEncodeInfo->KeyEncryptionAlgorithm.pszObjId = hb.AllocAsciiString(Oids.Rsa);
          pEncodeInfo->KeyEncryptionAlgorithm.Parameters.cbData = (uint)s_rsaPkcsParameters.Length;
          pEncodeInfo->KeyEncryptionAlgorithm.Parameters.pbData = hb.AllocBytes(s_rsaPkcsParameters);
          break;
      }
  }
Fasjeit commented 2 years ago

и добавляем тест из шарпея.

Fasjeit commented 2 years ago

... почему то не заработал csp-non persist сертификат при проверке Cms, не смог выкусить keyspec. В целом минор, но нужно будет выяснить в чём дело и можно ли исправить.

Fasjeit commented 2 years ago

На unix видимо и не работал. CryptMsgGetParam(CMSG_ENVELOPE_ALGORITHM_PARAM) возвращает -2146889724 - Invalid cryptographic message type.

Судя по jira (https://jira.cp.ru/browse/CPCSP-11233) поведение методов исправлено только 15 октября 20 года, используемая версия csp для тестов - не позднее 27 августа.

Раньше точно не работало - https://www.cryptopro.ru/forum2/default.aspx?g=posts&m=61697#post61697

Актуализировать версию csp, проверить ещё раз.

Fasjeit commented 2 years ago

Обновился до CSP 5.0.12417-6, помогло с CMSG_ENVELOPE_ALGORITHM_PARAM, теперь падаем с CMSG_CERT_COUNT_PARAM.

Fasjeit commented 2 years ago

Поддержка CMSG_CERT_COUNT_PARAM в capilight https://jira.cp.ru/browse/CPCSP-12931

dipsx commented 2 years ago

Нет еще прогнозов когда примерно сможете поправить?)

Fasjeit commented 2 years ago

Windows должен работать, на Linux ждём доработку в csp.

Fasjeit commented 2 years ago

Починили CMSG_CERT_COUNT_PARAM. Можем шифровать в ночной csp. Csp в сборке пока не обновлял.

Теперь чиним CMSG_UNPROTECTED_ATTR_PARAM, ибо не можем расшифровывать. https://jira.cp.ru/browse/CPCSP-12965

Fasjeit commented 2 years ago

Починили CMSG_UNPROTECTED_ATTR_PARAM. Версия с исправлениями - 5.0.12515

Сломались в corefx при поиске сертификата получателя по cn (строка из cms обрезалась до первого символа, и как результат не находил получателя) - тоже починили.

Расшифрование чувствительно к сроку действия закрытого ключа (пишет access denied). Если будут падать тесты на этом - отключить временно тест.

Fasjeit commented 2 years ago

Тест ожидаемо ломается в ожидаемом месте. С отключение срока действия ключа пока вопрос.

Убираю пока тест с unix. На винде остаётся.

Fasjeit commented 2 years ago

Проблема теста с контейнером на два ключа - на никсах ведёт себя иначе. Судя по всему шифрует и пытается расшифровать на ключе подписи.

Надо будет перегенерить нормально ключевые контейнеры.

Fasjeit commented 2 years ago

В текущем релизе csp правок ещё нет, ждём следующего. После чего релизим corefx.

Также надо будет обновить инструкцию по сборке в DotnetSampleProject, указав новые версии csp.

В мастере всё должно работать с ночным csp (5.0.12515) на unix. Windows работает и на более старых версиях.

Ночную сборку мастера corefx можно взять тут win - https://ci.appveyor.com/project/CryptoPro/corefx/build/job/poqe4onpj1brqyvp/artifacts unix - https://ci.appveyor.com/project/CryptoPro/corefx/build/job/akb1y6ks72sqaucg/artifacts