我正在尝试将私钥导入 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?
以下是通过创建新密钥容器导入私钥的步骤。为了简单起见,我省略了错误检查和输入。
// 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);