C-C++ Code Example: Requesting Authentication Using an External Certificate

 

Applies To: Windows 10, Windows 7, Windows 8, Windows 8.1, Windows Server 2008, Windows Server 2008 R2, Windows Server 2012, Windows Server 2012 R2, Windows Server Technical Preview, Windows Vista

The following example provides an application-defined function that sends a single message with a request for authentication using an external certificate.

For information on how Message Queuing authenticates messages, see Message Authentication.

This function uses the first certificate that it finds in the My store. The example can be modified to select a specific certificate among the certificates in the My store. In that case, the function must enumerate all the certificates in the store and choose the proper certificate for use.

This example can be run only on a computer operating in domain mode because only a domain user can register a certificate and Message Queuing does not use security identifiers (SIDs) in workgroup mode. You can modify the example for operation in workgroup mode by skipping the call to MQRegisterCertificate and setting PROPID_M_SENDERID_TYPE to MQMSG_SENDERID_TYPE_NONE so that Message Queuing will not attach the sender's SID to the message. In this case the receiving application will not be able to compare the certificate attached to the message to the registered external certificate or compare the sender's SID to the SID of the user who registered the certificate.

To request authentication using an external certificate

  1. Define the required constants and variables.

  2. Define the MQMSGPROPS structure.

  3. Call CryptAcquireContextW to acquire a key container handle within the cryptographic service provider (CSP) requested.

  4. Call CertOpenSystemStoreW to open the My certificate store. Note that only a certificate in the My certificate store can be registered as an external certificate.

  5. Call CryptReleaseContext to release the key container handle that is no longer needed.

  6. Retrieve the first certificate from the My certificate store. The example can be modified to enumerate all the certificates in the store and select a particular certificate for use.

  7. Call CertCloseStore to release the handle to the My certificate store that is no longer needed

  8. Specify the properties of the message. This example specifies PROPID_M_AUTH_LEVEL to request authentication and PROPID_M_SENDER_CERT to attach the external certificate to the message. By setting PROPID_M_TIME_TO_REACH_QUEUE to five seconds and PROPID_M_JOURNAL to MQMSG_DEADLETTER, it also requests that the message will be moved to the dead-letter queue on the source computer or an intermediate routing server if the message is not delivered to the destination queue (authenticated) within five seconds (negative source journaling).

  9. Initialize the MQMSGPROPS structure.

  10. Call MQRegisterCertificate to register the external certificate. Only a domain user can register a certificate. On the destination side, Message Queuing validates who sent the message by looking at who registered the certificate.

  11. If MQRegisterCertificate fails, call CertFreeCertificateContext to free the memory allocated for the certificate context and return.

  12. Create a format name for opening the queue. This example creates a direct format name.

Note

wcslen properly handles only null-terminated strings. This code example does not verify that the strings passed to it are null-terminated. It is the responsibility of the caller to ensure that the strings passed are null-terminated.

  1. Call MQOpenQueue to open the queue with send access.

  2. Call MQSendMessage to send the message.

  3. Call CertFreeCertificateContext to free the memory allocated for the certificate context and then call MQCloseQueue to close the opened queue and free resources.

Code Example

The following code example requires MSMQ 2.0 or later.

