Wincrypt TLS / Schannel从系统存储加载证书

时间:2014-09-29 11:27:39

标签: c++ ssl

我正在使用wincrypt api for C ++与从文件加载的预定根CA建立服务器端安全TLS / Schannel连接,但似乎当我尝试通过openssl客户端连接到我的服务器时,如下所示: openssl s_client -connect localhost:20715

它显示了系统存储中的一些CA证书以及我从接受的CA权限列表中的文件加载的CA证书。这是我的错误/预期行为/错误吗?

以下是重现问题的代码示例:

#undef UNICODE

#define WIN32_LEAN_AND_MEAN

#include <assert.h>
#include <windows.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdlib.h>
#include <stdio.h>
#define SECURITY_WIN32 1
#include <Security.h>
#include <WinCrypt.h>
#include <schannel.h>
#include <fstream>
#include <string.h>

#define DEFAULT_BUFLEN 2048
#define DEFAULT_PORT "27015"
static const char* BEGIN_CERTIFICATE_TAG = "-----BEGIN CERTIFICATE-----";
static const char* END_CERTIFICATE_TAG = "-----END CERTIFICATE-----";
static const size_t endTagLength = strlen(END_CERTIFICATE_TAG);
static const char* caCertPath = "C:\\client_ca.crt";
static const char* certPath = "C:\\server.p12";

ULONG getTokenMaxSize()
{
    PSecPkgInfo  pPackageInfo = NULL;
    SECURITY_STATUS status = QuerySecurityPackageInfo("schannel", &pPackageInfo);
    ULONG maxTokenSize = pPackageInfo->cbMaxToken;
    FreeContextBuffer(pPackageInfo);
    return maxTokenSize;
}

void DoHandshake(char* buffer, int bufferLength, char* outBuffer, int outBufferSize)
{
    HCERTSTORE caStore = CertOpenStore(CERT_STORE_PROV_MEMORY, 0, NULL, 0, NULL);

    FILE *caCertFile = NULL;
    fopen_s(&caCertFile, caCertPath, "rb");
    fseek(caCertFile, 0, SEEK_END);
    size_t caFileSize = ftell(caCertFile);
    fclose(caCertFile);

    BYTE* caData = new BYTE[caFileSize];

    std::fstream caCert(caCertPath, std::ios_base::in | std::ios_base::binary);

    caCert.read((char*)caData, caFileSize);

    char* startPos = strstr((char*)caData, BEGIN_CERTIFICATE_TAG);
    while (startPos != NULL)
    {
        char* endPos = strstr(startPos, END_CERTIFICATE_TAG) + endTagLength;

        DWORD sizeNeeded = 0;
        BOOL ok = CryptStringToBinary(startPos, endPos - startPos, CRYPT_STRING_BASE64HEADER, NULL, &sizeNeeded, NULL, NULL);

        BYTE* buffer = new BYTE[sizeNeeded];
        ok = CryptStringToBinary(startPos, endPos - startPos, CRYPT_STRING_BASE64HEADER, buffer, &sizeNeeded, NULL, NULL);

        ok = CertAddEncodedCertificateToStore(caStore, X509_ASN_ENCODING, buffer, sizeNeeded, CERT_STORE_ADD_USE_EXISTING, NULL);

        startPos = strstr(endPos, BEGIN_CERTIFICATE_TAG);
    }

    FILE *certFile = NULL;
    fopen_s(&certFile, certPath, "rb");
    fseek(certFile, 0, SEEK_END);
    size_t fileSize = ftell(certFile);
    fclose(certFile);

    BYTE* spData = new BYTE[fileSize];

    std::fstream cert(certPath, std::ios_base::in | std::ios_base::binary);

    cert.read((char*)spData, fileSize);

    CRYPT_DATA_BLOB blob;
    blob.cbData = (DWORD)fileSize;
    blob.pbData = spData;

    // Importing the cert. The file cannot be password protected
    HCERTSTORE certStore = PFXImportCertStore(&blob, L"", CRYPT_MACHINE_KEYSET);

    PCCERT_CONTEXT certContext = CertFindCertificateInStore(certStore, X509_ASN_ENCODING, 0, CERT_FIND_ANY, NULL, NULL);

    CertCloseStore(certStore, 0);

    DWORD sspiFlags = ASC_REQ_SEQUENCE_DETECT |
        ASC_REQ_REPLAY_DETECT |
        ASC_REQ_CONFIDENTIALITY |
        ASC_REQ_EXTENDED_ERROR |
        ASC_REQ_STREAM | 
        ASC_REQ_MUTUAL_AUTH;



    SecBuffer inBuffers[2];
    inBuffers[0].BufferType = SECBUFFER_TOKEN;
    inBuffers[0].cbBuffer = bufferLength;
    inBuffers[0].pvBuffer = buffer;

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

    SecBufferDesc inBufferDesc;
    inBufferDesc.cBuffers = 2;
    inBufferDesc.pBuffers = inBuffers;
    inBufferDesc.ulVersion = SECBUFFER_VERSION;


    SecBuffer outputBuffer[1];
    outputBuffer[0].BufferType = SECBUFFER_TOKEN;
    outputBuffer[0].cbBuffer = outBufferSize;
    outputBuffer[0].pvBuffer = outBuffer;

    SecBufferDesc   outBufferDesc;
    outBufferDesc.cBuffers = 1;
    outBufferDesc.pBuffers = outputBuffer;
    outBufferDesc.ulVersion = SECBUFFER_VERSION;
    CtxtHandle  sslContext;

    CredHandle  credHandle;

    SCHANNEL_CRED schannelCred;
    memset(&schannelCred, 0, sizeof(schannelCred));

    schannelCred.dwVersion = SCHANNEL_CRED_VERSION;
    schannelCred.grbitEnabledProtocols = SP_PROT_TLS1;
    schannelCred.cCreds = 1;
    schannelCred.paCred = &certContext;
    schannelCred.hRootStore = NULL;
    schannelCred.dwFlags |= SCH_SEND_ROOT_CERT | SCH_CRED_MANUAL_CRED_VALIDATION;
    schannelCred.hRootStore = caStore;

    TimeStamp       expiration;
    SECURITY_STATUS status = AcquireCredentialsHandle(
        NULL,
        UNISP_NAME,
        SECPKG_CRED_INBOUND,
        NULL,
        &schannelCred,
        NULL,
        NULL,
        &credHandle,
        &expiration);

    TimeStamp       expire;
    status = AcceptSecurityContext(
        (PCredHandle)(&credHandle),
        NULL,
        &inBufferDesc,
        sspiFlags,
        0,
        &sslContext,
        &outBufferDesc,
        &sspiFlags,
        &expire);
}

