通过Service Fabric中的HTTPS调用WCF:请求已中止:无法创建SSL/TLS安全通道

问题描述:

我正在通过HTTPS调用WCF服务. =>证书还可以.查看屏幕快照:

I'm calling a WCF service over HTTPS. => The certificates are ok. See screenshot:

客户端证书安装在我的帐户和本地计算机下. 两者都可以导出.

The client certificates are installed under my account and local computer. Both available for export.

因此,我有一段可在控制台应用程序中工作的代码.当我在网络服务"下运行控制台应用程序时,该服务调用有效.

So I have a piece of code that works in a console application. When I run the console app under Network Service, the service call works.

当我将此代码粘贴到StatefullService(服务结构内部)中时,出现以下异常.

When I paste this code inside a StatefullService (inside service fabric) I get the following exception.

我已经在控制台应用程序和服务fabcric中验证了ServicePointManager.SecurityProtocol它是System.Net.SecurityProtocolType.Tls | System.Net.SecurityProtocolType.Tls11 | System.Net.SecurityProtocolType.Tls12.

I've verified the ServicePointManager.SecurityProtocol It's System.Net.SecurityProtocolType.Tls | System.Net.SecurityProtocolType.Tls11 | System.Net.SecurityProtocolType.Tls12 in the console application and in service fabcric.

"System.ServiceModel.Security.SecurityNegotiationException:无法为具有权限'********.cloudapp.azure.com'的SSL/TLS建立安全通道.---> System.Net.WebException:请求已中止:无法在System.Net.HttpWebRequest.GetResponse()处创建SSL/TLS安全通道.System.ServiceModel.Channels.HttpChannelFactory 1.HttpRequestChannel.HttpChannelRequest.WaitForReply(TimeSpan timeout)\r\n --- End of inner exception stack trace ---\r\n\r\nServer stack trace: \r\n at System.ServiceModel.Channels.HttpChannelUtilities.ProcessGetResponseWebException(WebException webException, HttpWebRequest request, HttpAbortReason abortReason)\r\n at System.ServiceModel.Channels.HttpChannelFactory 1.HttpRequestChannel.HttpChannelRequest.WaitForReply (TimeSpan超时)\ r \ n在System.ServiceModel.Channels.RequestChannel.Request(消息,TimeSpan超时)\ r \ n在System.ServiceModel.Dispatcher.RequestChannelBinder.Request(消息,TimeSpan超时)\ r \ n在System.ServiceModel.Channels.ServiceChannel.Call(字符串操作,布尔型单向,ProxyOperationRuntime操作,Object [] ins,Object [] outs,TimeSpan超时)在System.ServiceModel.Channels.ServiceChannelProxy.InvokeService(IMethodCallMessage methodCall ,ProxyOperationRuntime操作)\ r \ n位于System.ServiceModel.Channels.ServiceChannelProxy.Invok e(IMessage消息)\ r \ n \ r \ n在[0]处抛出异常:System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage reqMsg,IMessage retMsg)\ r \ n在System.Runtime处抛出.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgData,Int32类型)\ r \ n位于********************************* \ r \ n位于* ******************************** 在 ***************** ******.cs:行75 \ r \ n在**********中的*********************** *************.cs:第34行\ r \ n位于***中的*********************** ********************.cs:第20行"

