// Populates the client certificate. The result may be null if there is no client cert. // TODO: Does it make sense for this to be invoked multiple times (e.g. renegotiate)? Client and server code appear to // enable this, but it's unclear what Http.Sys would do. public async Task <X509Certificate2> GetClientCertificateAsync(CancellationToken cancellationToken = default(CancellationToken)) { if (SslStatus == SslStatus.Insecure) { // Non-SSL return(null); } // TODO: Verbose log if (_clientCert != null) { return(_clientCert); } cancellationToken.ThrowIfCancellationRequested(); ClientCertLoader certLoader = new ClientCertLoader(RequestContext, cancellationToken); try { await certLoader.LoadClientCertificateAsync().SupressContext(); // Populate the environment. if (certLoader.ClientCert != null) { _clientCert = certLoader.ClientCert; } // TODO: Expose errors and exceptions? } catch (Exception) { if (certLoader != null) { certLoader.Dispose(); } throw; } return(_clientCert); }
private static unsafe void IOCompleted(ClientCertLoader asyncResult, uint errorCode, uint numBytes) { RequestContext requestContext = asyncResult.RequestContext; try { if (errorCode == UnsafeNclNativeMethods.ErrorCodes.ERROR_MORE_DATA) { // There is a bug that has existed in http.sys since w2k3. Bytesreceived will only // return the size of the initial cert structure. To get the full size, // we need to add the certificate encoding size as well. UnsafeNclNativeMethods.HttpApi.HTTP_SSL_CLIENT_CERT_INFO *pClientCertInfo = asyncResult.RequestBlob; asyncResult.Reset(numBytes + pClientCertInfo->CertEncodedSize); uint bytesReceived = 0; errorCode = UnsafeNclNativeMethods.HttpApi.HttpReceiveClientCertificate( requestContext.RequestQueueHandle, requestContext.Request.ConnectionId, (uint)UnsafeNclNativeMethods.HttpApi.HTTP_FLAGS.NONE, asyncResult._memoryBlob, asyncResult._size, &bytesReceived, asyncResult._overlapped); if (errorCode == UnsafeNclNativeMethods.ErrorCodes.ERROR_IO_PENDING || (errorCode == UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS && !WebListener.SkipIOCPCallbackOnSuccess)) { return; } } if (errorCode == UnsafeNclNativeMethods.ErrorCodes.ERROR_NOT_FOUND) { // The client did not send a cert. asyncResult.Complete(0, null); } else if (errorCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS) { asyncResult.Fail(new WebListenerException((int)errorCode)); } else { UnsafeNclNativeMethods.HttpApi.HTTP_SSL_CLIENT_CERT_INFO *pClientCertInfo = asyncResult._memoryBlob; if (pClientCertInfo == null) { asyncResult.Complete(0, null); } else { if (pClientCertInfo->pCertEncoded != null) { try { byte[] certEncoded = new byte[pClientCertInfo->CertEncodedSize]; Marshal.Copy((IntPtr)pClientCertInfo->pCertEncoded, certEncoded, 0, certEncoded.Length); asyncResult.Complete((int)pClientCertInfo->CertFlags, new X509Certificate2(certEncoded)); } catch (CryptographicException exception) { // TODO: Log asyncResult.Fail(exception); } catch (SecurityException exception) { // TODO: Log asyncResult.Fail(exception); } } } } } catch (Exception exception) { asyncResult.Fail(exception); } }