Gardening and Weeding Certificate Templates: Private Key Flags 

Certificate Template Mismanagement Cybersecuroty Threat Risk Compliance Laws Regulations IT Insurance Risk Analysts and Audits

Mismanagement of certificate templates is one of the lowest of the low-hanging fruits when it comes to ADCS threat vectors. Among other things, a misconfigured certificate template can lead to a threat actor obtaining a certificate which could be used for privilege escalation up to and including Enterprise Administrator! 

As you can imagine, it’s a good idea to keep an eye on the certificate templates that exist in your Active Directory forest(s). Pay special attention to those that are assigned to the certification authorities in your environment as these are exposed for use!  

Managing certificate templates requires a “gardener-style” approach with frequent weeding. In a complex environment, a PKI team is often tasked with fascinating pilots and POCs with unique certificate-related requirements. This, combined with the fire drill-style troubleshooting which often occurs when critical business capabilities are impacted by a PKI-related outage, can often lead to creation and assignment of certificate templates which are unneeded, don’t enforce adequate crypto security or which have ACLs (access control lists) that are far too permissive. 

While the Certificate Templates GUI is undoubtably a valuable resource for auditing, it necessitates a human being to manually view and assess template attributes. What’s even more challenging is that this GUI gives no indication of which templates are enabled on one or more CAs. The correlation must, again, be performed manually. 

At PKI Solutions, we’ve been working hard to address the challenges of monitoring certificate templates in the upcoming release of PKI Spotlight. While all of the data about the certificate templates in a given Active Directory environment is available in the Public Key Services container, a decent amount of reverse engineering of ADCS (based on publicly-available Microsoft documentation) was required in order to extract the data in a meaningful way.  

Many certificate template attributes are stored as flags. Flags are a bit-wise enumeration where the status of each bit in an integer value (when presented in binary format) constitutes an “on/off” switch for a particular setting. One of these flagged attributes is entitled “msPKI-Private-Key-Flag” 

This attribute dictates to the CA and the client how the subject’s private key should be handled. For instance: Should the private key be exportable? Should the CA archive the subject’s private key? A full listing of these properties along with their bitwise values, can be found in this Microsoft Open Specification

While developing the next release of PKI Spotlight, we encountered an interesting problem when dealing with the Private Key Flags enumeration. In .NET, if you have an enumeration defined with all the possible bitwise positions defined, you can then use the .ToString() extension method to automatically convert a value of that type to a comma-separated string containing the names of the bit-wise enumeration members which are “turned on” (1 versus 0). The result is that, when the value changes, we are able to present to the user a text representation of which settings were enabled before and after the value changed.  

This works swimmingly for other flagged enumerations such Subject Name flags: 

However, we found that with Private Key flags, the raw integer values were returned by .ToString() rather than the list of enabled members. This happens when the enumeration is missing entries corresponding to bit positions which are “turned on” in the indicated value. We defined our enum based on the Microsoft Open Specification, so why did it not work as we expected? 

Through trial and error, I determined that the values which were seemingly missing from the enumeration laid out in the Microsoft article were: 

  • 0x10000 (65,536 or the 17th bit position, counting from the right) 
  • 0x1000000 (16,777,216 or the 25th bit position counting from the right) 

Furthermore, I discovered that whenever I duplicated any of the default certificate templates, which are automatically present in an Active Directory forest, the new template always had these two bit positions turned on. 

As it turns out, these values have a special meaning. The answer is provided towards the bottom of the Open Specification:   

  • The bitwise AND of the value of the msPKI-Private-Key-Flag attribute and 0x000F0000 determines whether the current CA can issue a certificate based on this template, as explained in [MS-WCCE] section 3.2.2.6.2.1.4.5.7. 
  • The bitwise AND of the value of the msPKI-Private-Key-Flag attribute and 0x0F000000 determines whether the current template is supported by the client, as explained in [MS-WCCE] section 3.1.2.4.2.2.2.8. 

