A C#/.NET SCEP Client

Source Code: Download Zip Archive

As you have ended up here I must assume that, like me, you have a requirement to interface with a SCEP server using .NET and you have struggled to find any helpful examples of how to achieve this. I hope that this blog entry might be of some help to the few that venture into this obscure territory.

Background

As a little bit of background (there is too much to cover here), when I began my investigations into this area it seemed that there was pretty much only one option for implementing a .NET SCEP client and that was to use the Bouncycastle library, as the CLR System.Security.Cryptography API has some glaring omissions such as representation of a PKCS#10 request or ASN.1 DER type conversion. However this didn’t turn out to be quite the option that I had hoped. Firstly, I struggled to find any examples of a SCEP client implemented using Bouncycastle. Perhaps more importantly using 3rd party libraries always come with a risk, unless you know the code inside-and-out you have to place some trust in the authors to do the right thing. When it comes to integrating with a PKI – a major part of the infrastructure that you use to secure your digital estate – that risk seemed too great and so we had two options: either audit the Bouncycastle code-base or roll our own using the stock CLR APIs. Upon initial investigation of Bouncycastle I discovered quite quickly that it is a huge code-base and so I quickly turned to investigating building our own client, after all Bouncycastle did it so I knew it was possible, how hard could it be?

As it turns out it isn’t that hard, at least once you know the problem space, which turns out to be quite a mountain to climb for the uninitiated such as myself. My hope is that, having investigated the problem space and tackled some of the major hurdles, I am in a position to help some other people out in this area and provide a shortcut worth several days’ of effort that I could have done without.

The Example

To be clear, this isn’t going to be a full introduction into the ASN.1 and PKCS standards, there is plenty of that already very easily accessible on the Internet. Rather this is a simple working example of using the System.Security.Cryptography APIs to interface with a SCEP server. The code is documented with inline and XML comments that should point you to the online reference docs needed to understand the what and the why.

The Certificate Requests

The example I’ve provided here is in the form of a VS 2013 Unit Test project (converted from VS 2010 so beware of the old school Unit Test config!). This PoC was created for the specific case of receiving PKCS#10 signing requests and forwarding them to a PKI for enrolment via SCEP. This means that we were not concerned with creating the requests programmatically. If that is what you are after then I would suggest looking at using CertEnrol or automating the OpenSSL API. There are plenty of examples of using CertEnrol via the CERTCLIENTLib and CERTENROLLib DLLs, which are available on all Windows PC OS versions since, I think, XP. A good one is here: http://blogs.msdn.com/b/alejacma/archive/2008/09/05/how-to-create-a-certificate-request-with-certenroll-and-net-c.aspx?PageIndex=3. I struggled to find a simple way of forming a PKCS#10 request with the challenge password required by most SCEP environments. At any rate, I’m wandering outside the scope of this post and I ended up creating PKCS#10 requests for my tests using Open SSL at the command line.

The SCEP Server

When developing this PoC I was working with the NDES MSCEP component that comes with Active Directory Certificate Services. There are numerous other SCEP servers available, such as OpenSSL and theoretically this PoC should work with all of them. I just felt it was worth pointing this out so that you can bear it in mind while working with the example .

Lets Get Going Already!

You will need to tweak some of the example’s settings to fit within your environment but the logic itself is sound. Start by configuring URL of the SCEP server and the subject name that is used to locate the local/client certificate for signing the SCEP enrolment request. These can both be found at the top of the Unit Test class.

The example project contains a folder named ‘SCEP Tools’ that contains the Open SSL config and batch commands to create the PKCS#10 requests needed for the tests. Once created, update the respective request string variables in each test method with the request contents and you should be good to go!

Source Code: Download Zip Archive

