I think, I have to publish several articles that would cover common PKI/ADCS administration tasks with PowerShell by using my PowerShell PKI module (of course!). Today I'll cover very simple, but very common task: managing pending certificate requests.

In this post we would propose the following scenario:

  • IIS admin requested certificate for internal SharePoint portal
  • Certificate template is configured to require CA manager approval to issue the certificate.
  • No certificate will be issued until CA manager review and approve the request.

Just to make it clear, CA manager approval is configured in the certificate template, as follows:

you, as CA manager, received notification about incoming certificate request. Your task is to review the certificate request to ensure that it is properly constructed and conforms internal security policies and then make decision: approve or deny certificate request. You can do this by using Certification Authority MMC snap-in, but this would require a lot of clicks and without having a chance to automate this. Another solution that includes PowerShell offers you great automation capabilites.

PSPKI module has Get-PendingRequest command that retrieves pending requests from CA database. So, let's start:

PS C:\> Import-Module PSPKI
PS C:\> $ca = Get-CertificationAuthority dc2*
PS C:\> Get-PendingRequest -CA $ca

RequestID             : 1847
Request.RequesterName : CONTOSO\DC2$
Request.SubmittedWhen : 2016.02.05. 9:42:54
Request.CommonName    : www.contoso.com
CertificateTemplate   : Web Server V2
RowId                 : 1847
ConfigString          : dc2.contoso.com\contoso-DC2-CA
Table                 : Request

RequestID             : 1849
Request.RequesterName : CONTOSO\DC2$
Request.SubmittedWhen : 2016.02.05. 10:16:57
Request.CommonName    : www.contoso.com
CertificateTemplate   : Web Server V2
RowId                 : 1849
ConfigString          : dc2.contoso.com\contoso-DC2-CA
Table                 : Request



PS C:\>

We see two incoming requests. Both are generated against "Web Server V2" certificate and for "www.contoso.com" name. Why two? It doesn't really matter. Internal portal could be hosted on NLB cluster and each node would have its own certificate. Doesn't matter, we should review both certificates. Default output of the Get-PendingRequest command returns only few properties about the request. We want to look at request details deeper. We would include "Request.RawRequest" column that contains original PKCS#10/PKCS#7 certificate request:

PS C:\> $row = Get-PendingRequest -CA $ca -Property "RawRequest" -RequestID 1847
PS C:\> $row


