Get Pending Requests with SAN / DNS

Feb 14, 2014 at 5:13 PM
How would you use Get-PendingRequest and also pull the request extensions so I can get Subject Alternative Names / DNS extensions?
Coordinator
Feb 17, 2014 at 4:50 PM
Sorry for delayed response.
The only way is to get raw request ("Request.RawRequest" property) and use it to instantiate a X509CertificateRequest class (http://pkix2.sysadmins.lv/library/html/T_System_Security_Cryptography_X509CertificateRequests_X509CertificateRequest.htm)
In your case it would be something like this:
# get raw request in base64 string form
$rawRequest = (Get-CA myca | Get-PendingRequest -RequestID 100 -Property "Request.RawRequest")."Request.RawRequest"
# convert base64 string to byte array
$rawBytes = [convert]::frombase64string($rawRequest)
# instantiate X509 reuqest object:
$request = new-object security.cryptography.x509certificaterequests.x509certificaterequest (,$rawBytes)
# find out SAN extension:
$SAN = $request.Extensions | where {$_.oid.value -eq "2.5.29.17"}
Replace your query string with your own query and add included property to include raw request from the database.
if there is SAN extension in the request, $SAN will store the extension. I made a SAN extension class, so you can access individual names in the AlternativeNames property. Here is an example:
PS C:\> $SAN = $cert.Extensions | ?{$_.oid.value -eq "2.5.29.17"}
PS C:\> $san

AlternativeNames                                   Critical Oid                           RawData
----------------                                   -------- ---                           -------
{DNS Name=DC, DNS Name=dc2...                         False 2.5.29.17 (Subject Alterna... {48, 22, 130, 3...}


PS C:\> $san.AlternativeNames

                         Type OID                           Value                         RawData
                         ---- ---                           -----                         -------
                      DnsName                               DC2                           {130, 3, 68, 67...}
                      DnsName                               dc2.contoso.com               {130, 15, 100, 99...}


PS C:\>
Note: currently I can handle only PKCS#10 requests, so in certain cases it may not work.
Apr 11, 2014 at 4:15 AM
What would be involved with extracting a CMS Signed RawRequest and extracting the SAN/Extensions?
Coordinator
Apr 11, 2014 at 8:51 AM
Unfortunately, there is no managed way to read CMS requests. The only way is to use certutil for that.
Coordinator
May 9, 2014 at 4:56 PM
I just wrote new classes to handle PKCS7/CMS requests (which is your case, I believe). If you wish, you can get updated DLL file and test, whether it works for you.
May 12, 2014 at 12:24 PM
Yes, I can test the updated DLL.
Coordinator
May 12, 2014 at 7:13 PM
See the latest post: https://pspki.codeplex.com/workitem/43
you will find new DLL after first message in the attach list. Please, let me know how it works for you.
Coordinator
May 22, 2014 at 1:29 PM
Did it worked for you?
May 23, 2014 at 6:42 PM
When I use this code:
$rawBytes = [convert]::frombase64string($RawRequest)
$pkcs7 = new-object System.Security.Cryptography.Pkcs.PKCS7SignedMessage(,$rawBytes)
# get embedded request:
$pkcs7.Content[0]
# get SAN extension:
$pkcs7.Content[0].Extensions | ?{$_.oid.value -eq "2.5.29.17"}
I am still getting: Exception calling ".ctor" with "1" argument(s): "ASN1 bad tag value met"
Coordinator
May 23, 2014 at 7:20 PM
can you post your pkcs7 request (or email me directly via CodePlex mailing)?
May 27, 2014 at 6:44 PM
Test-Request-02.txt

MIIGhAYJKoZIhvcNAQcCoIIGdTCCBnECAQMxCzAJBgUrDgMCGgUAMIIE3gYIKwYB
BQUHDAKgggTQBIIEzDCCBMgwbTBrAgECBgorBgEEAYI3CgoBMVowWAIBADADAgEB
MU4wTAYJKwYBBAGCNxUUMT8wPQIBBQwdSVUtTVNTRy1GUkVEODEudGVzdGFkcy5p
dS5lZHUMEFRFU1RBRFNcdHNrZnNlcnYMB01NQy5FWEUwggRRoIIETQIBATCCBEYw
ggMuAgEAMDcxHDAaBgNVBAMME0Fub3RoZXIgQ29tbW9uIE5hbWUxFzAVBgNVBAMM
DnRoaXMgaXMgYSB0ZXN0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA
mII5rwfdmVj9NReG7lOgaR+QakHU6h58b/zHclOe19t+37+bncK4LTokhzvO/6gk
Bd3J2XylHUlYVEwSXq28Lq05ZRSz70pE/qbUP/hbP/h/JJr2gDxP+lLlHYkJBRSo
+YhXH6sExqahqmIaKsmX9L+OV4A0AMd5646Bg9scr7Z+nCeRBCsaCA8TCXuLYvRZ
KbnXFOdNXy8CBM+J/OoLuaewX7OLvz1gtn2gcGrvnsuTdk22XNb85iINrlpXUe8G
vs/VP1FPNsLkMr47hb/5up1KO7s1o9+cbSiZENLTATqmelIdy9csQcxUpNrFrKf6
E94eEYU8PI39IIaxV16+xwIDAQABoIIByDAaBgorBgEEAYI3DQIDMQwWCjYuMy45
NjAwLjIwTAYJKwYBBAGCNxUUMT8wPQIBBQwdSVUtTVNTRy1GUkVEODEudGVzdGFk
cy5pdS5lZHUMEFRFU1RBRFNcdHNrZnNlcnYMB01NQy5FWEUwcgYKKwYBBAGCNw0C
AjFkMGICAQEeWgBNAGkAYwByAG8AcwBvAGYAdAAgAFIAUwBBACAAUwBDAGgAYQBu
AG4AZQBsACAAQwByAHkAcAB0AG8AZwByAGEAcABoAGkAYwAgAFAAcgBvAHYAaQBk
AGUAcgMBADCB5wYJKoZIhvcNAQkOMYHZMIHWMD4GCSsGAQQBgjcVBwQxMC8GJysG
AQQBgjcVCIGl3meHsIY7gtmdFYaC+1SCw/h+gUiBpYtzh5eUcAIBZAIBETATBgNV
HSUEDDAKBggrBgEFBQcDATAOBgNVHQ8BAf8EBAMCBaAwGwYJKwYBBAGCNxUKBA4w
DDAKBggrBgEFBQcDATAzBgNVHREELDAqghNNb3JlRE5TLnRlc3QuaXUuZWR1ghNv
bmVtb3JlLnRlc3QuaXUuZWR1MB0GA1UdDgQWBBSFLrItBLReUP0I+NjTaxsQbg/N
wTANBgkqhkiG9w0BAQUFAAOCAQEAXzr7R9uBykwV/Uw9pjh6sX4620sjf85/OI2T
mBGt3NGSJNcX4/lnrAFOJF+sEXTM2CQjnNiv1EwUKCwOFaG/ZgQIRTwwagdvCda3
NXvLYE4uF33AJz1oi6xwD1P1l4mjfj1MJ3CtsxFpmR+jMXDOI0VIFZEJFcSAt9Kg
X/tcSUcMTrAgoLl+I4exPhBjK09izyPwwsR67LG7EKqgJZ74vS9deEskFqisSxWi
CorRjKmrnxTdBrKmFSPgNGHgTqYg7T3P3GLxxGjK9aJ4ZA0g8eJ2NmFpkaGt27bq
/7SzbSJ0ZQqkjPHEw1ZyQNJqBiPOii4DNyIZvxT/Ln5wCriLeDAAMAAxggF7MIIB
dwIBA4AUhS6yLQS0XlD9CPjY02sbEG4PzcEwCQYFKw4DAhoFAKA+MBcGCSqGSIb3
DQEJAzEKBggrBgEFBQcMAjAjBgkqhkiG9w0BCQQxFgQU6Ik1dZoGjjR5e/53UIh0
V7b3yGYwDQYJKoZIhvcNAQEBBQAEggEAgxzC9mPHoho6HyI/rVOO+sF8aoAIFLDX
s6OwE/v6Lv0+MLMnIbN7Jt3DT7nJAwuilRNnAMgf28ffZcBDcl0l/fweBzbEoHGz
YijQyf87glcmAb5ldw2/ORboSUvbXZnt0rLacqKpVN+7xaK+MnSwT+7N7JX3Itda
kvwHC9z81mFzfJSfmSuup8Or2IANnxV9b+X4JCv6KHb7Dxy3zZaceyQeGpHxukyJ
TOluuCzN1ZA8+QM6yHCsBRXNfc7cX6/rQGRnuSr8LckuGlMXetXP8J5zm+epTaa7
L1FMLuuJAq9rMWpBWIO2FcWkkZzC7agUclv30S3H9Tk9uyXJCBixdA==
Coordinator
May 27, 2014 at 7:24 PM
Edited May 27, 2014 at 7:25 PM
This request works for me:
[↓] [vPodans] $base64 = [io.file]::ReadAllText("C:\Users\vPodans\Desktop\req.txt")
[↓] [vPodans] $rawBytes = [convert]::FromBase64String($base64)
[↓] [vPodans] $pkcs7 = new-object System.Security.Cryptography.Pkcs.PKCS7SignedMessage(,$rawBytes)
[↓] [vPodans] $pkcs7


Version          : 3
DigestAlgorithms : {sha1}
ContentType      : System.Security.Cryptography.Oid
Content          : {System.Security.Cryptography.X509Certificates.PublicKey}
Certificates     :
RevocationLists  :
Attributes       : {}
SignerInfos      : {System.Security.Cryptography.Pkcs.SignerInfo2}
RawData          : {48, 130, 6, 132...}



[↓] [vPodans] $pkcs7.Content[0]


Version            : 1
RequestType        : PKCS10
Subject            : CN=this is a test, CN=Another Common Name
SubjectDN          : System.Security.Cryptography.X509Certificates.X500DistinguishedName
PublicKey          : System.Security.Cryptography.X509Certificates.PublicKey
Attributes         : {0, 0, 0}
Extensions         : {System.Security.Cryptography.Oid, System.Security.Cryptography.Oid, System.Security.Cryptography.
                     Oid, System.Security.Cryptography.Oid...}
SignatureIsValid   : True
SignatureAlgorithm : System.Security.Cryptography.Oid
RawData            : {48, 130, 4, 70...}



[↓] [vPodans] $pkcs7.Content[0].Extensions | ?{$_.oid.value -eq "2.5.29.17"}

AlternativeNames                                   Critical Oid                           RawData
----------------                                   -------- ---                           -------
{DNS Name=MoreDNS.test.iu....                         False System.Security.Cryptograp... {48, 42, 130, 19...}


[↓] [vPodans] $pkcs7.Content[0].Extensions | ?{$_.oid.value -eq "2.5.29.17"} | fl *


AlternativeNames : {DNS Name=MoreDNS.test.iu.ed, DNS Name=onemore.test.iu.ed}
Critical         : False
Oid              : System.Security.Cryptography.Oid
RawData          : {48, 42, 130, 19...}



[↓] [vPodans]
To make sure that you are doing it right: please post first 10 bytes from $rawBytes variable. Just type in PS console: $rawBytes[0..10]
Sep 3, 2014 at 6:14 PM
Edited Sep 3, 2014 at 6:34 PM
Just for fun ... I had a need to determine whether a cert was configured for SANs or not, so I came up with the following:
$CertData = (connect-ca $CertObject.IssuingCa | get-issuedrequest -RequestID $CertObject.RequestID | Receive-Certificate).GetRawCertData()
$TempCert = new-object system.security.cryptography.x509certificates.x509certificate2
$TempCert.Import($CertData)
$SANs = ($TempCert.Extensions | Where-Object {$_.Oid.FriendlyName -eq "subject alternative name"}).format(1)
$SANs will contain the stings which you can operate on for further filtering, or what have you. For example
switch -wildcard ($SANs)
{
"other name*" {
    $SANs = $SANs.Substring(17)
    $SANitem = "UPN"
    return $SANitem # (you could return $SANs for the actual string of UPNs)
           }
"dns name*"   {
    $SANs = $SANs.substring(0,$SANs.length-1).Split("\`n")
    foreach($SANitem in $SANs){
        $SANitems = "DNS"
        $SANitem += $SANitem    
                            }
     return $SANitem
                      }
}
This code will not copy and paste and run for you. It has not been fully sanatized from how I use it in my automations. The code is intended to provide an in-use context only, not just a general usage syntax. :)
Coordinator
Sep 3, 2014 at 6:51 PM
Starting with PSPKI v3.0, this process become much easier:
$extension = get-ca $ca | Get-DatabaseRow -RowID $RequestID -Table Extension | ?{$_.ExtensionName -eq "2.5.29.17"}
if ($extension) {
     Write-Host Request with Id=$RequestID is configured for SAN
} esle {
     Write-Host Request with Id=$RequestID is NOT configured for SAN
}
And to view SAN extension values, the following technique could be used:
$base64 = (get-ca $ca | Get-DatabaseRow -RowID $RequestID -Table Extension | ?{$_.ExtensionName -eq "2.5.29.17"}).ExtensionRawValue
$asn = New-Object Security.Cryptography.AsnEncodedData (,[Convert]::FromBase64String($base64))
$san = New-Object Security.Cryptography.X509Certificates.X509SubjectAlternativeNamesExtension $asn, $false
$san.AlternativeNames
We access Extension table and select extension raw value (which is base64-encoded), convert to a byte array and instantiate AsnEncodedData and then construct SAN extension object. There is AlternativeNames property which contains all SAN with their types, values and raw values. More details here: X509SubjectAlternativeNamesExtension Class
Sep 4, 2014 at 12:01 AM
Edited Sep 4, 2014 at 12:01 AM
Hi Camelot,

I just tried your last example. Any idea why the result would be truncated? I am missing the last caracter of every domain name returned:

This is what I get:
DNS Name=test.example.co, DNS Name=test2.example.co
But I would expect:
DNS Name=test.example.com, DNS Name=test2.example.com
Thanks!
Coordinator
Oct 2, 2014 at 3:20 PM
Hello, sorry for delayed response, recently I was busy on another projects.

this is an issue in the Format() method, which incorrectly strips trailing character. In any way, Value property of AlternativeName object returns the correct value. I fixed this for the next release.
Coordinator
Oct 2, 2014 at 3:22 PM
This discussion has been copied to a work item. Click here to go to the work item and continue the discussion.