将私钥导入 Microsoft TPM 虚拟智能卡,无需弹出卡选择和 PIN 的窗口

问题描述 投票:0回答:1

我正在尝试将私钥导入 Microsoft TPM 虚拟智能卡。

使用旧版加密API,私钥已成功导入。使用 certutil -scinfo 进行验证。这种方法的问题是,它向用户显示两个对话框,一个用于选择虚拟智能卡,另一个用于 PIN。

我已使用以下 tpmvscmgr 命令创建了 TPM 虚拟智能卡。 “tpmvscmgr.exe 创建/名称 TestVSC /pin 默认/adminkey 随机/生成”

这是带有遗留加密 API 的代码。 CryptAcquireContext() 显示用户对话框。

    BOOL Legacy_LoadPrivateKey(LPBYTE pbBlob, DWORD cbSize, PCCERT_CONTEXT pCertContext, 
        CHAR szContainerName[1024])
    {
        DWORD dwLen = 0;
        HCRYPTPROV hProv;
        BOOL bRet = CryptAcquireContext(&hProv, NULL, MS_SCARD_PROV, PROV_RSA_FULL, 
        CRYPT_NEWKEYSET);
    

    if (bRet)
    {
        const std::string pin = "12345678";
        
        // get the container name
        dwLen = 1024;
        CryptGetProvParam(hProv, PP_CONTAINER, (BYTE*)szContainerName, &dwLen, 0);

        HCRYPTKEY hKey;
        bRet = CryptImportKey(hProv, pbBlob, cbSize, NULL, 0, &hKey);
        if (bRet)
        {
            bRet = CryptSetKeyParam(hKey, KP_CERTIFICATE, pCertContext->pbCertEncoded, 0);
            if (!bRet)
            {
                DWORD dwError = GetLastError();                
                //_tprintf(_T("Failed to import the certificate into the smart card. Error 0x%.8X\n"), dwError);
            }

            CryptDestroyKey(hKey);
        }
        else
        {
            DWORD dwError = GetLastError();
            //_tprintf(_T("Failed to import the private key into the smart card. Error 0x%.8X\n"), dwError);
        }

        CryptReleaseContext(hProv, 0);

        if (!bRet)
        {
            // delete the container because of the error
            CryptAcquireContextA(&hProv, szContainerName, MS_SCARD_PROV_A, PROV_RSA_FULL, CRYPT_DELETEKEYSET);
        }
    }
    else
    {
        DWORD dwError = GetLastError();
        //_tprintf(_T("Failed to create a new container on the smart card. Error 0x%.8X\n"), dwError);
    }

    return bRet;
}

这是带有 Ncrypt API 的代码。 NCryptImportKey() 返回错误 NTE_BAD_KEY_STATE

bool Ncrypt_ImportPrivateKey(const wchar_t* keyName, BYTE* privateKeyBlob, DWORD privateKeyBlobSize) {
    NCRYPT_KEY_HANDLE hKey = 0;
    SECURITY_STATUS status;

    // Open a handle to the Microsoft Smart Card Key Storage Provider
    NCRYPT_PROV_HANDLE hProvider = 0;
    SECURITY_STATUS status = NCryptOpenStorageProvider(&hProvider, 
    MS_SMART_CARD_KEY_STORAGE_PROVIDER, 0);
    if (status != ERROR_SUCCESS) {
        std::cerr << "Failed to open storage provider: " << status << std::endl;
        return 1;
    }

    // Create a persisted key handle
    status = NCryptCreatePersistedKey(hProvider, &hKey, BCRYPT_RSA_ALGORITHM, L"\\\\.\\Microsoft Virtual Smart Card 1\\MyKeyContainer", 0, 0);
    if (status != ERROR_SUCCESS) {
        std::cerr << "Failed to create persisted key: " << status << std::endl;
        return false;
    }

    const wchar_t* pin = L"12345678";

    //PBYTE p = (PBYTE)pin.c_str();
    //auto len = (DWORD)(wcslen(pin.c_str()) + 1) * sizeof(wchar_t);

    auto ret = NCryptSetProperty(hKey, NCRYPT_PIN_PROPERTY, (PBYTE)pin, 8 /*(DWORD)(wcslen(pin) + 1) * sizeof(wchar_t)*/, 0);

    // Import the private key
    status = NCryptImportKey(hProvider, 0, BCRYPT_RSAFULLPRIVATE_BLOB, NULL, &hKey, privateKeyBlob, privateKeyBlobSize, 0);
    if (status != ERROR_SUCCESS) {
        std::cerr << "Failed to import private key: " << status << std::endl;
        NCryptFreeObject(hKey);
        return false;
    }

    // Finalize the key
    status = NCryptFinalizeKey(hKey, 0);
    if (status != ERROR_SUCCESS) {
        std::cerr << "Failed to finalize key: " << status << std::endl;
        NCryptFreeObject(hKey);
        return false;
    }

    // Clean up
    if (hKey) {
        NCryptFreeObject(hKey);
    }
    return true;
}

