PowerShell PKI Module v3.2.7 is out!

Hello world! Last time (year or so) I was busy on anything else but my module. Now I’m happy to announce that the project isn’t died, it is alive and new version is published.

This version doesn’t bring new commands, nor deprecate any. I think, command list is well-established and I don’t see anything useful to add. People doesn’t ask either. However there are things to work with code: refactor, optimize, make it cleaner and so on. Let’s look at what I’ve done here:

PowerShell Gallery

  • Moved sources from CodePlex to GitHub

Initially, project was hosted at CodePlex which is died now. I moved all my sources to GitHub, documentation to my web site and used CodePlex as module download place.

  • Moved binaries to PowerShell Gallery

Since CodePlex is done, the only real option to ship binaries was to use PowerShell Gallery. It is something new to me (I never used it till today) and was a bit lost there. But it appeared more easier than I thought. Starting with v3.2.7, the module is available on PowerShell Gallery: PSPKI. Please, provide feedback on your experience with getting PowerShell PKI module from gallery.

  • Deprecated MSI Installer

In the past, I used MSI installer to ship the module. It is still very good option to do that, because you can use various tools, like group policies or ConfigMgr to deploy the module within organization. Thanks to Caphyon Advanced Installer and their free NFR license (as a part of my Microsoft MVP award) I was able to do that. And their tool was really great and easy to use. However, my MVP award options are uncertain and PowerShell Gallery is an acceptable tradeoff, so there is no big need in MSI anymore.

Fixed bugs:

I’m working on:

  • Add managed PKCS#1 and PKCS#8 support in Cryptography namespace.
  • Redesign output for Get-IssuedRequest, Get-PendingRequest, Get-FailedRequest and Get-RevokedRequest commands.

When I initially developed these commands I simply mapped CA database columns as properties in output object:

RequestID             : 1944
Request.RequesterName : CONTOSO\Administrator