int __cdecl main(void)
{
    WSADATA wsaData;
    int iResult;

    SOCKET ListenSocket = INVALID_SOCKET;
    SOCKET ClientSocket = INVALID_SOCKET;

    struct addrinfo *result = NULL;
    struct addrinfo hints;

    int iSendResult;
    char recvbuf[DEFAULT_BUFLEN];
    int recvbuflen = DEFAULT_BUFLEN;

    // Initialize Winsock
    iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
    if (iResult != 0) {
        printf("WSAStartup failed with error: %d\n", iResult);
        return 1;
    }

    ZeroMemory(&hints, sizeof(hints));
    hints.ai_family = AF_INET;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_protocol = IPPROTO_TCP;
    hints.ai_flags = AI_PASSIVE;

    // Resolve the server address and port
    iResult = getaddrinfo(NULL, DEFAULT_PORT, &hints, &result);
    if (iResult != 0) {
        printf("getaddrinfo failed with error: %d\n", iResult);
        WSACleanup();
        return 1;
    }

    // Create a SOCKET for connecting to server
    ListenSocket = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
    if (ListenSocket == INVALID_SOCKET) {
        printf("socket failed with error: %ld\n", WSAGetLastError());
        freeaddrinfo(result);
        WSACleanup();
        return 1;
    }

    // Setup the TCP listening socket
    iResult = bind(ListenSocket, result->ai_addr, (int)result->ai_addrlen);
    if (iResult == SOCKET_ERROR) {
        printf("bind failed with error: %d\n", WSAGetLastError());
        freeaddrinfo(result);
        closesocket(ListenSocket);
        WSACleanup();
        return 1;
    }

    freeaddrinfo(result);

    iResult = listen(ListenSocket, SOMAXCONN);
    if (iResult == SOCKET_ERROR) {
        printf("listen failed with error: %d\n", WSAGetLastError());
        closesocket(ListenSocket);
        WSACleanup();
        return 1;
    }

    // Accept a client socket
    ClientSocket = accept(ListenSocket, NULL, NULL);
    if (ClientSocket == INVALID_SOCKET) {
        printf("accept failed with error: %d\n", WSAGetLastError());
        closesocket(ListenSocket);
        WSACleanup();
        return 1;
    }

    // No longer need server socket
    closesocket(ListenSocket);
    size_t outSize = getTokenMaxSize();
    char* outBuf = new char[outSize];
    // Receive until the peer shuts down the connection
    do {

        iResult = recv(ClientSocket, recvbuf, recvbuflen, 0);
        if (iResult > 0) {

            DoHandshake(recvbuf, recvbuflen, outBuf, outSize);
            iSendResult = send(ClientSocket, outBuf, outSize, 0);
            if (iSendResult == SOCKET_ERROR) {
                printf("send failed with error: %d\n", WSAGetLastError());
                closesocket(ClientSocket);
                WSACleanup();
                return 1;
            }
        }
        else if (iResult == 0)
            printf("Connection closing...\n");
        else  {
            printf("recv failed with error: %d\n", WSAGetLastError());
            closesocket(ClientSocket);
            WSACleanup();
            return 1;
        }

    } while (iResult > 0);

    // shutdown the connection since we're done
    iResult = shutdown(ClientSocket, SD_SEND);
    if (iResult == SOCKET_ERROR) {
        printf("shutdown failed with error: %d\n", WSAGetLastError());
        closesocket(ClientSocket);
        WSACleanup();
        return 1;
    }

    // cleanup
    closesocket(ClientSocket);
    WSACleanup();
    delete outBuf;
    return 0;
}

0 个答案:

没有答案