RequestID             : 1847
Request.RequesterName : CONTOSO\DC2$
Request.SubmittedWhen : 2016.02.05. 9:42:54
Request.CommonName    : www.contoso.com
CertificateTemplate   : Web Server V2
Request.RawRequest    : MIIFKQYJKoZIhvcNAQcCoIIFGjCCBRYCAQMxCzAJBgUrDgMCGgUAMIIEBgYIKwYB
                        BQUHDAKgggP4BIID9DCCA/AwZDBiAgECBgorBgEEAYI3CgoBMVEwTwIBADADAgEB
                        MUUwQwYJKwYBBAGCNxUUMTYwNAIBBQwPZGMyLmNvbnRvc28uY29tDBVDT05UT1NP
                        XEFkbWluaXN0cmF0b3IMB01NQy5FWEUwggOCoIIDfgIBATCCA3cwggLgAgEAMFUx
                        IDAeBgNVBAoMF0NvbnRvc28gUGhhcm1hY2V1dGljYWxzMRcwFQYDVQQLDA5EaXZp
                        c2lvbiBvZiBJVDEYMBYGA1UEAwwPd3d3LmNvbnRvc28uY29tMIGfMA0GCSqGSIb3
                        DQEBAQUAA4GNADCBiQKBgQDUNydDNjIqqJyMlVeMoPmEKB7sxsStSgaTn/RrGdhE
                        fzDWh/Xg/HySweUc28FsKbRenBuscAdEYMjdzwQjIPtKJg5V1MVob4MESnFVqML8
                        slO+NkUjXYUfL3yH4z8ZAg7/nTCuKFE6S+xzbG1i4Ct5FZKGAF+JDB9LaFFWi53X
                        tQIDAQABoIIB4DAaBgorBgEEAYI3DQIDMQwWCjYuMC42MDAyLjIwQwYJKwYBBAGC
                        NxUUMTYwNAIBBQwPZGMyLmNvbnRvc28uY29tDBVDT05UT1NPXEFkbWluaXN0cmF0
                        b3IMB01NQy5FWEUwcgYKKwYBBAGCNw0CAjFkMGICAQEeWgBNAGkAYwByAG8AcwBv
                        AGYAdAAgAFIAUwBBACAAUwBDAGgAYQBuAG4AZQBsACAAQwByAHkAcAB0AG8AZwBy
                        AGEAcABoAGkAYwAgAFAAcgBvAHYAaQBkAGUAcgMBADCCAQcGCSqGSIb3DQEJDjGB
                        +TCB9jA8BgkrBgEEAYI3FQcELzAtBiUrBgEEAYI3FQiJkAaDvrg7h8GPD4S48iqB
                        4e9VJYaFzFmGxegkAgFkAgEHMBMGA1UdJQQMMAoGCCsGAQUFBwMBMAsGA1UdDwQE
                        AwIFoDBYBgNVHSAEUTBPME0GJSsGAQQBgjcVCImQBoO+uDuHwY8PhLjyKoHh71Ul
                        hrnBAIL59TMwJDAiBggrBgEFBQcCARYWaHR0cDovL3d3dy5jb250b3NvLmNvbTAb
                        BgkrBgEEAYI3FQoEDjAMMAoGCCsGAQUFBwMBMB0GA1UdDgQWBBRfhF+Ikq/9hiD4
                        U3Oj5MfoFSKybDANBgkqhkiG9w0BAQUFAAOBgQDPUKo5kRhWLmxHkRGt6GG5Kt6M
                        +B/8DFQjq5l3IUc8Of2llgLXbzsafTByo22mm1Zr+jZ5XuOFMBMjModIgzgPL599
                        bO1SgHEtZdC6UIxECMNe+Pr1Sy7kr4lxM+A6T1rDDdd0JIn3e2fIPDQWlZT0r3ap
                        FdoYDQ/SBOjaFTgJhjAAMAAxgfkwgfYCAQOAFF+EX4iSr/2GIPhTc6Pkx+gVIrJs
                        MAkGBSsOAwIaBQCgPjAXBgkqhkiG9w0BCQMxCgYIKwYBBQUHDAIwIwYJKoZIhvcN
                        AQkEMRYEFLzmQsISbCiv4csQ/TnMRsVcgHVcMA0GCSqGSIb3DQEBAQUABIGAZ9KT
                        YiPVFu2wM88yjPPlx3AsnpK0IbULx75qEEN4nHz+5raRD/+gC94CVd4wccm3EpZh
                        8rVgQLVJRfgOnVBaE/Figd+uuvCCBpaoio/AeGr+4+JN1eB3r7QOetXPUIK9B2+Z
                        CGatTYJG6usERX5sZpVD8nG13b3OOi6FnhLQWgQ=

RowId                 : 1847
ConfigString          : dc2.contoso.com\contoso-DC2-CA
Table                 : Request



PS C:\>

Here it is. Raw request is base64-encoded (always, when viewing from CA database). We should construct a X509CertificateRequest class by using a X509CertificateRequest( Byte[] ) constructor:

PS C:\> $reqbytes = [convert]::FromBase64String($row."Request.RawRequest")
PS C:\> $req = New-Object System.Security.Cryptography.X509CertificateRequests.X509CertificateRequest (,$reqBytes)
PS C:\> $req


