servicestack,client-certificates,mutual-authentication,C#,servicestack,Client Certificates,Mutual Authentication" /> servicestack,client-certificates,mutual-authentication,C#,servicestack,Client Certificates,Mutual Authentication" />

C# 如何使用ServiceStack中的证书对客户端进行身份验证?

C# 如何使用ServiceStack中的证书对客户端进行身份验证?,c#,servicestack,client-certificates,mutual-authentication,C#,servicestack,Client Certificates,Mutual Authentication,我正在探索使用ServiceStack作为WCF的替代方案。我的要求之一是服务器和客户机必须使用证书进行相互身份验证。客户端是一个服务,因此我不能使用任何涉及用户输入的身份验证类型。客户机还需要能够使用mono在Linux上运行,这样Windows身份验证就不存在了 我已使用netsh.exe将服务器证书绑定到服务器的端口,验证客户端是否正在获取服务器证书,以及是否正在使用wireshark对数据进行加密。然而,我一辈子都搞不清楚如何配置服务器以要求客户端证书 有些人建议使用请求过滤器来验证客户

我正在探索使用ServiceStack作为WCF的替代方案。我的要求之一是服务器和客户机必须使用证书进行相互身份验证。客户端是一个服务,因此我不能使用任何涉及用户输入的身份验证类型。客户机还需要能够使用mono在Linux上运行,这样Windows身份验证就不存在了

我已使用netsh.exe将服务器证书绑定到服务器的端口,验证客户端是否正在获取服务器证书,以及是否正在使用wireshark对数据进行加密。然而,我一辈子都搞不清楚如何配置服务器以要求客户端证书

有些人建议使用请求过滤器来验证客户端证书,但这似乎效率很低,因为每个请求都会检查客户端证书。性能是一个非常重要的优先事项。创建自定义IAuthProvider似乎很有希望,但所有文档和示例都面向在某些点上涉及用户交互的身份验证类型,而不是证书

是否可以使用证书通过自托管ServiceStack服务对客户端和服务器进行相互身份验证?

这是我的测试服务供参考

public class Host : AppHostHttpListenerBase
{
    public Host()
        : base("Self-hosted thing", typeof(PutValueService).Assembly)
    {
        //TODO - add custom IAuthProvider to validate the client certificate?
        this.RequestFilters.Add(ValidateRequest);

        //add protobuf plugin
        //https://github.com/ServiceStack/ServiceStack/wiki/Protobuf-format
        Plugins.Add(new ProtoBufFormat());

        //register protobuf
        base.ContentTypeFilters.Register(ContentType.ProtoBuf,
                (reqCtx, res, stream) => ProtoBuf.Serializer.NonGeneric.Serialize(stream, res),
                ProtoBuf.Serializer.NonGeneric.Deserialize);
    }

    public override void Configure(Funq.Container container)
    {}

    void ValidateRequest(IHttpRequest request, IHttpResponse response, object dto)
    {
        //TODO - get client certificate?
    }
}

[DataContract]
[Route("/putvalue", "POST")]
//dto
public class PutValueMessage : IReturnVoid
{
    [DataMember(Order=1)]
    public string StreamID { get; set; }

    [DataMember(Order=2)]
    public byte[] Data { get; set; }
}

//service
public class PutValueService : Service
{
    public void Any(PutValueMessage request)
    {
        //Comment out for performance testing

        Console.WriteLine(DateTime.Now);
        Console.WriteLine(request.StreamID);
        Console.WriteLine(Encoding.UTF8.GetString(request.Data));
    }
}
有些人建议使用请求过滤器来验证客户端证书,但这似乎效率很低,因为每个请求都会检查客户端证书。性能是一个非常重要的优先事项

REST是无状态的,因此如果您不愿意在每个请求上检查客户端证书,则需要提供一个替代身份验证令牌,以显示已提供的有效身份

因此,如果在对客户端证书进行身份验证后,向客户端提供了一个会话Id cookie,而该cookie可以进行验证,则可以避免在后续请求中检查证书

然而,我一辈子都搞不清楚如何配置服务器以要求客户端证书

客户端证书仅在原始http请求对象上可用,这意味着您必须强制转换请求对象才能访问此值。下面的代码用于将请求强制转换为自托管应用程序使用的
ListenerRequest

服务器进程: 请求筛选器将检查:

  • 首先是有效的会话cookie,如果有效,将允许请求而无需进一步处理,因此不需要在后续请求中验证客户端证书

  • 如果未找到有效会话,筛选器将尝试检查客户端证书的请求。如果它存在,请尝试根据某些条件匹配它,并在接受后为客户端创建一个会话,然后返回一个cookie

  • 如果客户端证书不匹配,则引发授权异常

GlobalRequestFilters.Add((req,res,requestDto)=>{
//检查会话cookie
常量字符串cookieName=“auth”;
var sessionCookie=req.GetCookieValue(cookieName);
if(sessionokie!=null)
{
//尝试使用会话cookie进行身份验证
var cache=req.GetCacheClient();
var session=cache.Get(sessioncokie);
if(session!=null&&session.Expires>DateTime.Now)
{
//会话有效并允许请求
返回;
}
}
//回退到检查客户端证书
var originalRequest=req.originalRequest作为ListenerRequest;
if(原始请求!=null)
{
//从请求中获取证书
var certificate=originalRequest.HttpRequest.GetClientCertificate();
/*
*检查证书是否有效
*(此处替换为您自己的支票)
*您可以通过检查已知证书序列号或公钥等的数据库来实现这一点。
* 
*如果需要访问数据库,可以从容器中解析
*var db=HostContext.TryResolve();
*/
bool isValid=certificate!=null&&certificate.SerialNumber==“XXXXXXXXXXXXXX”;
//处理有效证书
如果(有效)
{
//为用户创建会话
var sessionId=SessionExtensions.CreateRandomBase64Id();
var expiration=DateTime.Now.AddHours(1);
var session=新的MySession{
Id=会话Id,
名称=证书。主题名称,
ClientCertificateSerialNumber=certificate.SerialNumber,
过期=过期
};
//将会话添加到缓存中
var cache=req.GetCacheClient();
Add(sessionId,session);
//设置会话cookie
res.SetCookie(cookieName、sessionId、expiration);
//允许请求
返回;
}
}
//没有有效的会话cookie或客户端证书
抛出新的HttpError(System.Net.HttpStatusCode.Unauthorized,“401”,“需要有效的客户端证书或会话”);
});
这使用了一个名为
MySession
的自定义会话类,您可以根据需要将其替换为自己的会话对象

公共类MySession
{
公共字符串Id{get;set;}
公共日期时间过期{get;set;}
公共字符串名称{get;set;}
公共字符串ClientCertificateSerialNumber{get;set;}
}
客户端进程: 客户机需要设置其客户机证书以随请求一起发送

var-client=新的JsonServiceClient(“https://servername:port/");
client.RequestFilter+=(httpReq)=>{
var certificate=…//加载客户端证书
httpReq.ClientCertificates.Add(证书);
};
一旦您向服务器发出第一个请求,您的客户端将收到会话Id cookie,并且您可以选择删除客户端Id cookie