0

証明書を介して Windows にログオンするための資格情報プロバイダーとキー ストレージ プロバイダーを作成しました。この点のドキュメントは非常に曖昧であるため、Microsoft のさまざまなサンプルを使用して動作させました。

私はもうすぐそこにいると思いますが、ログオンは予期しない動作をします。ときどき kerberos サーバー (証明書について不平を言う) に到達することもあれば、情報なしで 0x80090029 でプロセスが失敗することもあれば、Windows がクラッシュすることもあります。これらのクラッシュはすべてアクセス違反またはヌル ポインターと関係があり、たまたまさまざまな場所 (kerberos.dll、Windows.UI.Logon.dll、...) で発生するため、私のキー構造と関係があると思います。指定された NCRYT_KEY_HANDLE を OpenKey 実装でポイントします。

CNG-Kit の KeyStorageProviderSample に例がありますが、%AppData% に保存されている RSA キーに依存しています。安全なハードウェアに保存されているため、利用可能な秘密鍵はありません。別のデバイスから読み取って次のコードを介してインポートする公開部分(つまり、公開証明書)だけがあります。

SECURITY_STATUS WINAPI KeyHandler::ReadPemCert(__inout  KSP_KEY *keyHandle)
{
    LOG_FUNCTION;

    CERT_CONTEXT certContext = {};
    DWORD readLength = 0;

    LOG("Fetch certificate");
    const int maxSizeInBytes = 4096;
    char pemCertificateAsBytes[maxSizeInBytes];
    BluetoothClient bluetoothClient = BluetoothClient();
    bluetoothClient.getCertificate((PBYTE)pemCertificateAsBytes, readLength);

    DWORD certAsDerLen = readLength;
    BYTE* certAsDer = new BYTE[certAsDerLen];
    LOG("convert PEM to DER");
    if (!CryptStringToBinaryA(pemCertificateAsBytes, 0, CRYPT_STRING_BASE64, certAsDer, &certAsDerLen, NULL, NULL))
    {
        LOG_LAST_ERROR("CryptStringToBinary failed. Err:");
    }
    LOG_BYTES_AS_HEX("DER-Zertifikat", certAsDer, certAsDerLen);

    PCCERT_CONTEXT pcCertContext = CertCreateCertificateContext(X509_ASN_ENCODING, certAsDer, certAsDerLen);
    certContext->pCertInfo = pcCertContext->pCertInfo;
    certContext->cbCertEncoded = pcCertContext->cbCertEncoded;
    certContext->pbCertEncoded = pcCertContext->pbCertEncoded;
    certContext->dwCertEncodingType = pcCertContext->dwCertEncodingType;

    CERT_INFO *certInfo;
    certInfo = certContext.pCertInfo;

    CERT_PUBLIC_KEY_INFO pubKeyInfo = certInfo->SubjectPublicKeyInfo;

    LOG("Aquire cryptocontext");
    HCRYPTPROV hProv = 0;
    if (!CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT))
    {
        {
            LOG_LAST_ERROR("CryptAcquireContext failed. Err:");
            return -1;
        }
    }

    LOG("Importing public key");
    NCRYPT_KEY_HANDLE publicKeyHandle = NULL;
    if (!CryptImportPublicKeyInfo(hProv, X509_ASN_ENCODING, &pubKeyInfo, &publicKeyHandle))
    {
        LOG_LAST_ERROR("CryptImportPublicKeyInfo failed. Err:");
        return -1;
    }

    keyHandle->fFinished = TRUE;
    keyHandle->hPublicKey = (BCRYPT_KEY_HANDLE)publicKeyHandle;
    keyHandle->pszKeyBlobType = BCRYPT_RSAPUBLIC_BLOB;

    LocalFree(certInfo);

    return ERROR_SUCCESS;
}

キー構造は次のように初期化されます。

SECURITY_STATUS
WINAPI
KeyHandler::CreateNewKeyObject(
    __in_opt LPCWSTR pszKeyName,
    __deref_out KSP_KEY **ppKey)
{
    LOG_FUNCTION;

    KSP_KEY *pKey = NULL;
    DWORD   cbKeyName = 0;
    SECURITY_STATUS   Status = NTE_INTERNAL_ERROR;
    NTSTATUS          ntStatus = STATUS_INTERNAL_ERROR;

    pKey = (KSP_KEY *)HeapAlloc(GetProcessHeap(), 0, sizeof(KSP_KEY));
    if (pKey == NULL)
    {
        return NTE_NO_MEMORY;
    }
    pKey->cbLength = sizeof(KSP_KEY);
    pKey->dwMagic = KSP_KEY_MAGIC;
    pKey->dwAlgID = KSP_RSA_ALGID;
    pKey->pszKeyFilePath = NULL;
    pKey->pszKeyBlobType = NULL;
    pKey->dwKeyBitLength = 0;
    pKey->fFinished = FALSE;

    //Copy the keyname into the key struct.
    if (pszKeyName != NULL)
    {
        cbKeyName = (DWORD)(wcslen(pszKeyName) + 1) * sizeof(WCHAR);
        pKey->pszKeyName = (LPWSTR)HeapAlloc(
            GetProcessHeap(),
            0,
            cbKeyName + sizeof(WCHAR));
        if (pKey->pszKeyName == NULL)
        {
            return NTE_NO_MEMORY;
        }
        CopyMemory(pKey->pszKeyName, pszKeyName, cbKeyName);
        pKey->pszKeyName[cbKeyName / sizeof(WCHAR)] = L'\0';
    }
    else
    {
        pKey->pszKeyName = NULL;
    }

    if (globalRSAProviderHandle == NULL)
    {
        ntStatus = BCryptOpenAlgorithmProvider(
            &globalRSAProviderHandle,
            BCRYPT_RSA_ALGORITHM,
            NULL,
            0);
        if (!NT_SUCCESS(ntStatus))
        {
            return NormalizeNteStatus(ntStatus);
        }

    }
    pKey->hProvider = globalRSAProviderHandle;

    pKey->pbKeyFile = NULL;
    pKey->cbKeyFile = 0;

    pKey->pbPrivateKey = NULL;
    pKey->cbPrivateKey = 0;

    pKey->hPublicKey = NULL;
    pKey->hPrivateKey = NULL;

    pKey->dwExportPolicy = NCRYPT_ALLOW_EXPORT_FLAG | NCRYPT_ALLOW_PLAINTEXT_EXPORT_FLAG;

    pKey->dwKeyUsagePolicy = NCRYPT_ALLOW_DECRYPT_FLAG | NCRYPT_ALLOW_SIGNING_FLAG;

    pKey->pbSecurityDescr = NULL;
    pKey->cbSecurityDescr = 0;

    InitializeListHead(&pKey->PropertyList);
    *ppKey = pKey;
    pKey = NULL;
    return ERROR_SUCCESS;
}

どこかに、さまざまなメモリ エラーの原因となる間違いがあるはずです。しかし、私はWindowsプログラミングとc/c++にまったく慣れていないので、ポイントを見つけることができず、WindowsがNCRYTP_KEY_HANDLEに期待するデータ構造に関するドキュメントを見つけることができません。この構造について詳しく知っている人はいますか?

4

1 に答える 1