Version            : 1
RequestType        : PKCS7
Subject            : CN=www.contoso.com, OU=Division of IT, O=Contoso Pharmaceuticals
SubjectDN          : System.Security.Cryptography.X509Certificates.X500DistinguishedName
PublicKey          : System.Security.Cryptography.X509Certificates.PublicKey
Attributes         : {0, 0, 0}
Extensions         : {1.3.6.1.4.1.311.21.8.149510.7314491.15746959.9320746.3700693.37.12674649.13726756 (Web Server V2)
                     , 2.5.29.37 (Enhanced Key Usage), 2.5.29.15 (Key Usage), 2.5.29.32 (Certificate Policies)...}
ExternalData       : System.Security.Cryptography.Pkcs.PKCS7SignedMessage
SignatureIsValid   : True
SignatureAlgorithm : 1.2.840.113549.1.1.5 (sha1RSA)
RawData            : {48, 130, 5, 41...}



PS C:\>

We can explore $req object and automate request validation rules. Alternatively, we can dive in to request details by calling ToString method:

PS C:\> $req.ToString()
PKCS10 Certificate Request:
Version: 1
Subject:
    CN=www.contoso.com, OU=Division of IT, O=Contoso Pharmaceuticals

Public Key Algorithm:
    Algorithm ObjectId: RSA (1.2.840.113549.1.1.1)
    Algorithm Parameters:
    05 00
Public Key Length: 1024 bits
Public Key: UnusedBits=0
    0000    30 81 89 02 81 81 00 d4  37 27 43 36 32 2a a8 9c
    0010    8c 95 57 8c a0 f9 84 28  1e ec c6 c4 ad 4a 06 93
    0020    9f f4 6b 19 d8 44 7f 30  d6 87 f5 e0 fc 7c 92 c1
    0030    e5 1c db c1 6c 29 b4 5e  9c 1b ac 70 07 44 60 c8
    0040    dd cf 04 23 20 fb 4a 26  0e 55 d4 c5 68 6f 83 04
    0050    4a 71 55 a8 c2 fc b2 53  be 36 45 23 5d 85 1f 2f
    0060    7c 87 e3 3f 19 02 0e ff  9d 30 ae 28 51 3a 4b ec
    0070    73 6c 6d 62 e0 2b 79 15  92 86 00 5f 89 0c 1f 4b
    0080    68 51 56 8b 9d d7 b5 02  03 01 00 01
Request attributes (Count=3):
  Attribute[0], Length=12 (0c):
    OS Version (OID=1.3.6.1.4.1.311.13.2.3): 6.0.6002.2

  Attribute[1], Length=54 (36):
    Client Info (OID=1.3.6.1.4.1.311.21.20):
        Client ID: ClientIdDefaultRequest (5)
        Computer name: dc2.contoso.com
        User name: CONTOSO\Administrator
        Process name: MMC.EXE

  Attribute[2], Length=100 (64):
    CSP Info (OID=1.3.6.1.4.1.311.13.2.2):
        KeySpec: 1
        Provider: Microsoft RSA SChannel Cryptographic Provider
        Signature unused bits: 0

Request extensions (Count=6):
  OID=Certificate Template Information (1.3.6.1.4.1.311.21.7), Critical=False, Length=47 (2f):
    Template=Web Server V2(1.3.6.1.4.1.311.21.8.149510.7314491.15746959.9320746.3700693.37.12674649.13726756)
    Major Version Number=100
    Minor Version Number=7

  OID=Enhanced Key Usage (2.5.29.37), Critical=False, Length=12 (0c):
    Server Authentication (1.3.6.1.5.5.7.3.1)

  OID=Key Usage (2.5.29.15), Critical=False, Length=4 (04):
    Digital Signature, Key Encipherment (a0)

  OID=Certificate Policies (2.5.29.32), Critical=False, Length=81 (51):
    [1]Certificate Policy:
         Policy Identifier=EV
         [1,1]Policy Qualifier Info:
              Policy Qualifier Id=CPS
              Qualifier:
                   http://www.contoso.com

  OID=Application Policies (1.3.6.1.4.1.311.21.10), Critical=False, Length=14 (0e):
    [1]Application Certificate Policy:
         Policy Identifier=Server Authentication

  OID=Subject Key Identifier (2.5.29.14), Critical=False, Length=22 (16):
    5f 84 5f 88 92 af fd 86 20 f8 53 73 a3 e4 c7 e8 15 22 b2 6c

