使用Service Fabric ServicePartitionClient发生SSL错误

时间:2018-02-17 00:58:08

标签: c# ssl azure-service-fabric

我们有一个单件服务结构服务,需要与分区服务进行通信,这两个服务都在基于证书的身份验证的安全集群中运行。我们正在使用ServicePartitionClient进行讨论。以下是ICommunicationClient所要求的ICommunicationClientFactoryServicePartitionClient实施的简化版本。

客户:

public class HttpCommunicationClient : ICommunicationClient
{
    // Lots of fields omitted for simplicity.

    public HttpCommunicationClient(HttpClientWrapper client, Uri baseAddress)
    {
        this.HttpClient = client;
        this.BaseAddress = baseAddress;
    }

    public Uri BaseAddress { get; set; }

    // Wraps System.Net.Http.HttpClient to do stuff like add default headers
    public HttpClientWrapper HttpClient { get; }

    public ResolvedServicePartition ResolvedServicePartition { get; set; }

    public string ListenerName { get; set; }

    public ResolvedServiceEndpoint Endpoint { get; set; }

    public Uri GetUri(string relativeUri)
    {
        return new Uri(this.BaseAddress, relativeUri);
    }
}

工厂:

// Note that this base class is under the
// Microsoft.ServiceFabric.Services.Communication.Client namespace,
// it's not something we wrote
public class HttpCommunicationClientFactory : 
    CommunicationClientFactoryBase<HttpCommunicationClient>
{
    // Lots of fields omitted for simplicity.

    private readonly HttpClientWrapper client;

    public HttpCommunicationClientFactory(
        ServicePartitionResolver resolver,
        IEnumerable<IExceptionHandler> exceptionHandlers)
        : base(resolver, exceptionHandlers, null)
    {
        // There's a bunch of other args that are omitted for clarity.
        this.client = new HttpClientWrapper();
    }

    protected override Task<HttpCommunicationClient> CreateClientAsync(
        string endpoint,
        CancellationToken cancellationToken)
    {
        HttpCommunicationClient client = new HttpCommunicationClient(
            this.client,
            new Uri(endpoint));

        if (this.ValidateClient(endpoint, client))
        {
            return Task.FromResult(client);
        }
        else
        {
            throw new ArgumentException();
        }
    }

    protected override bool ValidateClient(HttpCommunicationClient client)
    {
        // Not much to validate on httpclient 
        return true;
    }

    protected override bool ValidateClient(
        string endpoint,
        HttpCommunicationClient client)
    {
        return !string.IsNullOrWhiteSpace(endpoint);
    }

    protected override void AbortClient(HttpCommunicationClient client)
    {
    }
}

这是一个如何构建和使用所有内容的例子:

internal class FooFabricClient : IDisposable
{
    // Lots of fields omitted for simplicity.

    private readonly HttpCommunicationClientFactory clientFactory;

    private readonly ServicePartitionClient<HttpCommunicationClient> partitionClient;

    public FooFabricClient(Uri fabricUri, ServicePartitionKey partitionKey = null)
    {
        this.clientFactory = new HttpCommunicationClientFactory(
            ServicePartitionResolver.GetDefault());
        this.partitionClient = new ServicePartitionClient<HttpCommunicationClient>(
            this.clientFactory,
            fabricUri,
            partitionKey,
            retrySettings: new OperationRetrySettings());
    }

    public async Task<Foo> GetFooAsync()
    {
        return await this.partitionClient.InvokeWithRetryAsync(async (client) =>
        {
            // Note: See above for actual GetUri() implementation and context,
            // but this will give us a Uri composed of the client's BaseAddress
            // (passed in during HttpCommunicationClientFactory.CreateClientAsync())
            // followed by "/foo"
            Uri requestUri = client.GetUri("foo");
            return await client.HttpClient.GetAsync<Foo>(requestUri);
        });
    }

现在,问题在于,当我致电GetFooAsync()时,会抛出一个异常说:

  

无法为SSL / TLS安全通道建立信任关系。 ---&GT; System.Security.Authentication.AuthenticationException:根据验证过程,远程证书无效。

经过一些调试后,我们发现这很可能是因为我们将内部服务结构IP地址(例如10.0.0.4)作为HttpCommunicationClient.BaseAddress获取,因此当我们进行API调用时服务器证书不会对请求中的域进行验证。作为临时修复,我们在呼叫服务中完成了以下操作:

ServicePointManager.ServerCertificateValidationCallback += (a, b, c, d) => true;

当然,在验证服务器证书时,我们不是只是盲目地说“是的,看起来不错”,那么我们应该如何解决这个问题呢?是否有另一种方法来设置客户端,以便他们可以在请求上正确验证服务器证书而无需我们自己的回调?或者我们只需要在配置中添加可接受的指纹,并与ServicePointManager回调中的那些或其他内容进行比较?

0 个答案:

没有答案