"System.ServiceModel.Security.SecurityNegotiationException: Could not establish secure channel for SSL/TLS with authority '********.cloudapp.azure.com'. ---> System.Net.WebException: The request was aborted: Could not create SSL/TLS secure channel.\r\n at System.Net.HttpWebRequest.GetResponse()\r\n at System.ServiceModel.Channels.HttpChannelFactory1.HttpRequestChannel.HttpChannelRequest.WaitForReply(TimeSpan timeout)\r\n --- End of inner exception stack trace ---\r\n\r\nServer stack trace: \r\n at System.ServiceModel.Channels.HttpChannelUtilities.ProcessGetResponseWebException(WebException webException, HttpWebRequest request, HttpAbortReason abortReason)\r\n at System.ServiceModel.Channels.HttpChannelFactory1.HttpRequestChannel.HttpChannelRequest.WaitForReply(TimeSpan timeout)\r\n at System.ServiceModel.Channels.RequestChannel.Request(Message message, TimeSpan timeout)\r\n at System.ServiceModel.Dispatcher.RequestChannelBinder.Request(Message message, TimeSpan timeout)\r\n at System.ServiceModel.Channels.ServiceChannel.Call(String action, Boolean oneway, ProxyOperationRuntime operation, Object[] ins, Object[] outs, TimeSpan timeout)\r\n at System.ServiceModel.Channels.ServiceChannelProxy.InvokeService(IMethodCallMessage methodCall, ProxyOperationRuntime operation)\r\n at System.ServiceModel.Channels.ServiceChannelProxy.Invoke(IMessage message)\r\n\r\nException rethrown at [0]: \r\n at System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage reqMsg, IMessage retMsg)\r\n at System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgData, Int32 type)\r\n at **********************************\r\n at ********************************* in ***********************.cs:line 75\r\n at *********************** in ***********************.cs:line 34\r\n at *********************** in ***********************.cs:line 20"

代码如下



            var binding = new BasicHttpBinding();
            binding.Security.Mode = BasicHttpSecurityMode.Transport;
            binding.Security.Transport = new HttpTransportSecurity
            {
                ClientCredentialType = HttpClientCredentialType.Certificate,
            };
            string endpoint = "https://********.cloudapp.azure.com/*****/SomeService.svc";
            var endpoint1 = new EndpointAddress(endpoint);

            var factory = new ChannelFactory(binding, endpoint);

            var clientCredentials = new ClientCredentials();
            clientCredentials.ClientCertificate.SetCertificate("CN=my-cn-name",
                System.Security.Cryptography.X509Certificates.StoreLocation.LocalMachine,
                System.Security.Cryptography.X509Certificates.StoreName.My);

            if (factory.Endpoint.EndpointBehaviors.Contains(typeof(ClientCredentials)))
            {
                factory.Endpoint.EndpointBehaviors.Remove(typeof(ClientCredentials));
            }

            factory.Endpoint.EndpointBehaviors.Add(clientCredentials);

            var channel = factory.CreateChannel();

            try
            {
                var result = channel.GetData(1);

                Console.WriteLine("Success");
                Console.ReadLine();
            }
            catch (Exception e)
            {
                Console.WriteLine(e);
                Console.ReadLine();
            }

我想在服务结构中通过HTTPS调用WCF服务时缺少什么? 如果我禁用HTTPS协议并在现有WCF服务上启用HTTP协议,则可以连接.但是出于明显的原因,我们需要HTTPS.

What I'm I missing to call the WCF service over HTTPS in service fabric? If I disable the HTTPS protocol and enable the HTTP protocol on the existing WCF service i'm able to connect. But for obvious reasons we need HTTPS.

编辑1

已测试:

  1. 使用HttpClient和WebRequestHandler作为GET调用服务以接收HTML,这在控制台应用程序中也可以使用.不在服务结构中.
  2. 从我的个人商店中删除了证书.控制台应用程序由于使用本地计算机存储而保持工作状态(请参见上面的代码示例)
  3. 通过HTTPS在邮递员中进行肥皂请求是可行的.

编辑2

遵循此答案 https://serverfault.com/a/132791/135762 (适用于网络服务)工作.

Following this answer https://serverfault.com/a/132791/135762 (for network service) made it work.

问题是网络服务帐户没有正确的访问权限来访问证书的私钥.

The problem was that network service account did not have the correct access rights to access the private key of the certificate.

您可以通过两种方法解决此问题.

You can solve this in 2 ways.

第一个使用powershell和setup.bat来安装和配置证书.

在服务结构服务中,将Setup.bat添加到项目的根目录中. 编辑服务清单,然后添加

In your service fabric service add Setup.bat in the root of the project. Edit the service manifest and add a setup entry point

<ExeHost> <Program>Setup.bat</Program> <WorkingFolder>CodePackage</WorkingFolder> </ExeHost>

bat文件可能应该以增强的信任度运行.您可以在应用清单中添加以下内容 在ServiceManifestImport内部:

The bat file should probably run with elevated trust. You can add the following in the Application Manifest Inside the ServiceManifestImport:

<Policies> <RunAsPolicy CodePackageRef="Code" UserRef="SetupAdminUser" EntryPointType="Setup" /> </Policies>