Signature Algorithm:
    Algorithm ObjectId: 1.2.840.113549.1.1.5 (sha1RSA)
Signature: Unused bits=0
    0000    86 09 38 15 da e8 04 d2  0f 0d 18 da 15 a9 76 af
    0010    f4 94 95 16 34 3c c8 67  7b f7 89 24 74 d7 0d c3
    0020    5a 4f 3a e0 33 71 89 af  e4 2e 4b f5 fa f8 5e c3
    0030    08 44 8c 50 ba d0 65 2d  71 80 52 ed 6c 7d 9f 2f
    0040    0f 38 83 48 87 32 23 13  30 85 e3 5e 79 36 fa 6b
    0050    56 9b a6 6d a3 72 30 7d  1a 3b 6f d7 02 96 a5 fd
    0060    39 3c 47 21 77 99 ab 23  54 0c fc 1f f8 8c de 2a
    0070    b9 61 e8 ad 11 91 47 6c  2e 56 18 91 39 aa 50 cf
Signature matches Public Key: True

PS C:\>

This information is enough to ensure whether the request is legitimate or not. In this case, we don't see any suspicious data, so let issue certificate. For these purposes we have Approve-CertificateRequest command:

PS C:\> Get-PendingRequest -CA $ca -RequestID 1847 | Approve-CertificateRequest
The certificate '1847' was issued.'
PS C:\>

call IIS admin and tell them that the certificate is issued and they can retrieve and install in IIS.

Why request review is important? Apply the same techniques to request with ID=1649:

PS C:\> $row = Get-PendingRequest -CA $ca -Property "RawRequest" -RequestID 1849
PS C:\> $reqbytes = [convert]::FromBase64String($row."Request.RawRequest")
PS C:\> $req = New-Object System.Security.Cryptography.X509CertificateRequests.X509CertificateRequest (,$reqBytes)
PS C:\> $req.ToString()
PKCS10 Certificate Request:
Version: 1
Subject:
    CN=www.contoso.com

Public Key Algorithm:
    Algorithm ObjectId: RSA (1.2.840.113549.1.1.1)
    Algorithm Parameters:
    05 00
Public Key Length: 1024 bits
Public Key: UnusedBits=0
    0000    30 81 89 02 81 81 00 b2  bd 27 6f 03 bb 76 91 3a
    0010    e1 28 40 d8 5f fe 9c 32  7e 16 e9 e2 19 fc 70 6c
    0020    54 26 fe 86 f6 49 81 71  15 b9 a1 e6 01 1e b6 7c
    0030    0a 97 25 ff f4 59 d5 13  34 3d ac 5d ac 23 b6 7a
    0040    24 7b c0 5c 64 8d 27 d5  42 01 fb 59 77 9c 77 e5
    0050    20 2d c3 53 f0 a3 dd 54  0b 4e 1d f9 07 7a ce 20
    0060    12 bf d8 2a b3 6e 42 2c  a5 d8 ed e2 68 79 f0 81
    0070    25 cc 37 69 c6 56 c0 37  4f 1d cb 99 2b ec ec 52
    0080    57 1a 47 2e 22 f5 0b 02  03 01 00 01
Request attributes (Count=3):
  Attribute[0], Length=12 (0c):
    OS Version (OID=1.3.6.1.4.1.311.13.2.3): 6.0.6002.2

  Attribute[1], Length=54 (36):
    Client Info (OID=1.3.6.1.4.1.311.21.20):
        Client ID: ClientIdDefaultRequest (5)
        Computer name: dc2.contoso.com
        User name: CONTOSO\Administrator
        Process name: MMC.EXE

  Attribute[2], Length=100 (64):
    CSP Info (OID=1.3.6.1.4.1.311.13.2.2):
        KeySpec: 1
        Provider: Microsoft RSA SChannel Cryptographic Provider
        Signature unused bits: 0

