title: Structure of the certificate pointed to by NCRYPT_KEY_HANDLE
body: <p>I've written a credential provider and a key storage provider to logon to windows via certificate. As the documentation in this points is quite vague I used different samples from Microsoft to get things working.</p>
  
  <p>I think I'm nearly there, but the logon behaves unpredictably. Sometimes I get through to the kerberos server (which complains about the certificate), sometimes the process fails with 0x80090029 without any information and sometimes windows crashes. As these crashes all have to do with access violations or null pointers and happen to occur in various places (kerberos.dll,  Windows.UI.Logon.dll, ...) I think it has something to do with my key structure that i point the given NCRYT_KEY_HANDLE to in my OpenKey-implementation. </p>
  
  <p>The KeyStorageProviderSample in the CNG-Kit has an example, but relies on a RSA-key stored in %AppData%. I don't have the private key available as it is stored in secure hardware, I just have the public part (i.e. the public certificate), that I read from another device and import via the following code:</p>
  
  <pre><code>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, &amp;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-&gt;pCertInfo = pcCertContext-&gt;pCertInfo;
      certContext-&gt;cbCertEncoded = pcCertContext-&gt;cbCertEncoded;
      certContext-&gt;pbCertEncoded = pcCertContext-&gt;pbCertEncoded;
      certContext-&gt;dwCertEncodingType = pcCertContext-&gt;dwCertEncodingType;
  
      CERT_INFO *certInfo;
      certInfo = certContext.pCertInfo;
  
      CERT_PUBLIC_KEY_INFO pubKeyInfo = certInfo-&gt;SubjectPublicKeyInfo;
  
      LOG("Aquire cryptocontext");
      HCRYPTPROV hProv = 0;
      if (!CryptAcquireContext(&amp;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, &amp;pubKeyInfo, &amp;publicKeyHandle))
      {
          LOG_LAST_ERROR("CryptImportPublicKeyInfo failed. Err:");
          return -1;
      }
  
      keyHandle-&gt;fFinished = TRUE;
      keyHandle-&gt;hPublicKey = (BCRYPT_KEY_HANDLE)publicKeyHandle;
      keyHandle-&gt;pszKeyBlobType = BCRYPT_RSAPUBLIC_BLOB;
  
      LocalFree(certInfo);
  
      return ERROR_SUCCESS;
  }
  </code></pre>
  
  <p>The key structure is initialized this way:</p>
  
  <pre><code>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-&gt;cbLength = sizeof(KSP_KEY);
      pKey-&gt;dwMagic = KSP_KEY_MAGIC;
      pKey-&gt;dwAlgID = KSP_RSA_ALGID;
      pKey-&gt;pszKeyFilePath = NULL;
      pKey-&gt;pszKeyBlobType = NULL;
      pKey-&gt;dwKeyBitLength = 0;
      pKey-&gt;fFinished = FALSE;
  
      //Copy the keyname into the key struct.
      if (pszKeyName != NULL)
      {
          cbKeyName = (DWORD)(wcslen(pszKeyName) + 1) * sizeof(WCHAR);
          pKey-&gt;pszKeyName = (LPWSTR)HeapAlloc(
              GetProcessHeap(),
              0,
              cbKeyName + sizeof(WCHAR));
          if (pKey-&gt;pszKeyName == NULL)
          {
              return NTE_NO_MEMORY;
          }
          CopyMemory(pKey-&gt;pszKeyName, pszKeyName, cbKeyName);
          pKey-&gt;pszKeyName[cbKeyName / sizeof(WCHAR)] = L'\0';
      }
      else
      {
          pKey-&gt;pszKeyName = NULL;
      }
  
      if (globalRSAProviderHandle == NULL)
      {
          ntStatus = BCryptOpenAlgorithmProvider(
              &amp;globalRSAProviderHandle,
              BCRYPT_RSA_ALGORITHM,
              NULL,
              0);
          if (!NT_SUCCESS(ntStatus))
          {
              return NormalizeNteStatus(ntStatus);
          }
  
      }
      pKey-&gt;hProvider = globalRSAProviderHandle;
  
      pKey-&gt;pbKeyFile = NULL;
      pKey-&gt;cbKeyFile = 0;
  
      pKey-&gt;pbPrivateKey = NULL;
      pKey-&gt;cbPrivateKey = 0;
  
      pKey-&gt;hPublicKey = NULL;
      pKey-&gt;hPrivateKey = NULL;
  
      pKey-&gt;dwExportPolicy = NCRYPT_ALLOW_EXPORT_FLAG | NCRYPT_ALLOW_PLAINTEXT_EXPORT_FLAG;
  
      pKey-&gt;dwKeyUsagePolicy = NCRYPT_ALLOW_DECRYPT_FLAG | NCRYPT_ALLOW_SIGNING_FLAG;
  
      pKey-&gt;pbSecurityDescr = NULL;
      pKey-&gt;cbSecurityDescr = 0;
  
      InitializeListHead(&amp;pKey-&gt;PropertyList);
      *ppKey = pKey;
      pKey = NULL;
      return ERROR_SUCCESS;
  }
  </code></pre>
  
  <p>Somewhere in there must be the mistake leading to the various memory errors. But as I'm quite new to windows programming and c/c++ I just can't spot the point and can't find any documentation about the datastructure that windows expects for the NCRYTP_KEY_HANDLE.
  Does anybody know more about this structure?</p>