Continuing the story about new release I will talk about two interesting features.

Set-CertificateExtension

Windows CA supports extension addition to a certificate request. Sometimes it is necessary. For example, IIS admin submitted certificate request for one of the web site. He used IIS Manager console to generate request. The biggest drawback of the IIS certificate request manager – it doesn’t allow you to specify subject alternative names (SAN). Or another example, some application requires custom extension which cannot be added through Certificate Templates console. Or, maybe, certificate request contains some unwanted extensions and you want to disable them. Set-CertificateExtension command will be very handy in such situations.

Why not to modify certificate request directly? Sure, you can, but you will break signature and no CA will accept it. And direct certificate edit isn’t that simple task, so don’t think about this.

Microsoft CA supports extension value modification (as well, as addition and removal) in a timeframe between certificate request is submitted and processed by a policy module. Normally, Windows CA immediately passes incoming requests to policy module. With the only exception, when certificate request is pending for CA manager approval.

There is ‘certutil –setextension’ command that does the same, however it’s syntax is too cryptic even for me. My own implementation isn’t very simple for use, but more convenient for users who ever worked with .NET X509 extensions.

At first, we need to prepare extension object to add. There are several predefined in native .NET stored in the  X509Certificates namespace (look for classes that ends with “Extension” suffix). In addition, I wrote several extension classes in the same namespace: X509Certificates.

If you are creating an X509 extension class, it must inherit from X509Extension base class.

They behave exactly as .NET native with the only exception – these extensions do not rely on unmanaged functions, all encoders and decoders are managed. Let’s add something simple, for example, Basic Constraints extension:

$bc = New-Object security.cryptography.x509certificates.x509basicconstraintsextension 0,0,0,0
Get-CA dc2* | Get-PendingRequest -RequestID 1622 | Set-CertificateExtension -Extension $bc
PS C:\> $bc = New-Object security.cryptography.x509certificates.x509basicconstraintsextension 0,0,0,0
PS C:\> $bc


CertificateAuthority    : False
HasPathLengthConstraint : False
PathLengthConstraint    : 0
Critical                : False
Oid                     : 2.5.29.19 (Basic Constraints)
RawData                 : {48, 0}



PS C:\> Get-CA dc2* | Get-PendingRequest -RequestID 1622 | Set-CertificateExtension -Extension $bc -Verbose
VERBOSE: Extension 'Basic Constraints' was addedd to request ID='1622'.


RequestID             : 1622
Request.RequesterName : CONTOSO\DC2$
Request.SubmittedWhen : 2013.11.18. 15:55:50
Request.CommonName    :
CertificateTemplate   : Web Server V2
RowId                 : 1622
ConfigString          : dc2.contoso.com\contoso-DC2-CA
Table                 : Request



PS C:\>

Now, if we issue the certificate it will contain Basic Constraints extension issued to End Entity without path length constraint and is not marked critical. Plain and simple. Doesn’t? Moreover, the code returns request row object, so it can be piped to Approve-CertificateRequest to issue the certificate.

In order to remove specific extension, pass extension OID to the Extension parameter and enable Remove switch:

PS C:\> Get-CA dc2* | Get-PendingRequest -RequestID 1622 | Set-CertificateExtension -Extension 2.5.29.19 -Remove -Verbose
VERBOSE: Extension OID='2.5.29.19' was removed from request ID='1622'.


RequestID             : 1622
Request.RequesterName : CONTOSO\DC2$
Request.SubmittedWhen : 2013.11.18. 15:55:50
Request.CommonName    :
CertificateTemplate   : Web Server V2
RowId                 : 1622
ConfigString          : dc2.contoso.com\contoso-DC2-CA
Table                 : Request



PS C:\>

CA security

This is another handy functionality. There are 4 commands that are used to manage CA security (ACL):

These commands are self-explanatory. I tried various designs and ended up with a regular .NET-style ACL object. You can even use Get-ACL on Certification Authority object:

PS C:\> Get-CA dc2* | Get-CAACL

Path                                    Owner                                   Access
----                                    -----                                   ------
                                        BUILTIN\Administrators                  NT AUTHORITY\Authenticated Users All...


PS C:\> (Get-CA dc2* | Get-CAACL).access


CertificationAuthorityRights : Read, Enroll
AccessControlType            : Allow
IdentityReference            : NT AUTHORITY\Authenticated Users
IsInherited                  : False
InheritanceFlags             : None
PropagationFlags             : None

CertificationAuthorityRights : ManageCA, ManageCertificates
AccessControlType            : Allow
IdentityReference            : BUILTIN\Administrators
IsInherited                  : False
InheritanceFlags             : None
PropagationFlags             : None