34 responses to “A C#/.NET SCEP Client”

  1. Hi Stephen,

    I tried your code to enroll the certificate with Windows server 2012. It is enrolling the certificate. But the issue is I am not able to receive the certificate as It is not able to decrypt it properly.

    Like

    • I came across similar issues while developing the integration. I usually found this to be due to the return payload not containing a certificate at all and instead containing an error or status code, usually just a single digit integer. This is usually indicated by a small payload being returned (i.e. the contents of the inner envelope being around 600-800 bytes). Can you check the logs on the AD CS host to see if there are any potential errors there? I say potential as I found the errors to be quite esoteric at the best of times.

      Note that it has been a little while since I dealt with this and I’ll take a proper look at the code again this evening if the above is not helpful.

      Like

  2. I’ve tested your client example and it works for me but I got some question about the GetCACert part, it returns 3 certificates too for me. In a comment in the function createEnvelopedDataPkcs7() you wrote “you will need a reliable means of determining the correct to fetch”. Being new to this I have not many ideas on how to determining that. I noticed that the 3 certificates have a property called thumbprint but none of those match with the SCEP servers thumbprint which I can see on its admin page (default server/certsrv/mscep_admin).

    The only one that works of the 3 with my SCEP server is the one with index 1 as in your example, but it would be great to know why! :)

    Like

    • If I recall correctly the three certs you are seeing are the root certificate (signing authority) followed by an intermediate certificate (i.e. your AD CA) and the issued cert. I would have thought that you should be able to compare the thumbprints to exclude the root and intermediate certs but now I am wondering if there is a better way. I suspect the ‘proper’ way to do this is to check the signature to establish the correct sequence in the chain and to identify the most derived certificate but that seems like it could be complicated. Let me have a little think about that and I’ll get back to you shortly.

      Like

    • Yeah, it looks like you want to look for an ‘Issuer’ attribute in each certificate. The issuer contains the Subject of the parent certificate so you can use that to establish the chain and therefore the most derived certificate which will be your newly enrolled cert.

      Like

  3. Thanks for your replies!

    The ‘Issuer’ attribute is the same for all three certificates. Maybe that is weird I dont know. Anyway I looked at the ‘Extensions’ attribute for each certificate and if a X509KeyUsageExtension is present I check the KeyUsages. For the certificate at index 1 (which works) the KeyUsage is ‘KeyEncipherment’, could that be something to look for when fetching the correct one?

    (If it sounds like I have no idea what I’m doing the sound is correct :) )

    Like

    • KeyUsage might actually be a reliable (enough) attribute to look at. Any root and intermediate certificates will absolutely have KeyUsage of ‘Certificate signing’ while in the vast majority of cases any certificate issued for client enrollment (i.e. through SCEP) will not. I suppose it depends on your use case. If you are enrolling devices then it is probably safe enough to say that your newly enroled certificate would have a KU or EKU of ‘Key encipherment’ or ‘Client Authentication’ while it would NOT have a KU of ‘Certificate signing’. You could potentially also check the subject as sometimes a CA will honor the requested Subject if it is present in the Request.

      Like

  4. Hi Stephen,

    having the same problem as you described. “I came across similar issues while developing the integration. I usually found this to be due to the return payload not containing a certificate at all and instead containing an error or status code, usually just a single digit integer. This is usually indicated by a small payload being returned (i.e. the contents of the inner envelope being around 600-800 bytes). Can you check the logs on the AD CS host to see if there are any potential errors there? I say potential as I found the errors to be quite esoteric at the best of times.
    Note that it has been a little while since I dealt with this and I’ll take a proper look at the code again this evening if the above is not helpful.”

    How did you solve it?

    Like

    • Hi Marcel, sorry for the late reply. IIRC this was due to an issue with the template on the CA server but I cannot remember the exact details. As I mentioned, you will need to dig through the errors on the CA logs. I’m sorry I can’t be any more help.

      Like

  5. Hi Stephen, thank you very much for the code and insights, they have been extremely helpful in adding a SCEP client functionality to a project we are working on. After some exploration and a bit of trial-and-error, we can now generate CSRs (with a challengePassword attribute) directly in C#/Bouncycastle; we would be happy to share the code with you if you’re interested.

    Like

  6. Hi Stephen, question about the certificate that we are supposed to specify for use with signing. Does this have to be the public key of the cert generated for the CSR? Or does it have to have any requirements? Can it be any public key? If it can how does the CA get the public key in order to decrypt the PKCS7 packet?

    I’m able to create a CSR and send it using this library but the problem is I’m not getting a certificate back and the event logs aren’t showing me any reason as to why. Do you know of any additional logs to dive into?

    Like

      • Apologies for not getting back to you sooner but glad to hear you got it sorted. It’s been a while since I looked at this stuff but for future reference IIRC I had a similar issue to what you describe even though the URL was correct. In the end I had to dig into the PKCS package returned from the AD CS server and there was an integer in the final package which correlated to a number in the Windows Event Log on the AD CS host. So if you find you just get back a PKCS package with a simple number as the final payload then search the Event Log for that number and you can get some further details on the error as reported by AD CS.

        Like

  7. Hi Stephen,
    Thanks for posting this SCEP client, it’s helped me move forward in my project, but I’m still a bit stuck. From the SCEP client I receive the certificate without a private key correct? How do I go about now using this certificate for SSL communications with C#’s httpListener?

    From what I understand (which could be wrong) I need to bind the certificate to the port I’m listening on, but trying to do this with the ‘netsh http add sslcert’ seems to fail because I don’t have a corresponding private key.

    Like

  8. Hi Stephen,
    I’m getting ‘Cannot find the Original signer’ when trying to get Signed certificate from Convert.ToBase64String(envelopedCmsResponse.ContentInfo.Content);
    Could you please help?

    Like

  9. Hi Stepen I’m trying to use your code in combination with Bouncy Castle to generate a self signed certificate from my code. When I try to sign the cert the code fail at this line :

    var signedContent = new System.Security.Cryptography.Pkcs.ContentInfo(encryptedMessageData); //new Oid(“1.2.840.113549.1.7.1”, “data”), encryptedMessageData);
    var signedMessage = new SignedCms(signedContent);
    signedMessage.ComputeSignature(signer); //FAIL HERE – internal.cryptography.cryptothrowhelper.windowscryptographicexception ‘keyset does not exist.’

    Can you help me ? Thank You

    Like

    • Without trying to reproduce it myself this appears to be an issue with finding the public/private key pair in the Windows certificate store. I would double check the values you are using in your code to identify the certificate (i.e. store and thumbprint).

      Failing that it could be a permissions issue maybe. If your certificate is in your user store but your code is running under a machine context then it won’t see the certificate so probably best to ensure the certificate is in the machine store.

      Finally, you should also check the ACLs on the key pair in the certificate snap-in. IIRC you can right-click on the private key to access the permissions there to make sure your application process identity can read the key.

      Please let me know if these are any help.

      Ste

      Like

  10. Thank You for the answer.
    I’m not using a Windows SCEP server and I don’t want to use a Cert from Windows Store… I need to use a public PKI signing infrastucture that use SCEP protocol, genereate a self signed certificate in my application and obtain a signed certificate from SCEP server.

    Like

    • Ok, so the theory should still be the same. Your application host machine will need to have a key pair installed in the certificate store or keychain for signing the request. It is here that the issue seems to be. The crypto signing API is not able to find the certificate specified in the signer. This could be due to the store name or the thumbprint or whatever identifier is being used to locate the key in the certificate store. I believe in *nix environments this would also be possibly caused by ACLs on the certificate so worth checking that.

      Like

      • I’m trying to modify the code of SCEPMAN project but I don’t think I’m good enough to understand all the passages … how can I share to you “my” code and have a contact with you to explain my purpose ?

        Like

  11. Hi Emanuele,

    I am one of the SCEPman developers and I also maintain the associated scepclient (https://github.com/scepman/scepclient) based on Stephen’s code. The unmodified code may already do what you want.

    If you still have an issue, you could open an issue on GitHub for SCEPclient. This allows you to paste code with Syntax Highlighting, and we (Stephen, you, me, and anybody else interested) can comment on the code and so on.

    If you cannot share your code publicly, you may open a support request for SCEPman and mention my name, so I am quickly assigned to the ticket.

    And BTW, if this is a generic kind of issue (i.e. not specific to the SCEP client) Stack Overflow (https://stackoverflow.com/) is a great resource to ask for help.

    Best,

    Christoph

    Like

    • Hi Christoph, thank you for your answer, I will contact you on git hub, and I will share the code here, when we solve my issues

      Like

  12. It’s been 5 years since I first stumbled upon this and since then I’ve written/maintained SCEP servers for the largest of corporations. It all began with you demystifying the SCEP protocol through this one simple program. Thank you.

    Like

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.