SChannel, SEC_E_ALGORITHM_MISMATCH (0x80090331)

Дни устранения неполадок, поиска решений и перечитывания документации Microsoft по необходимым функциям. Изменение переменных, повторная попытка снова и снова. Помощь очень ценится, я уверен, что это не только я столкнулся с этим.

Я работаю над реализацией сетевых клиентских и серверных приложений, которые взаимодействуют со слоем SSL/TLS с использованием SChannel (и позже я также получу эту работу с OpenSSL для перекрестной совместимости). У меня есть клиент, который, как известно, работает, поэтому мы можем сосредоточиться на стороне сервера.

На данный момент цель состоит в том, чтобы заставить его работать без предоставления сертификата с обеих сторон (они должны просто генерироваться на лету при необходимости). Я выполняю AcquireCredentialsHandle, загружаю исходный токен с клиента (работающего на том же хосте) и вызываю AcceptSecurityContext.

Кажется, что независимо от того, какую переменную я изменяю, я всегда получаю ту же ошибку 0x80090331 при первом вызове AcceptSecurityContext. Протестировано на Windows 7 и Windows Server 2012.

Мне кажется, что вне моего кода должно быть что-то, параметр ОС, который мне нужно исправить. Я нахожу противоречивую информацию в Интернете. TLS 1.1 и TLS 1.2 были добавлены в реестр по адресу SecurityProviders\SCHANNEL\Protocols* и установлены с данными DisabledByDefault=0. Также добавлено ", schannel.dll" в "SecurityProviders".

Код выглядит следующим образом:

... snip... (код для вызова AcquireCredentialsHandle)

PCCERT_CONTEXT serverCerts[1] = {0};

SCHANNEL_CRED sc = {0};
sc.dwVersion = SCHANNEL_CRED_VERSION;
//sc.grbitEnabledProtocols = SP_PROT_SSL3_SERVER | SP_PROT_TLS1_SERVER | SP_PROT_TLS1_1_SERVER | SP_PROT_TLS1_2_SERVER;
sc.grbitEnabledProtocols = SP_PROT_TLS1_1_SERVER | SP_PROT_TLS1_2_SERVER;
sc.dwFlags = 0;
sc.cCreds = 0; // Let Crypto API find the appropriate certificate for us
sc.paCred = serverCerts;

TimeStamp tsExpiry;
SECURITY_STATUS status = AcquireCredentialsHandle(
    NULL,
    UNISP_NAME,
    SECPKG_CRED_INBOUND,
    NULL,
    &sc,
    NULL,
    NULL,
    &hCredential,
    &tsExpiry);
std::cout << "AcquireCredentialsHandle result = 0x" << std::hex << status << std::endl;

... snip... (код для вызова AcceptSecurityContext)

    // TOKEN is received from client into m_receivedData
    //std::vector<char> m_receivedData;

    m_ctxtFlags = ASC_REQ_ALLOCATE_MEMORY | ASC_REQ_STREAM;
    SecBuffer inBuffers[2];

    // Provide Schannel with the remote host's handshake data
    inBuffers[0].pvBuffer    = (char*)(&m_receivedData[0]);
    inBuffers[0].cbBuffer    = (unsigned long)m_receivedData.size();
    inBuffers[0].BufferType  = SECBUFFER_TOKEN;

    inBuffers[1].pvBuffer   = NULL;
    inBuffers[1].cbBuffer   = 0;
    inBuffers[1].BufferType = SECBUFFER_EMPTY;

    SecBufferDesc inBufferDesc = {0};
    inBufferDesc.cBuffers   = 2;
    inBufferDesc.pBuffers   = inBuffers;
    inBufferDesc.ulVersion  = SECBUFFER_VERSION;

    SecBuffer outBuffers[2];

    // We let Schannel allocate the output buffer for us
    outBuffers[0].pvBuffer   = NULL;
    outBuffers[0].cbBuffer   = 0;
    outBuffers[0].BufferType = SECBUFFER_TOKEN;

    // Contains alert data if an alert is generated
    outBuffers[1].pvBuffer   = NULL;
    outBuffers[1].cbBuffer   = 0;
    outBuffers[1].BufferType = SECBUFFER_ALERT;

    SecBufferDesc outBufferDesc = {0};
    outBufferDesc.cBuffers   = 2;
    outBufferDesc.pBuffers   = outBuffers;
    outBufferDesc.ulVersion  = SECBUFFER_VERSION;

    unsigned long fContextAttr;
    TimeStamp tsTimeStamp;

    SECURITY_STATUS status = NULL;
    if (isFirstCall) {
        status = AcceptSecurityContext(
            &hCredential,
            NULL,
            &inBufferDesc,
            m_ctxtFlags,
            0,
            &hNewContext,
            &outBufferDesc,
            &fContextAttr,
            NULL);
        std::cout << "AcceptSecurityContext (isFirstCall) result = 0x" << std::hex << status << std::endl;
    }

1 ответ

Решение

SSL/TLS требует сертификат на стороне сервера. Отсутствующий сертификат на стороне сервера генерирует ошибку 0x80090331. Следующим шагом является создание самозаверяющего сертификата по мере необходимости. См. CertCreateSelfSignCertificate. Пример A C с использованием CryptoAPI можно найти здесь.

Другие вопросы по тегам