对于这两种情况,我都通过指定 PIN 建立了 scard 上下文。

SCARDHANDLE ConnectToVirtualSmartCardWithPin(SCARDCONTEXT& hContext, const wchar_t* readerName, const char* pin) {
    SCARDHANDLE hCard;
    DWORD dwActiveProtocol;
    LONG lResult = SCardConnect(hContext, readerName, SCARD_SHARE_SHARED, SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1, &hCard, &dwActiveProtocol);
    if (lResult != SCARD_S_SUCCESS) {
        std::wcerr << L"Failed to connect to virtual smart card: " << lResult << std::endl;
        return NULL;
    }

    // Authenticate with the PIN
    SCARD_IO_REQUEST pioSendPci = *SCARD_PCI_T1;
    BYTE pbRecvBuffer[2];
    DWORD dwRecvLength = sizeof(pbRecvBuffer);

    // Construct the APDU for VERIFY command
    BYTE pbSendBuffer[256] = { 0x00, 0x20, 0x00, 0x80 }; // VERIFY command with the PIN
    size_t pinLen = strlen(pin);
    pbSendBuffer[4] = static_cast<BYTE>(pinLen); // Length of the PIN
    memcpy(pbSendBuffer + 5, pin, pinLen);

    lResult = SCardTransmit(hCard, &pioSendPci, pbSendBuffer, 5 + pinLen, NULL, pbRecvBuffer, &dwRecvLength);
    if (lResult != SCARD_S_SUCCESS) {
        std::wcerr << L"Failed to authenticate with PIN: " << lResult << std::endl;
        SCardDisconnect(hCard, SCARD_LEAVE_CARD);
        return NULL;
    }

    return hCard;
}

int main() {
    
    PCCERT_CONTEXT certContext = nullptr;

    // Load certificate and private key from PFX file   
    std::vector<BYTE> keyData;
    HCRYPTKEY privateKey;
    
    std::vector<BYTE> certData = LoadCertificate("C:\\path_to_your_certificate.pfx", L"YourPFXPassword", certContext, privateKey, keyData);


    SCARDCONTEXT hContext;
    SCARDHANDLE hCard;
    LONG lResult;
    const wchar_t* readerName = L"Microsoft Virtual Smart Card 1";
    const char* pin = "12345678";

    // Initialize the SCARD context
    lResult = SCardEstablishContext(SCARD_SCOPE_USER, NULL, NULL, &hContext);
    if (lResult != SCARD_S_SUCCESS) {
        std::wcerr << L"Failed to establish SCARD context: " << lResult << std::endl;
        return 1;
    }

    // Connect to the virtual smart card with PIN
    hCard = ConnectToVirtualSmartCardWithPin(hContext, readerName, pin);
    if (hCard == NULL) {
        SCardReleaseContext(hContext);
        return 1;
    }
   
    CHAR szContainerName[1024] = { 0 };
    Legacy_LoadPrivateKey(keyData.data(), keyData.size(), certContext, szContainerName);

    
    SCardDisconnect(hCard, SCARD_LEAVE_CARD);
    SCardReleaseContext(hContext);

    return 0;
}

有没有办法将私钥导入 Microsoft TPM 虚拟智能卡,而不提示选择卡和 PIN 的对话框。是否可以通过 API(传统或 Ncrypt)设置 PIN?

c++ winapi cryptography smartcard smartcard-reader
1个回答
0
投票

以下是通过创建新密钥容器导入私钥的步骤。为了简单起见,我省略了错误检查和输入。

// Create a persisted key handle
status = NCryptCreatePersistedKey(hProvider, &hKey, BCRYPT_RSA_ALGORITHM, L"\\\\.\\Microsoft Virtual Smart Card 1\\MyKeyContainer1", 0, 0);

const wchar_t* pin = L"12345678";
DWORD keysize = 2048;

auto ret = NCryptSetProperty(hKey, NCRYPT_PIN_PROPERTY, (PBYTE)pin, (DWORD)(wcslen(pin) + 1) * sizeof(wchar_t), 0);

ret = NCryptSetProperty(hKey, NCRYPT_LENGTH_PROPERTY, (PBYTE)&keysize, sizeof(DWORD), 0);

ret = NCryptSetProperty(
    hKey,
    BCRYPT_PRIVATE_KEY_BLOB,
    pbBlob,
    cbBlob,
    NCRYPT_PERSIST_FLAG | NCRYPT_SILENT_FLAG
);
ret = NCryptSetProperty(hKey, NCRYPT_CERTIFICATE_PROPERTY, pbCert, cbCert, 0);

// Finalize the key
status = NCryptFinalizeKey(hKey, 0);
© www.soinside.com 2019 - 2024. All rights reserved.