Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get the X509Certificate from a client request

I have a web-service which I secured using certificates. Now, I want to identify the client by looking at the certificate thumbprint. This means that I have a list of thumbprints on my service somewhere that are linked to some user.

Actually, my first question (a little off-topic) is: is this a good approach or should I still introduce some username password construction?

Second question is: how can I get the certificate that the client used to connect to the web-service so I can read the thumbprint at the service side.

I did read a lot about it (like this post:How do I get the X509Certificate sent from the client in web service?) but could not find an answer.

I have no HTTPContext, so that is not an option. In the post mentioned above is spoken about Context.Request.ClientCertificate.Certificate but I guess they mean the HTTPContext there as well. Also adding <serviceHostingEnvironment aspNetCompatibilityEnabled="true" /> to the web.config is also not an option.

like image 881
Bas Slagter Avatar asked Sep 23 '11 11:09

Bas Slagter


2 Answers

this is how we do this in the constructor of our webservice:

if (OperationContext.Current.ServiceSecurityContext.AuthorizationContext.ClaimSets == null)
    throw new SecurityException ("No claimset service configured wrong");

if (OperationContext.Current.ServiceSecurityContext.AuthorizationContext.ClaimSets.Count <= 0)
    throw new SecurityException ("No claimset service configured wrong");


var cert = ((X509CertificateClaimSet) OperationContext.Current.ServiceSecurityContext.
            AuthorizationContext.ClaimSets[0]).X509Certificate;

//this contains the thumbprint
cert.Thumbprint
like image 200
albertjan Avatar answered Sep 30 '22 18:09

albertjan


I don't think there is anything wrong with this approach, as long as this service is used in an environment where you can control certificate distribution and ensure they are stored securely.

Assuming this is a WCF service, you can get the certificate the client is presenting using a class that inherits from ServiceAuthorizationManager. Something like this will do the job:

public class CertificateAuthorizationManager : ServiceAuthorizationManager
{
    protected override bool CheckAccessCore(OperationContext operationContext)
    {
        if (!base.CheckAccessCore(operationContext))
        {
            return false;
        }

        string thumbprint = GetCertificateThumbprint(operationContext);

        // TODO: Check the thumbprint against your database, then return true if found, otherwise false
    }

    private string GetCertificateThumbprint(OperationContext operationContext)
    {
        foreach (var claimSet in operationContext.ServiceSecurityContext.AuthorizationContext.ClaimSets)
        {
            foreach (Claim claim in claimSet.FindClaims(ClaimTypes.Thumbprint, Rights.Identity))
            {
                string tb = BitConverter.ToString((byte[])claim.Resource);
                tb = tb.Replace("-", "");
                return tb;
            }
        }

        throw new System.Security.SecurityException("No client certificate found");
    }
}

You then need to change your configuration at the server to use this authorization manager:

<system.serviceModel>

    <behaviors>
        <serviceBehaviors>
            <behavior name="MyServerBehavior">

                <serviceAuthorization serviceAuthorizationManagerType="myNamespace.CertificateAuthorizationManager, myAssembly"/>

                ...

            </behavior>
        </serviceBehaviors>
    </behaviors>

    ...

</system.serviceModel>
like image 31
Cocowalla Avatar answered Sep 30 '22 19:09

Cocowalla