在 Service Fabric 中通过 HTTPS 调用 WCF:请求已中止:无法创建 SSL/TLS 安全通道
Calling WCF over HTTPS in Service Fabric : The request was aborted: Could not create SSL/TLS secure channel
我正在通过 HTTPS 调用 WCF 服务。
=> 证书没问题。见截图:
客户端证书安装在我的帐户和本地计算机下。
两者均可出口。
所以我有一段代码可以在控制台应用程序中运行。当我 运行 网络服务下的控制台应用程序时,服务调用有效。
当我将此代码粘贴到 StatefullService 中(在服务结构中)时,出现以下异常。
我已经在控制台应用程序和服务 fabcric 中验证了 ServicePointManager.SecurityProtocol
它是 System.Net.SecurityProtocolType.Tls | System.Net.SecurityProtocolType.Tls11 | System.Net.SecurityProtocolType.Tls12
。
"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.HttpChannelFactory
1.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。
编辑 1
已测试:
- 使用 HttpClient 和 WebRequestHandler 作为 GET 调用服务以接收 HTML 这也适用于控制台应用程序。不在服务结构中。
- 已从我的个人存储中删除证书。控制台应用程序继续工作,因为它使用本地机器存储(参见上面的代码示例)
- 通过 HTTPS 在邮递员中执行 soap 请求。
编辑 2
遵循这个答案https://serverfault.com/a/132791/135762(对于网络服务)使其工作。
问题是网络服务帐户没有访问证书私钥的正确访问权限。
您可以通过两种方式解决此问题。
第一个使用 powershell 和 setup.bat 安装和配置证书。
在您的服务结构服务中,在项目的根目录中添加 Setup.bat。
编辑 服务清单 并添加 setup entry point
<ExeHost> <Program>Setup.bat</Program> <WorkingFolder>CodePackage</WorkingFolder> </ExeHost>
bat 文件可能 运行 具有较高的信任度。您可以在 Application Manifest 中添加以下内容
在 ServiceManifestImport 内部:
<Policies> <RunAsPolicy CodePackageRef="Code" UserRef="SetupAdminUser" EntryPointType="Setup" /> </Policies>
在应用程序清单的底部(在 xml 的根目录中)以管理员身份将以下内容添加到 运行。
<Principals> <Users> <User Name="SetupAdminUser"> <MemberOf> <SystemGroup Name="Administrators" /> </MemberOf> </User> </Users> </Principals>
bat文件很简单:
powershell.exe -ExecutionPolicy Bypass -Command ".\Scripts\Install-Certificates.ps1"
powershell 脚本可能如下所示
$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 的重要一点是它还为网络服务访问它的私钥设置了正确的权限。
第二种方法:使用mmc的manuel
- 按Windows键+R
- 类型 mmc
- 文件 => 添加/删除 Snappin
- 添加证书
- 选择计算机帐户
- 右键单击受信任的根证书Authorities\Certificates
- 导入根证书*.pfx(标记为可导出)
- 右击Personal\Certificates
- 导入客户端证书*.pfx(标记为可导出)
- 右键单击导入的客户端证书:所有任务 => 管理私钥
- 将网络服务添加为用户并赋予其完全控制权
有第三种方法比上述方法更简单,应该适用于 WCF。如果此服务在网络服务帐户下 运行ning,那么您可以像这样在服务端点声明中放置 "protocol=https",这将执行 acling;
<Resources>
<Endpoints>
<Endpoint Name="ServiceEndpoint" Protocol="https" />
</Endpoints>
您可以选择 return 给客户端的证书,并 运行 此服务在不同的安全权限下。详情请看这里 https://docs.microsoft.com/en-us/azure/service-fabric/service-fabric-application-runas-security#assign-a-security-access-policy-for-http-and-https-endpoints
我正在通过 HTTPS 调用 WCF 服务。
=> 证书没问题。见截图:
客户端证书安装在我的帐户和本地计算机下。 两者均可出口。
所以我有一段代码可以在控制台应用程序中运行。当我 运行 网络服务下的控制台应用程序时,服务调用有效。
当我将此代码粘贴到 StatefullService 中(在服务结构中)时,出现以下异常。
我已经在控制台应用程序和服务 fabcric 中验证了 ServicePointManager.SecurityProtocol
它是 System.Net.SecurityProtocolType.Tls | System.Net.SecurityProtocolType.Tls11 | System.Net.SecurityProtocolType.Tls12
。
"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.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 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。
编辑 1
已测试:
- 使用 HttpClient 和 WebRequestHandler 作为 GET 调用服务以接收 HTML 这也适用于控制台应用程序。不在服务结构中。
- 已从我的个人存储中删除证书。控制台应用程序继续工作,因为它使用本地机器存储(参见上面的代码示例)
- 通过 HTTPS 在邮递员中执行 soap 请求。
编辑 2
遵循这个答案https://serverfault.com/a/132791/135762(对于网络服务)使其工作。
问题是网络服务帐户没有访问证书私钥的正确访问权限。
您可以通过两种方式解决此问题。
第一个使用 powershell 和 setup.bat 安装和配置证书。
在您的服务结构服务中,在项目的根目录中添加 Setup.bat。 编辑 服务清单 并添加 setup entry point
<ExeHost> <Program>Setup.bat</Program> <WorkingFolder>CodePackage</WorkingFolder> </ExeHost>
bat 文件可能 运行 具有较高的信任度。您可以在 Application Manifest 中添加以下内容 在 ServiceManifestImport 内部:
<Policies> <RunAsPolicy CodePackageRef="Code" UserRef="SetupAdminUser" EntryPointType="Setup" /> </Policies>
在应用程序清单的底部(在 xml 的根目录中)以管理员身份将以下内容添加到 运行。
<Principals> <Users> <User Name="SetupAdminUser"> <MemberOf> <SystemGroup Name="Administrators" /> </MemberOf> </User> </Users> </Principals>
bat文件很简单:
powershell.exe -ExecutionPolicy Bypass -Command ".\Scripts\Install-Certificates.ps1"
powershell 脚本可能如下所示
$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 的重要一点是它还为网络服务访问它的私钥设置了正确的权限。
第二种方法:使用mmc的manuel
- 按Windows键+R
- 类型 mmc
- 文件 => 添加/删除 Snappin
- 添加证书
- 选择计算机帐户
- 右键单击受信任的根证书Authorities\Certificates
- 导入根证书*.pfx(标记为可导出)
- 右击Personal\Certificates
- 导入客户端证书*.pfx(标记为可导出)
- 右键单击导入的客户端证书:所有任务 => 管理私钥
- 将网络服务添加为用户并赋予其完全控制权
有第三种方法比上述方法更简单,应该适用于 WCF。如果此服务在网络服务帐户下 运行ning,那么您可以像这样在服务端点声明中放置 "protocol=https",这将执行 acling;
<Resources>
<Endpoints>
<Endpoint Name="ServiceEndpoint" Protocol="https" />
</Endpoints>
您可以选择 return 给客户端的证书,并 运行 此服务在不同的安全权限下。详情请看这里 https://docs.microsoft.com/en-us/azure/service-fabric/service-fabric-application-runas-security#assign-a-security-access-policy-for-http-and-https-endpoints