HRESULT RequestExternalAuthentication(  
                                      WCHAR * wszQueueName,   
                                      WCHAR * wszComputerName  
                                      )  
  {  
  
  // Validate the input strings.  
  if (wszQueueName == NULL || wszComputerName == NULL)  
  {  
    return MQ_ERROR_INVALID_PARAMETER;  
  }  
  
  // Define required constants and variables.  
  const int NUMBEROFPROPERTIES = 5;                   // Number of properties  
  DWORD cPropId = 0;                                  // Property counter  
  HRESULT hr = MQ_OK;                                 // Return code  
  HANDLE hQueue = NULL;                               // Queue handle  
  WCHAR wszStore[3] = L"My";                          // The My certificate store  
  
  // Define an MQMSGPROPS structure.  
  MQMSGPROPS msgProps;  
  MSGPROPID aMsgPropId[NUMBEROFPROPERTIES];  
  MQPROPVARIANT aMsgPropVar[NUMBEROFPROPERTIES];  
  HRESULT aMsgStatus[NUMBEROFPROPERTIES];  
  
  HCRYPTPROV hProv = NULL;  
  HCERTSTORE hStore = NULL;  
  PCCERT_CONTEXT pCertContext = NULL;  
  
  // Acquire a handle to a particular key container within the   
  // requested cryptographic service provider (CSP).  
  if (CryptAcquireContextW(  
                           &hProv,                    // Key container handle  
                           NULL,  
                           NULL,                     // Default CSP  
                           PROV_RSA_FULL,            // Provider type  
                           CRYPT_VERIFYCONTEXT       // Authentication without encryption.  
                           ) == NULL)  
  {  
    return E_FAIL;  
  }  
  
  // Open the My certificate store.  
  hStore = CertOpenSystemStoreW(  
                                hProv,  
                                wszStore  
                                );  
  
  // Release the key container handle that is no longer needed.  
  if(hProv) CryptReleaseContext(hProv, 0);  
  
  if (hStore == NULL)  
  {  
    return E_FAIL;  
  }  
  
  // Retrieve the first certificate in the My certificate store.  
  pCertContext = CertEnumCertificatesInStore(  
                                             hStore,  
                                             NULL  
                                             );  
  
  // Release the store handle that is no longer needed.  
  CertCloseStore(hStore, 0);  
  
  if(pCertContext == NULL)  
  {  
    return E_FAIL;  
  }  
  
  // Specify the message properties.  
  aMsgPropId[cPropId] = PROPID_M_AUTH_LEVEL;               // Property ID  
  aMsgPropVar[cPropId].vt = VT_UI4;                        // Type indicator  
  aMsgPropVar[cPropId].ulVal = MQMSG_AUTH_LEVEL_ALWAYS;    // Signature requested  
  cPropId++;  
  
  aMsgPropId[cPropId] = PROPID_M_SENDER_CERT;              // Property ID  
  aMsgPropVar[cPropId].vt = VT_VECTOR | VT_UI1;            // Type indicator  
  aMsgPropVar[cPropId].caub.pElems = pCertContext->pbCertEncoded;  
  aMsgPropVar[cPropId].caub.cElems = pCertContext->cbCertEncoded;  
  cPropId++;  
  
  aMsgPropId[cPropId] = PROPID_M_TIME_TO_REACH_QUEUE;      // Property ID  
  aMsgPropVar[cPropId].vt = VT_UI4;                        // Type indicator  
  aMsgPropVar[cPropId].ulVal = 5;                          // 5 sec  
  cPropId++;  
  
  aMsgPropId[cPropId] = PROPID_M_JOURNAL;                  // Property ID  
  aMsgPropVar[cPropId].vt = VT_UI1;                        // Type indicator  
  aMsgPropVar[cPropId].bVal = MQMSG_DEADLETTER;            // Negative source journaling  
  cPropId++;  
  
  // Initialize the MQMSGPROPS structure  
  msgProps.cProp = cPropId;  
  msgProps.aPropID = aMsgPropId;  
  msgProps.aPropVar = aMsgPropVar;  
  msgProps.aStatus = aMsgStatus;  
  
  // Call MQRegisterCertificate to register the external certificate.  
  hr = MQRegisterCertificate(  
                             MQCERT_REGISTER_ALWAYS,  
                             pCertContext->pbCertEncoded,  
                             pCertContext->cbCertEncoded  
                             );  
  
  // If MQRegisterCertificate fails, free the memory that was allocated   
  // for the certificate context and return.  
  if (FAILED(hr) && (hr != 0xC00E002E))  
  {  
    wprintf(L"MQRegisterCertificate returned %0x.\n", hr);  
    CertFreeCertificateContext(pCertContext);  
    return hr;  
  }  
  
  // Create a format name for opening the queue.  
  WCHAR * wszFormatName = NULL;  
  DWORD dwFormatNameLength = 0;  
  dwFormatNameLength = wcslen(wszQueueName) + wcslen(wszComputerName) + 12;  
  wszFormatName = new WCHAR[dwFormatNameLength];  
  if (wszFormatName == NULL)  
  {  
    return MQ_ERROR_INSUFFICIENT_RESOURCES;  
  }  
  memset(wszFormatName, 0, dwFormatNameLength*sizeof(WCHAR));  
  
  // ************************************  
  // You must concatenate "DIRECT=OS:", wszComputerName, "\", and  
  // wszQueueName into the wszFormatName buffer.  
  // wszFormatName = "DIRECT=OS:" + wszComputerName + "\" +   
  // wszQueueName  
  // if the format name is too long for the buffer, free the   
  // free the certificate context,       
  // CertFreeCertificateContext(pCertContext) and return FALSE.  
  // ************************************  
  
  // Call MQOpenQueue to open the queue with send access.  
  hr = MQOpenQueue(  
                   wszFormatName,                     // Format name of queue  
                   MQ_SEND_ACCESS,                    // Access mode  
                   MQ_DENY_NONE,                      // Share mode  
                   &hQueue                            // [out] Queue handle  
                   );  
  // Free the memory that was allocated for the format name string.  
  delete [] wszFormatName;  
  
  // Handle any error returned by MQOpenQueue.  
  if (FAILED(hr))  
  {  
    CertFreeCertificateContext(pCertContext);  
    return hr;  
  }  
  
  // Call MQSendMessage to send a message requesting authentication.  
  hr = MQSendMessage(  
                     hQueue,                          // Handle of queue  
                     &msgProps,                       // Message property structure  
                     MQ_NO_TRANSACTION                // Not in a transaction  
                     );  
  
  // Free the memory that was allocated for the certificate context  
  // and then check the value returned by MQSendMessage.  
  CertFreeCertificateContext(pCertContext);  
  if (FAILED(hr))  
  {  
    MQCloseQueue(hQueue);  
    return hr;  
  }  
  
  // Call MQCloseQueue to close the queue.  
  hr = MQCloseQueue(hQueue);  
  
  return hr;  
}