And some columns have dots in names. As the result, you cannot easily access these properties, e.g. $row.Request.RequesterName. PowerShell don’t expect Request.RequesterName as a single property. Instead, it expects a property called Request and another property (in underlying object) with RequesterName name. In order to access this property you have to do ugly things like $row."Request.RequesterName". I didn’t bother myself with quick fix (simply replace dots with underline character) and lost time. Now I can’t simply change it, because too many scripts will be broken. Currently I see two options for this:

  • make a breaking change and replace these dots with underscore character and leave the rest as is.
  • make “Properties” property in row object as a dictionary where you can access particular properties this way:$row.Properties["Request.RequesterName"] or $row.Properties["Request_RequesterName]". Similarly like you access AD properties in ADSI objects.

I haven’t decided which one to use and would like to hear your opinions as well.

    I’m struggling with:

    I’m trying to develop universal generic ASN.1 tree/tree node class to easily traverse ASN nodes and consume it everywhere with an ability to extend it via interfaces. This is blocking issue for me, as I can’t move forward with ASN.1 Editor and PKI.Core.dll library.

    • Namespaces in PKI.Core.dll library

    PKI.Core.dll is main underlying library I wrote to support PSPKI functionality. The library is relatively large, there are tons of classes and they are used even outside of PSPKI module. The problem is with authoring and bad design. When I started this project I didn’t follow conventions/guidelines and selected wrong namespaces. For example, I added a lot of stuff in System.Security.Cryptography namespace which belongs to Microsoft. As more I learn as more difficult issues I face in design. I may need to change, but this will lead to total mess for module/library users, who will be forced to rewrite a lot of code to fix these namespaces. I don’t see any other option except to abandon everything and start from scratch. Make something like “reload”. This is very hard decision and I have no idea how to deal with these huge design mistakes I made in the past. If you have ideas, suggestions how to solve these design issues, do not hesitate to reach me.

    Download

    Where to get the module now?

    Comments:

    Jordan
    Jordan 05.02.2018 12:03 (GMT+2)

    Hello Vadims,

    Nice to see a new version :) And even nicer to be able to install it from PowerShellGet!

    As for your requests for feedback, in a general manner please try not to break backwards compatibility. As you know, your library and module are used in professional environments and having to change code when upgrading to newer versions is often very complicated.

    For your first topic about properties names, I would go with the same as for ADSI: adding a Properties dictionnary property and not touch any of the existing evil-looking property names containing a dot as they are being used already in many places. And in such a dictionnary, replacing dots with underscores does not make much sense I guess and might even confuse users that are already used to the dots.

    As for the namespace problem in the library, I'm not sure this is such a very big deal that absolutely requires a fix. I may be missing something of course but even if this is bad design I don't see that myself as a real issue. That being said, if you do want to change it, what I'd suggest is the following:

    • Starting from next 3.x version, move everything to proper namespaces you own but keep the existing classes for BC.
    • To avoid duplicating code, just make the current class in the wrong namespace inherit from the new one in the correct one and move all the actual code to the new class. This works only for non-final classes of course... For those (if you have some), you will have to duplicate the code I'm afraid.
    • Tag the wrongly namespaced class as deprecated (if possible writing some deprecation message on the debug stream if you use it). From there on, clearly list all deprecations somewhere on your website and the estimated date of removal, which should occur at the next major version of the library/module (i.e. v4.0).

    That would leave your existing users some time to upgrade all their existing scripts.

    Apart from that, if I may make some (unordered) feature requests that come to my mind just now:

    • Support for exporting/importing/creating from scratch/modifying/removing certificate templates. This remains one of the very few things that I could not make into a script (few tests I did never worked).
    • Abstracting the OCSP responder CertAdm.OCSPAdmin COM class into .NET classes or even better into PowerShell cmdlets. If you have some way of creating/managing an OCSP array and force synchronization of its members of revocation data, I'd be interested (I did not find any such method in the COM classes, only in the MMC snap-in).
    • Officially support managing certificate managers and enrolment agents ACLs (you already provided some code snippets to do it but this never made it to the library).
    • Fix the CRL sign method which never worked for now
    • Implement a Get-RDNAttribute command (or at least a method in the library). I copied yours from an old blog post or forum article but having it officially would be better
    • Change Approve and Deny-CertificateRequest so that it does not write to the host directly but instead outputs an object. I had a hard time working around that to get the status of those commands
    • Implement an equivalent of "certreq -sign" for CSRs (all types of them, embedding them in PKCS#7 if required for PKCS#10 for example).
    • That's maybe something I do wrong but I never managed to retrieve with Get-DatabaseRow a specific attribute or extension for a particular request ID (filtered by the attribute name or extension OID). Everytime I tried it either retrieved me either all attributes/extensions unfiltered for the request, or none at all.

    Best regards,

    Jordan

    Vadims Podāns
    Vadims Podāns 05.02.2018 14:02 (GMT+2)

    Hi Jordan, thanks for your input!

    > in a general manner please try not to break backwards compatibility

    exactly what I concern most! From what I remember, the module download rate on CodePlex was about ~3k/year.

    > I would go with the same as for ADSI: adding a Properties dictionnary property

    Ok, the idea with Properties dictionary and keeping existing property set sounds good. Dictionary will be recommended for any new scripts and existing property generation will be marked as obsolete. Existing behavior will not be changed or updated in future.

    > As for the namespace problem in the library, I'm not sure this is such a very big deal that absolutely requires a fix

    It will be an issue in future. Even now, some .NET code starts to overlap with my code. For example, .NET Core introduced CertificateRequest class. Hopefully, they didn't name it X509CertificateRequest, because it would totally overlap with my class. Ok, since it is in X509Certificates namespace, it is not a big deal. But I have a lot of stuff in X509Certificates namespace and if names+namespaces will match, then this can raise issues.

    > Starting from next 3.x version, move everything to proper namespaces you own but keep the existing classes for BC.

    I don't think it will work reliably, because when there are multiple classes with the same name in GAC, you have explicitly specify which one you want to use in usings section. I thought about this option, but apparently it will be a total mess.

    > To avoid duplicating code, just make the current class in the wrong namespace inherit from the new one in the correct one and move all the actual code to the new class

    interesting approach. I will look into it. Final/sealed classes -- IIRC, they are only in System.Security.Cryptography.X509Certificates namespace.

    > Tag the wrongly namespaced class as deprecated (if possible writing some deprecation message on the debug stream if you use it). From there on, clearly list all deprecations somewhere on your website and the estimated date of removal, which should occur at the next major version of the library/module (i.e. v4.0).

    sounds worth to look at this.

    > Support for exporting/importing/creating from scratch/modifying/removing certificate templates

    I thought about this. However I had a discussion with Vic Heller (Windows PKI team) on this. They were very strict on this and they do not appreciate certificate template modification outside of Certificate Templates MMC. And anyway, this will require a hell of work to mimic MMC behavior. There is one COM interface which allows template modification, but haven't tested it much and whether it performs checks when setting values.

    > Abstracting the OCSP responder CertAdm.OCSPAdmin COM class into .NET classes or even better into PowerShell cmdlets

    can you add an issue to GitHub (pkix.net project)?

    >  Officially support managing certificate managers and enrolment agents ACLs (you already provided some code snippets to do it but this never made it to the library).

    Did I? I may need to look at my records if I've ever worked on this. If I had something, then please, add an issue to GitHub (PSPKI project).

    > Fix the CRL sign method which never worked for now

    it is already posted: https://github.com/Crypt32/pkix.net/issues/8. At the moment, the issue is with hashing and signing algorithm selection. I upgraded my coding skills and now it should be certainly doable when I refactor signing routines I have in ManagedAPI namespace.

    > Implement a Get-RDNAttribute command (or at least a method in the library). I copied yours from an old blog post or forum article but having it officially would be better

    I considered this in the past. There are some APIs in unmanaged CryptoAPI functions, but I would like to avoid this and write pure managed code. Decoder can be implemented pretty easy. However, if there is a need to have X.500 name generator in ASN, then the job will be signifcantly complicated, because there are a lot of constraints, restrictions for each attribute. Some are defined in RFC5280, some in other RFCs to stay conformant with standard. This functionality requires additional research. But decoder for existing X.500 name can be done easily.

    > Change Approve and Deny-CertificateRequest so that it does not write to the host directly but instead outputs an object

    File an issue on GitHub (PSPKI project). I think, I could create some generic OperationStatus class with HResult and Message properties and fire this object in output.

    > Implement an equivalent of "certreq -sign" for CSRs (all types of them, embedding them in PKCS#7 if required for PKCS#10 for example).

    and what does this command? Adds external signatures (enroll on behalf of)? If so, then I can start to work on this only after I fix X509CRL2.Build method and get managed PKCS#7 generation code. There is SignedCms class in .NET, which should work with PKCS#7 messages. However it doesn't work very well and signing operations (built-in Sign method) produces only SHA1 signature.

    > That's maybe something I do wrong but I never managed to retrieve with Get-DatabaseRow a specific attribute or extension for a particular request ID (filtered by the attribute name or extension OID). Everytime I tried it either retrieved me either all attributes/extensions unfiltered for the request, or none at all.

    Can you provide additional details on GitHub? I don't know what is wrong there, because the code is unified with other DB reader functions.

     

    Since I got more time to work on PKI module, I can make some progress faster. Since I deprecated MSI installer, I can ship updates to the module much faster even with just minor updates.

     

    Jordan
    Jordan 05.02.2018 18:48 (GMT+2)

    Thank you for the quick feedback!

    >> As for the namespace problem in the library, I'm not sure this is such a very big deal that absolutely requires a fix

    > It will be an issue in future. Even now, some .NET code starts to overlap with my code. For example, .NET Core introduced CertificateRequest class. Hopefully, they didn't name it X509CertificateRequest, because it would totally overlap with my class. Ok, since it is in X509Certificates namespace, it is not a big deal. But I have a lot of stuff in X509Certificates namespace and if names+namespaces will match, then this can raise issues.

    I did not think of that but I can see this is definitely a problem-to-be yes...

    >> Starting from next 3.x version, move everything to proper namespaces you own but keep the existing classes for BC.

    > I don't think it will work reliably, because when there are multiple classes with the same name in GAC, you have explicitly specify which one you want to use in usings section. I thought about this option, but apparently it will be a total mess.

    OK yes I see the problem here... at least if writing .NET code. In PowerShell (which is all I'm concerned about to be honest) you already have to specify the FQDN of each class anyway. But OK clearly not as easy to fix as I thought...

    >> Support for exporting/importing/creating from scratch/modifying/removing certificate templates

    > I thought about this. However I had a discussion with Vic Heller (Windows PKI team) on this. They were very strict on this and they do not appreciate certificate template modification outside of Certificate Templates MMC. And anyway, this will require a hell of work to mimic MMC behavior. There is one COM interface which allows template modification, but haven't tested it much and whether it performs checks when setting values.

    I'm not surprised... I don't understand why they implemented everything only in the MMC instead of having the MMC rely on some well documented COM classes... I tried your export/import proposal from this blog post but did not managed to make it work :/ Didn't have much time to investigate though.

    >> Abstracting the OCSP responder CertAdm.OCSPAdmin COM class into .NET classes or even better into PowerShell cmdlets

    > can you add an issue to GitHub (pkix.net project)?

    Done: https://github.com/Crypt32/pkix.net/issues/11

    >>  Officially support managing certificate managers and enrolment agents ACLs (you already provided some code snippets to do it but this never made it to the library).

    > Did I? I may need to look at my records if I've ever worked on this. If I had something, then please, add an issue to GitHub (PSPKI project).

    Feature request posted to Github here (with link to your previous work): https://github.com/Crypt32/pkix.net/issues/12

    >> Implement a Get-RDNAttribute command (or at least a method in the library). I copied yours from an old blog post or forum article but having it officially would be better

    > I considered this in the past. There are some APIs in unmanaged CryptoAPI functions, but I would like to avoid this and write pure managed code. Decoder can be implemented pretty easy. However, if there is a need to have X.500 name generator in ASN, then the job will be signifcantly complicated, because there are a lot of constraints, restrictions for each attribute. Some are defined in RFC5280, some in other RFCs to stay conformant with standard. This functionality requires additional research. But decoder for existing X.500 name can be done easily.

    Only decoder is useful for me at least. Feature request posted here: https://github.com/Crypt32/pkix.net/issues/14

    >> Change Approve and Deny-CertificateRequest so that it does not write to the host directly but instead outputs an object

    > File an issue on GitHub (PSPKI project). I think, I could create some generic OperationStatus class with HResult and Message properties and fire this object in output.

    Good idea yes. Issue here: https://github.com/Crypt32/PSPKI/issues/19

    >> Implement an equivalent of "certreq -sign" for CSRs (all types of them, embedding them in PKCS#7 if required for PKCS#10 for example).

    > and what does this command? Adds external signatures (enroll on behalf of)? If so, then I can start to work on this only after I fix X509CRL2.Build method and get managed PKCS#7 generation code. There is SignedCms class in .NET, which should work with PKCS#7 messages. However it doesn't work very well and signing operations (built-in Sign method) produces only SHA1 signature.

    Issue posted here: https://github.com/Crypt32/pkix.net/issues/15

    >> That's maybe something I do wrong but I never managed to retrieve with Get-DatabaseRow a specific attribute or extension for a particular request ID (filtered by the attribute name or extension OID). Everytime I tried it either retrieved me either all attributes/extensions unfiltered for the request, or none at all.

    > Can you provide additional details on GitHub? I don't know what is wrong there, because the code is unified with other DB reader functions.

    My bad, I just re-tested and found out how to make it work. The only thing (which was not clear last time I tried) is that if we want to filter on AttributeName or ExtensionName for instance, we cannot also use the -RowID parameter but instead have to add another filter for either AttributeRequestId or ExtensionRequestId. I will still open an issue on github because I think the Filter and RowID parameters should be used together.

    Captcha