开发者

Possible to have WCF STS with username/password and client certificate?

I'm using WIF with STS. Everything works fine and the client authenticates by sending the username/password in the credentials.

We install clients at customer's sites. What I would like is for each customer to use its own certificate for its clients. The reason for that is that there's no "customer-site" validation possible. I can deactivate some user accounts but I cannot disable all clients installed at a customer at once.

If each customer had a different certificate I could revoke the certificate and no client from that customer would be able to connect anymo开发者_开发百科re.

I couldn't find any way to force the client to set its certificate to the communication. Usually this is automatic when the authentication mode is set to certificate but I need to set it to windows to be able to send the authentication.

Does anyone have an idea on how to achieve that? Or let me know if that's just not possible.

Cheers.


Sure is, you just need to do a little footwork to get this going starting with a custom security binding extension element to describe the tokens and which one should be used for signing/endorsing. For purposes of explanation I will assume you always want both the certificate and a username/password passed.

In the custom binding element, you will need to create a TransportSecurityBindingElement and add token parameters to it. There's three collections to add token parameters to: SignedEncrypted, Signed and Endorsing. For the scenario we're talking about here, I recommend adding the UserNameSecurityTokenParameters to the SignedEncrypted collection and the X509SSecurityTokenParameters to the Endorsing collection. This means that the message validity/integrity is provided by the certificate token rather than the username/password and the username/password token will be signed and encrypted by the certificate token. This will look something like the following:

public class MySecurityBindingElement : BindingElementExtensionElement
{
    public override void ApplyConfiguration(BindingElement bindingElement)
    {
        base.ApplyConfiguration(bindingElement);

        TransportSecurityBindingElement transportSecurityBindingElement = (TransportSecurityBindingElement)bindingElement;

        transportSecurityBindingElement.EndpointSupportingTokenParameters.SignedEncrypted.Add(new UserNameSecurityTokenParameters());

        transportSecurityBindingElement.EndpointSupportingTokenParameters.Endorsing.Add(new X509SecurityTokenParameters
        {
            InclusionMode = SecurityTokenInclusionMode.AlwaysToRecipient,
            ReferenceStyle = SecurityTokenReferenceStyle.Internal,
            RequireDerivedKeys = false,
            X509ReferenceStyle = X509KeyIdentifierClauseType.Any
        });         
    }

    protected override BindingElement CreateBindingElement()
    {
        TransportSecurityBindingElement result = new TransportSecurityBindingElement
        {
            IncludeTimestamp = true,
            LocalClientSettings.DetectReplays = false,
            LocalServiceSettings.DetectReplays = false
        };

        this.ApplyConfiguration(result);

        return result;
     }
}

Then, from a client perspective, you just make sure to set both the certificate and username password get set for each channel you're using to communicate with the server. You can do this at runtime by setting the properties on the Credentials property of either the ChannelFactory or a standard WCF, ClientBase proxy class. Another thing you can do is set the client certificate via an endpoint behavior like so:

<endpointBehavior>
    <behavior name="MyBehavior">
        <clientCredentials>
            <clientCertificate findValue="MySubject" x509FindType="FindBySubjectName" storeLocation="LocalMachine" storeName="My" />
        </clientCredentials>
    </behavior>
</endpointBehavior>

Done this way, you will only ever need to explicitly set the username/password at runtime.

Finally, at the STS, you can read the tokens that are specified by the user via the OperationContext::SupportingTokens property. You'll find instances of UserNameSecurityToken and X509SecurityToken in the collection and from there you can use those to authenticate the caller.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