Request extensions (Count=7):
  OID=Certificate Template Information (1.3.6.1.4.1.311.21.7), Critical=False, Length=47 (2f):
    Template=Web Server V2(1.3.6.1.4.1.311.21.8.149510.7314491.15746959.9320746.3700693.37.12674649.13726756)
    Major Version Number=100
    Minor Version Number=7

  OID=Enhanced Key Usage (2.5.29.37), Critical=False, Length=12 (0c):
    Server Authentication (1.3.6.1.5.5.7.3.1)

  OID=Key Usage (2.5.29.15), Critical=False, Length=4 (04):
    Digital Signature, Key Encipherment (a0)

  OID=Certificate Policies (2.5.29.32), Critical=False, Length=81 (51):
    [1]Certificate Policy:
         Policy Identifier=EV
         [1,1]Policy Qualifier Info:
              Policy Qualifier Id=CPS
              Qualifier:
                   http://www.contoso.com

  OID=Application Policies (1.3.6.1.4.1.311.21.10), Critical=False, Length=14 (0e):
    [1]Application Certificate Policy:
         Policy Identifier=Server Authentication

  OID=Subject Alternative Name (2.5.29.17), Critical=False, Length=36 (24):
    DNS Name=www.microsoft.com
    DNS Name=microsoft.com

  OID=Subject Key Identifier (2.5.29.14), Critical=False, Length=22 (16):
    1a 33 0c 21 ee 4d 8e 50 f8 d6 49 56 1f e2 07 47 6d d9 b7 18

Signature Algorithm:
    Algorithm ObjectId: 1.2.840.113549.1.1.5 (sha1RSA)
Signature: Unused bits=0
    0000    c0 6c db 8a f2 aa ac 8b  9f 90 77 90 89 09 27 e1
    0010    e5 23 5a 5a 03 1c ba 87  f3 d1 26 24 df 71 4c dc
    0020    19 db 7f f1 f5 8b c9 4a  fc 6e fc 42 49 a2 53 db
    0030    e0 03 2e 36 59 1e c6 1d  11 5d ad 94 21 81 ac 40
    0040    f3 5c c2 74 a9 a1 10 6f  d1 8c a5 8d cd 4e c8 a5
    0050    81 42 29 26 e4 c2 b1 af  f2 ca e3 48 b1 80 2f 22
    0060    df 4a f4 43 6e a3 40 e8  95 8d f7 6b 91 25 06 aa
    0070    95 04 db 35 66 ee 18 84  bf b0 ff 6d dd 52 26 63
Signature matches Public Key: True

PS C:\>

At first glance, the request looks the same, but...wait, what hell happens in the Subject Alternative Names extension? Does internal SharePoint portal run microsoft.com website? Apparently, no. Should we appove such request? Definitely, no. What to do? For such cases we have Deny-CertificateRequest command:

PS C:\> Get-PendingRequest -CA $ca -Property "RawRequest" -RequestID 1849 | Deny-CertificateRequest
Successfully denied request with ID = 1849
PS C:\>

Just to recap commands and techniques we used to achieve current scenario:

by using these commands you can automate pending certificate request management in both, automated or manual way. And everything in a single PowerShell console.


Share this article:

Comments:

Bill Lingle

Thank you for putting this together.  Yet another tool in my Powershell toolbelt to automate my life. Great work.

Max

PS C:\tmp> $row = Get-PendingRequest -CA $ca -Property "RawRequest" -RequestID 151
PS C:\tmp> $reqbytes = [convert]::FromBase64String($row."Request.RawRequest")
PS C:\tmp> $req = New-Object System.Security.Cryptography.X509CertificateRequests.X509CertificateRequest(,$reqbytes)
PS C:\tmp> $req.ToString()
System.Security.Cryptography.X509CertificateRequests.X509CertificateRequest