In simple terms, the CA server and enrollee client use these values, respectively, to determine whether it can support the certificate template. Conversely, for our purposes, we found that we could use this logic to indicate the minimum supported ADCS CA and Windows client versions for any given certificate template. 

Credit goes to the great Vadims Podāns for integrating the logic to populate these properties into his incredible PKIX Library

To determine the minimum supported ADCS CA version for a given certificate template, take the value for Private Key Flags and perform a bitwise AND against the value 0xF0000 (983,040 in decimal). The result of this calculation can be used with the below lookup table: 

To determine the minimum supported Windows Client version for a given certificate template, take the value for Private Key Flags and perform a bitwise AND against the value 0xF000000 (251,658,240 in decimal). The result of this calculation can be used with the below lookup table: 

Lastly, in order to facilitate the Private Key Flags enumeration to automatically be displayed as human-friendly comma-separated text, each of the values from the tables above, which represent minimum supported CA server and client versions, need to be added to the enumeration. This way, the bit positions in all valid Private Key Flags values are represented. The completed enumeration can be viewed here

Conclusion: All information about certificate templates can be found in the Public Key Services container of your Active Directory forest, however a decent amount of data transformation is required to make it consumable for a human being. Here at PKI Solutions, we are working hard to ensure that this data can be made available to you in PKI Spotlight so that you can conveniently audit the certificate templates in your environment.  