在应用程序清单的底部(在xml的根目录中)添加以下内容以admin身份运行. <Principals> <Users> <User Name="SetupAdminUser"> <MemberOf> <SystemGroup Name="Administrators" /> </MemberOf> </User> </Users> </Principals>

At the bottom of the application manifest (in the root of the xml) add the following to run as admin. <Principals> <Users> <User Name="SetupAdminUser"> <MemberOf> <SystemGroup Name="Administrators" /> </MemberOf> </User> </Users> </Principals>

bat文件很简单: powershell.exe -ExecutionPolicy Bypass -Command ".\Scripts\Install-Certificates.ps1"

The bat file is simple: powershell.exe -ExecutionPolicy Bypass -Command ".\Scripts\Install-Certificates.ps1"

powershell脚本如下所示

The powershell script can look like this


$pwd = ConvertTo-SecureString -String "YourPassword" -Force -AsPlainText

function Set-CertificatePermission
{
 param
 (
    [Parameter(Position=1, Mandatory=$true)]
    $cert ,

    [Parameter(Position=2, Mandatory=$true)]
    [ValidateNotNullOrEmpty()]
    [string]$serviceAccount
 )


 # Specify the user, the permissions and the permission type
 $permission = "$($serviceAccount)","Read,FullControl","Allow"
 $accessRule = New-Object -TypeName System.Security.AccessControl.FileSystemAccessRule -ArgumentList $permission;

 # Location of the machine related keys
 $keyPath = $env:ProgramData + "\Microsoft\Crypto\RSA\MachineKeys\";
 $keyName = $cert.PrivateKey.CspKeyContainerInfo.UniqueKeyContainerName;
 $keyFullPath = $keyPath + $keyName;

 try
 {
    # Get the current acl of the private key
    $acl = (Get-Acl $keyFullPath)

    # Add the new ace to the acl of the private key
    $acl.AddAccessRule($accessRule);

    # Write back the new acl
    Set-Acl -Path $keyFullPath -AclObject $acl;
 }
 catch
 {
    throw $_;
 }
}

function Install-RootCA($path){
    Write-Host "Installing root certificate"

    Import-PfxCertificate -FilePath $path -CertStoreLocation "Cert:\LocalMachine\Root" -Password $pwd -Exportable
}

function Install-Certificate($path){
    Write-Host "Installing certificate"

     $cert = Import-PfxCertificate -FilePath $path -CertStoreLocation "Cert:\LocalMachine\My" -Password $pwd -Exportable
     Set-CertificatePermission $cert "NT AUTHORITY\NETWORK SERVICE"
}



Install-RootCA ".\Certificates\CARoot.pfx"
Install-Certificate ".\Certificates\ClientCert.pfx"

这将在受信任的根存储中安装一个证书(因为我们使用自签名证书),并在计算机帐户的个人"存储中安装一个证书. Service Fabric的重要一点是它还为网络服务访问它的私钥设置了正确的权限.

This will install a certificate in the trusted root store (because we use self signed certificates) and install a certificate in the Personal store of the computer account. The important bit for Service Fabric is it also sets correct rights on the private key for network service to access it.

第二种方法:使用mmc手动操作

  1. 按Windows键+ R
  2. 输入mmc
  3. 文件=>添加/删除卡扣
  4. 添加证书
  5. 选择计算机帐户
  6. 右键单击受信任的根证书颁发机构\证书"
  7. 导入根证书* .pfx(将其标记为可导出)
  8. 右键单击个人\证书"
  9. 导入客户端证书* .pfx(将其标记为可导出)
  10. 右键单击导入的客户端证书:所有任务=>管理私钥
  11. 将网络服务添加为用户并赋予其完全控制权
  1. Press Windows Key + R
  2. Type mmc
  3. File => Add / Remove Snappin
  4. Add Certificates
  5. Choose computer account
  6. Right click Trusted Root Certification Authorities\Certificates
  7. Import the root certificate *.pfx (Mark it as exportable)
  8. Right click Personal\Certificates
  9. Import the client certificate *.pfx (Mark it as exportable)
  10. Right click on the imported client certificate: All tasks => Manage Private Keys
  11. Add Network Service as a user and give it full control