CertificationAuthorityRights : ManageCA, ManageCertificates
AccessControlType            : Allow
IdentityReference            : CONTOSO\Domain Admins
IsInherited                  : False
InheritanceFlags             : None
PropagationFlags             : None

CertificationAuthorityRights : ManageCA, ManageCertificates
AccessControlType            : Allow
IdentityReference            : CONTOSO\Enterprise Admins
IsInherited                  : False
InheritanceFlags             : None
PropagationFlags             : None



PS C:\>

Looks very familiar. CA ACL is simpler than, say, file system ACL, because it doesn’t use ACL inheritance and ACL propagation and uses very simple permissions:

PS C:\> [enum]::GetNames([PKI.Security.AccessControl.CertificationAuthorityRights])
ManageCA
ManageCertificates
Read
Enroll
PS C:\>

Exactly four entries, like in Certification Authority MMC:

image

Permission addition consist of access control entry (ACE) object creation:

$account = [Security.Principal.NTAccount]"domain\username"
New-Object PKI.Security.AccessControl.CertificationAuthorityAccessRule $account, $AccessMask, $AccessType

Or one-liner:

New-Object PKI.Security.AccessControl.CertificationAuthorityAccessRule ([Security.Principal.NTAccount]"domain\username"), $AccessMask, $AccessType
PS C:\> Get-CA dc2* | Get-CAACL | Add-CAACL -AccessControlEntry (new-object PKI.Security.AccessControl.CertificationAuth
orityAccessRule ([security.principal.ntaccount]"acl"), managecertificates, allow) | Set-CASecurityDescriptor -RestartCA
PS C:\> (Get-CA dc2* | Get-CAACL).access


CertificationAuthorityRights : Read, Enroll
AccessControlType            : Allow
IdentityReference            : NT AUTHORITY\Authenticated Users
IsInherited                  : False
InheritanceFlags             : None
PropagationFlags             : None

CertificationAuthorityRights : ManageCA, ManageCertificates
AccessControlType            : Allow
IdentityReference            : BUILTIN\Administrators
IsInherited                  : False
InheritanceFlags             : None
PropagationFlags             : None

CertificationAuthorityRights : ManageCA, ManageCertificates
AccessControlType            : Allow
IdentityReference            : CONTOSO\Domain Admins
IsInherited                  : False
InheritanceFlags             : None
PropagationFlags             : None

CertificationAuthorityRights : ManageCA, ManageCertificates
AccessControlType            : Allow
IdentityReference            : CONTOSO\Enterprise Admins
IsInherited                  : False
InheritanceFlags             : None
PropagationFlags             : None

CertificationAuthorityRights : ManageCertificates
AccessControlType            : Allow
IdentityReference            : CONTOSO\acl
IsInherited                  : False
InheritanceFlags             : None
PropagationFlags             : None



PS C:\>

Very easy task. The only note here is that, there is allowed only one entry per account. Therefore, if particular user account (or group) is already listed in the ACL, new ACE will not be added, instead, you should remove this account from ACL and add new ACE. Account removal is simpler, you need to specify account name to remove:

PS C:\> Get-CA dc2* | Get-CAACL | Remove-CAACL -User "acl" | Set-CAACL -RestartCA
PS C:\> (Get-CA dc2* | Get-CAACL).access


CertificationAuthorityRights : Read, Enroll
AccessControlType            : Allow
IdentityReference            : NT AUTHORITY\Authenticated Users
IsInherited                  : False
InheritanceFlags             : None
PropagationFlags             : None

CertificationAuthorityRights : ManageCA, ManageCertificates
AccessControlType            : Allow
IdentityReference            : BUILTIN\Administrators
IsInherited                  : False
InheritanceFlags             : None
PropagationFlags             : None

CertificationAuthorityRights : ManageCA, ManageCertificates
AccessControlType            : Allow
IdentityReference            : CONTOSO\Domain Admins
IsInherited                  : False
InheritanceFlags             : None
PropagationFlags             : None

CertificationAuthorityRights : ManageCA, ManageCertificates
AccessControlType            : Allow
IdentityReference            : CONTOSO\Enterprise Admins
IsInherited                  : False
InheritanceFlags             : None
PropagationFlags             : None



PS C:\>

Very easy. CA ACL management via certutil would be a real nightmare. This is another place where PowerShell takes over certutil.

In the next post I will talk about dealing with X509 certificate requests in PowerShell.


Share this article:

Comments:

Mahsa

Hi

Thank you for your useful information. Could you please explain how can I add a certificate policy with an OID and a URL address to a request by use of this method? I really need your help.


Post your comment:

Please, solve this little equation and enter result below. Captcha