Tried from CA itself and output is always "System.Security.Cryptography.X509CertificateRequests.X509CertificateRequest" (Server 2012 R2, PS v5.1)

Output from $req

PS C:\tmp> $req


RequestType        : PKCS7
SubjectDn          :
ExternalData       : SysadminsLV.PKI.Cryptography.X509CertificateRequests.X509CertificateRequestCmc
Version            : 1
SubjectName        :
Subject            :
PublicKey          : System.Security.Cryptography.X509Certificates.PublicKey
Extensions         : {System.Security.Cryptography.Oid, System.Security.Cryptography.Oid,
                     System.Security.Cryptography.Oid, System.Security.Cryptography.Oid...}
Attributes         : {0, 0, 0}
SignatureAlgorithm : System.Security.Cryptography.Oid
SignatureIsValid   : True
RawData            : {48, 130, 6, 63...}

Any thought?

Max

PS C:\tmp> $row


RequestID              : 151
Request.RequesterName  : %Sanitized\Sanitized%
Request.SubmittedWhen  : 3/29/2019 1:34:16 AM
Request.CommonName     :
CertificateTemplate    : 1.3.6.1.4.1.311.21.8.14711400.4002105.6506161.13047349.12019167.65.9391372.2825925
Request.RawRequest     : MIIGPwYJKoZIhvcNAQcCoIIGMDCCBiwCAQMxDzANBglghkgBZQMEAgEFADCCBIUG
                                                  
                         %Sanitized%
                         
                         6LIw3iYY+CYxN7uPGH0sSC643A==

CertificateTemplateOid : System.Security.Cryptography.Oid
RowId                  : 151
ConfigString           : %Sanitized\Sanitized%
Table                  : Request
Properties             : {[RequestID, 151], [Request.RequesterName, %Sanitized\Sanitized],
                         [Request.SubmittedWhen, 3/29/2019 1:34:16 AM], [Request.CommonName, ]...}

Jon Towles

These commands doesn't seem to work for PKCS10

Vadims Podāns

What commands don't work with PKCS#10? Can you be more specific and provide details.

Alex

I have the same issue as Max, on Windows Server 2016

Heather Miller

All the guidance provided in this blog post works fine up until you attempt $req.ToString(). With a PKCS#10 request the output from the command $req.ToString() only yields "System.Security.Cryptography.X509CertificateRequests.X509CertificateRequest". It does not show the verbose details as depicted in your screenshot above.

 

That is what everyone is wondering about. How do we get the verbose output for a PKCS#10 request?

Vadims Podāns

Try $req.Format(), this should work.

Peter Johnson

Hi Vadims,

I'm trying to view the SAN of a pending request (that was signed with a 'certificate request agent' certificate and submitted on behalf of another user).

This step errors out for me:

req = New-Object System.Security.Cryptography.X509CertificateRequests.X509CertificateRequest(,$reqbytes)

With the following error:

Exception calling ".ctor" with "1" argument(s): "ASN1 bad tag value met."

 

I am able to successfully dump the request using 'certutil -dump'; it's only with X509CertificateRequest that I get an error.

Could you kindly look into this?

 

p.s. X509CertificateRequest works fine if it's a regular CSR that was not signed by a 'certificate request agent'.

(I'm trying to do enroll on behalf here, and need to verify the SAN)

 

Thank you!

Brian Bagent

@Peter - does the preceding line look like this?

$ca2newreq = Get-PendingRequest -CertificationAuthority $ca2authority -Property "RawRequest" -RequestID $ca2requests.RequestID

 

if you don't specify "-Property "RawRequest" -RequestID $ca2requests.RequestID" then i think that's why you're having an issue.


Post your comment:

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