1 Comment

  1. Shawn Rabourn on January 19, 2023 at 9:07 pm

    Excellent Article, Mike!

    Here’s a little bit more info and a neat little lightly documented trick. If you run the command:

    certutil -v -dstemplate [templateName]

    the output will show the symbolic enumeration of that attribute and the flag names show they are preparing for potential changes in the future…

    msPKI-Private-Key-Flag = “67371024” 0x4040010
    (CTPRIVATEKEY_FLAG_REQUIRE_PRIVATE_KEY_ARCHIVAL — 1)
    CTPRIVATEKEY_FLAG_EXPORTABLE_KEY — 10 (16)
    (CTPRIVATEKEY_FLAG_STRONG_KEY_PROTECTION_REQUIRED — 20 (32))
    (CTPRIVATEKEY_FLAG_REQUIRE_ALTERNATE_SIGNATURE_ALGORITHM — 40 (64))
    (CTPRIVATEKEY_FLAG_REQUIRE_SAME_KEY_RENEWAL — 80 (128))
    (CTPRIVATEKEY_FLAG_USE_LEGACY_PROVIDER — 100 (256))
    (CTPRIVATEKEY_FLAG_EK_TRUST_ON_USE — 200 (512))
    (CTPRIVATEKEY_FLAG_EK_VALIDATE_CERT — 400 (1024))
    (CTPRIVATEKEY_FLAG_EK_VALIDATE_KEY — 800 (2048))
    CTPRIVATEKEY_FLAG_ATTEST_NONE — 0
    (CTPRIVATEKEY_FLAG_ATTEST_PREFERRED — 1000 (4096))
    (CTPRIVATEKEY_FLAG_ATTEST_REQUIRED — 2000 (8192))
    (CTPRIVATEKEY_FLAG_ATTEST_WITHOUT_POLICY — 4000 (16384))
    (TEMPLATE_SERVER_VER_NONE<<CTPRIVATEKEY_FLAG_SERVERVERSION_SHIFT — 0)
    (TEMPLATE_SERVER_VER_2003<<CTPRIVATEKEY_FLAG_SERVERVERSION_SHIFT — 10000 (65536))
    (TEMPLATE_SERVER_VER_2008<<CTPRIVATEKEY_FLAG_SERVERVERSION_SHIFT — 20000 (131072))
    (TEMPLATE_SERVER_VER_2008R2<<CTPRIVATEKEY_FLAG_SERVERVERSION_SHIFT — 30000 (196608))
    TEMPLATE_SERVER_VER_WIN8<<CTPRIVATEKEY_FLAG_SERVERVERSION_SHIFT — 40000 (262144)
    (TEMPLATE_SERVER_VER_WINBLUE<<CTPRIVATEKEY_FLAG_SERVERVERSION_SHIFT — 50000 (327680))
    (TEMPLATE_SERVER_VER_THRESHOLD<<CTPRIVATEKEY_FLAG_SERVERVERSION_SHIFT — 60000 (393216))
    (V7<<CTPRIVATEKEY_FLAG_SERVERVERSION_SHIFT — 70000 (458752))
    (V8<<CTPRIVATEKEY_FLAG_SERVERVERSION_SHIFT — 80000 (524288))
    (V9<<CTPRIVATEKEY_FLAG_SERVERVERSION_SHIFT — 90000 (589824))
    (V10<<CTPRIVATEKEY_FLAG_SERVERVERSION_SHIFT — a0000 (655360))
    (V11<<CTPRIVATEKEY_FLAG_SERVERVERSION_SHIFT — b0000 (720896))
    (V12<<CTPRIVATEKEY_FLAG_SERVERVERSION_SHIFT — c0000 (786432))
    (V13<<CTPRIVATEKEY_FLAG_SERVERVERSION_SHIFT — d0000 (851968))
    (V14<<CTPRIVATEKEY_FLAG_SERVERVERSION_SHIFT — e0000 (917504))
    (V15<<CTPRIVATEKEY_FLAG_SERVERVERSION_SHIFT — f0000 (983040))
    (CTPRIVATEKEY_FLAG_HELLO_KSP_KEY — 100000 (1048576))
    (CTPRIVATEKEY_FLAG_HELLO_LOGON_KEY — 200000 (2097152))
    (TEMPLATE_CLIENT_VER_NONE<<CTPRIVATEKEY_FLAG_CLIENTVERSION_SHIFT — 0)
    (TEMPLATE_CLIENT_VER_XP<<CTPRIVATEKEY_FLAG_CLIENTVERSION_SHIFT — 1000000 (16777216))
    (TEMPLATE_CLIENT_VER_VISTA<<CTPRIVATEKEY_FLAG_CLIENTVERSION_SHIFT — 2000000 (33554432))
    (TEMPLATE_CLIENT_VER_WIN7<<CTPRIVATEKEY_FLAG_CLIENTVERSION_SHIFT — 3000000 (50331648))
    TEMPLATE_CLIENT_VER_WIN8<<CTPRIVATEKEY_FLAG_CLIENTVERSION_SHIFT — 4000000 (67108864)
    (TEMPLATE_CLIENT_VER_WINBLUE<<CTPRIVATEKEY_FLAG_CLIENTVERSION_SHIFT — 5000000 (83886080))
    (TEMPLATE_CLIENT_VER_THRESHOLD<<CTPRIVATEKEY_FLAG_CLIENTVERSION_SHIFT — 6000000 (100663296))
    (V7<<CTPRIVATEKEY_FLAG_CLIENTVERSION_SHIFT — 7000000 (117440512))
    (V8<<CTPRIVATEKEY_FLAG_CLIENTVERSION_SHIFT — 8000000 (134217728))
    (V9<<CTPRIVATEKEY_FLAG_CLIENTVERSION_SHIFT — 9000000 (150994944))
    (V10<<CTPRIVATEKEY_FLAG_CLIENTVERSION_SHIFT — a000000 (167772160))
    (V11<<CTPRIVATEKEY_FLAG_CLIENTVERSION_SHIFT — b000000 (184549376))
    (V12<<CTPRIVATEKEY_FLAG_CLIENTVERSION_SHIFT — c000000 (201326592))
    (V13<<CTPRIVATEKEY_FLAG_CLIENTVERSION_SHIFT — d000000 (218103808))
    (V14<<CTPRIVATEKEY_FLAG_CLIENTVERSION_SHIFT — e000000 (234881024))
    (V15<<CTPRIVATEKEY_FLAG_CLIENTVERSION_SHIFT — f000000 (251658240))

    Be ready to figure out what V7-15 end up being…

Leave a Comment





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