This article will explain and demonstrate the techniques that will add custom extensions to certificate requests.

A little abstract. As you know, when Hewlett-Packard iLO generate certificate request for SSL it include server *short* name to the Subject field. There is no way to change subject name format. While output request file is signed you cannot edit this request, because signature will become broken and CA (Certification Authority) will reject this request. Subject name short format is not quite useful, because many administrators prefer FQDN (Fully Qualified Domain Name), for example: iLO1.domain.com. I agree with this point that FQDN are more useful and can be used in various network configurations and in multi-domain/multi-forest environments.

Is there a way to work around this issue? Yes there is a way. You can add additional extensions (such Subject Alternative Name) to a request that is submitted to a CA and is stored in Pending Requests container. If request is issued or denied there is no way to add anything to certificate request.

Configure CA to require manual HP iLO certificate request approval

In this section I will discuss about CA server configuration for request manual approval by CA Manager or CA Administrator.

  • Standalone Certification Authority

By default Standalone CAs place all certificate requests to Pending Request node, so every request must be manually approved by CA Manager or CA Administrator and there no changes are required.

  • Enterprise Certification Authority

By default Enterprise CAs automatically process and issue or deny certificate requests. So you need to configure certificate template for CA Manager approval. While V1 templates don't support any modifications, you must create new V2 or V3 template. To do this you need to perform the following steps:

  1. Log on to the computer that acts as an Issuing Enterprise Certification Authority with Enterprise Admin privileges.
  2. On the Windows desktop, click Start, and then click Run.
  3. In the Run dialog box type mmc, and then click OK. If User Account Control is enabled, enter required account credentials or just click Yes on consent window.
  4. In the Console1 window, click File, and then click Add/Remove Snap-in.
  5. In the Add/Remove Snap-in dialog box, click Add.
  6. In the Add Standalone Snap-in dialog box, click Certificate Templates.
  7. In the Certificate Templates window locate the template named Web Server.
  8. Click Action and then click Duplicate Template. If prompted, select Windows Server 2003, Enterprise Edition.
  9. In Properties of New Template window in General tab specify new template name. For example, Web Server V2.
  10. Switch to Issuance Requirements tab an enable CA certificate manager approval check-box as shown here:CA certificate manager approval check-box

  11. Click Ok to save these changes.

By default CA ignores Subject Alternative Name (SAN) extension for certificate requests. You need to configure CA server to enable this extension as described here: How to add a Subject Alternative Name to a secure LDAP certificate.

Add new template to Issuing Enterprise CA

  1. On the Windows desktop, click Start, click Administrative Tools and click Certification Authority. If User Account Control is enabled, enter required account credentials or just click Yes on consent window.
  2. Expand your Certification Authority name and select Certificate Templates.
  3. In the Certificate Templates click Action, New and Certificate template to issue.
  4. In the Enable Certificate Templates window locate custom template (in our example this is Web Server V2) and click Ok.

Submit HP iLO certificate request

  1. Click Start, click All Programs, Accessories and then click Command Prompt.
    Note: If User Account Control is enabled run Command Prompt window by choosing Run as Administrator command from the shortcut’s context menu. If prompted enter local Administrator password or just press Yes on consent prompt.

  2. In the Command Prompt window run the following command:

    for Enterprise CA:
    certreq –submit –attrib "CertificateTemplate:WebServerV2" path\certificaterequest.req

    for Standalone CA:
    certreq –submit path\certificaterequest.req

Explore CryptoAPI ICertAdmin2::SetCertificateExtension method

In this we will explore ICertAdmin2::SetCertificateExtension method that will add additional extensions to certificate request. This method requires the following arguments:

  • strConfig — specifies CA server configuration string in the following format: CAComputerName\CAName
  • RequestId — specifies request ID to edit. When you submit request using CertReq, the command will output certificate Request ID number. You will need to use this number in the method.
  • strExtensionName — specifies extension OID. The list of common extension is covered in the following article: Supported Extensions. While we will use Subject Alternative Name the following value is used: 2.5.29.17
  • Type — specifies extension value format type. In our case we will use Binary (0x3).
  • Flags — while SAN extension will be enabled and marked as non-critical, zero (0x0) will be used.
  • pvarValue — encoded extension value type.

While first five arguments are simple strings/numbers, last argument is quite complex, because it need to be passed in ASN.1 DER encoding format. Unfortunately there are no .NET encoders that will encode value strings. But there is a functionality in CertEnroll CryptoAPI interfaces. The following interface will be used: IX509ExtensionAlternativeNames. I order to encode extension value using InitializeEncode method we need to create IAlternativeNames object. Note that this is collection of IAlternativeName objects. IAlternativeName is created by calling InitializeFromString method (see interface reference page).

These interfaces are available in Windows Vista/Windows Server 2008 and higher. Windows XP and Windows Server 2003 don't support these interfaces. For additional info please refer to the corresponding interface MSDN pages.

Let's start with object creation and investigation. At first we need to instantiate mentioned interfaces using the following commands:

$SAN = New-Object -ComObject X509Enrollment.CX509ExtensionAlternativeNames
$IANs = New-Object -ComObject X509Enrollment.CAlternativeNames
$IAN1 = New-Object -ComObject X509Enrollment.CAlternativeName
$IAN2 = New-Object -ComObject X509Enrollment.CAlternativeName

We have created two IAlternativeName objects to add both short and FQDN names to SAN extension. Now we initialize both IAlternativeName extensions by calling InitializeFromString method:

[↓] [vPodans] # instantiate IALternativeName, IAlternativeNames and IX509ExtensionAlternativeNames objects
[↓] [vPodans] $SAN = New-Object -ComObject X509Enrollment.CX509ExtensionAlternativeNames
[↓] [vPodans] $IANs = New-Object -ComObject X509Enrollment.CAlternativeNames
[↓] [vPodans] $IAN1 = New-Object -ComObject X509Enrollment.CAlternativeName
[↓] [vPodans] $IAN2 = New-Object -ComObject X509Enrollment.CAlternativeName
[↓] [vPodans] $IAN1.InitializeFromString(0x3,"iLO-ShortName")
[↓] [vPodans] $IAN2.InitializeFromString(0x3,"iLO-FQDN.company.com")
[↓] [vPodans] # add both SAN names to IAlternativeNames collection
[↓] [vPodans] $IAN1, $IAN2 | ForEach-Object {$IANs.Add($_)}
[↓] [vPodans] # now we add IAlternativeNames collection to SAN object
[↓] [vPodans] $SAN.InitializeEncode($IANs)
[↓] [vPodans] $SAN

ObjectId                                                               Critical AlternativeNames
--------                                                               -------- ----------------
System.__ComObject                                                        False System.__ComObject

[↓] [vPodans] # ensure correct extension OID that should be 2.5.29.17
[↓] [vPodans] $SAN.ObjectId

                                   Name FriendlyName                            Value
                                   ---- ------------                            -----
                                    183 Subject Alternative Name                2.5.29.17


[↓] [vPodans] # ensure if all necessary names are added correctly
[↓] [vPodans] $SAN.AlternativeNames

                                   Type strValue                                ObjectId
                                   ---- --------                                --------
                                      3 iLO-ShortName
                                      3 iLO-FQDN.company.com

Looks pretty nice. Now we need to retrieve encoded data. While IX509ExtensionAlternativeNames ineherits from IX509Extension, all methods from IX509Extension are available here and there is a method RawData() that accepts only one argument that specifies output encoding as shown here: EncodingType Enumeration. Unfortunately there is no standard encoding for pure byte array. However there is native .NET Convert::FromBase64String() method that converts Base64 string to a byte array. Therefore for RawData method we'll specify Base64 encoding without headers (0x1) and convert the Base64 string to a byte array:

[↓] [vPodans] $SAN.RawData(1)
MCWBDWlMTy1TaG9ydE5hbWWBFGlMTy1GUUROLmNvbXBhbnkuY29t

[↓] [vPodans] $bytes = [convert]::FromBase64String($SAN.RawData(1))
[↓] [vPodans] $bytes[0..5]
48
37
130
13
105
76
[↓] [vPodans]

I have displayed first 6 array members, so we can ensure that this is a byte array. Now we need to reformat this array to a string where each character is constructed from two bytes in little-endian format. You may ask: where this requirement is described? I really don't know and there is no MSDN article that will describe this (this is one of the things why I hate Microsoft). This requirement was found during my internal investigations. So there are to choices for you — trust me or to not trust me. While there are no constructors or methods that will do this reformat stuff, I wrote the following function:

function ConvertTo-DERstring ([byte[]]$bytes) {
    if ($bytes.Length % 2 -eq 1) {$bytes += 0}
    $SB = New-Object System.Text.StringBuilder
    for ($n = 0; $n -lt $bytes.count; $n += 2) {
        [void]$SB.Append([char]([int]$bytes[$n+1] -shl 8 -bor $bytes[$n]))
    }
    $SB.ToString()
}

Run the following function and specify byte array as function argument:

[↓] [vPodans] function ConvertTo-DERstring ([byte[]]$bytes) {
>>     if ($bytes.Length % 2 -eq 1) {$bytes += 0}
>>     $SB = New-Object System.Text.StringBuilder
>>     for ($n = 0; $n -lt $bytes.count; $n += 2) {
>>         [void]$SB.Append([char]([int]$bytes[$n+1] -shl 8 -bor $bytes[$n]))
>>     }
>>     $SB.ToString()
>> }
>>
[↓] [vPodans] $pvarvalue = ConvertTo-DERstring $bytes
[↓] [vPodans] $pvarvalue
???????????????????m
[↓] [vPodans]

Output string is not quite user-friendly, but certainly acceptable for SetCertificateExtension method. Let's look how this can be done:

[↓] [vPodans] # instantiate ICertAdmin COM object
[↓] [vPodans] $CertAdmin = New-Object -ComObject CertificateAuthority.Admin
[↓] [vPodans] $CertAdmin.SetCertificateExtension("int-pica1\Sysadmins LV Internal Class 1 SubCA-1",43,"2.5.29.17",0x3,0,$pvarv
alue)

and check the results:

SAN

Look, we got it! Now you can issue (approve) this certificate and install to iLO back! And complete code example:

function Add-SANCertificateExtension {
#requires -Version 2.0
    param(
        [Parameter(Mandatory = $true)]
        [string]$BSTRCA,
        [Parameter(Mandatory = $true)]
        [int]$RequestID,
        [Parameter(Mandatory = $true)]
        [String[]]$AlternativeNames
    )
    function ConvertTo-DERstring ([byte[]]$bytes) {
        if ($bytes.Length % 2 -eq 1) {$bytes += 0}
        $SB = New-Object System.Text.StringBuilder
        for ($n = 0; $n -lt $bytes.count; $n += 2) {
            [void]$SB.Append([char]([int]$bytes[$n+1] -shl 8 -bor $bytes[$n]))
        }
        $SB.ToString()
    }    
    $SAN = New-Object -ComObject X509Enrollment.CX509ExtensionAlternativeNames
    $IANs = New-Object -ComObject X509Enrollment.CAlternativeNames
    foreach ($SANstr in $AlternativeNames) {
        $IAN = New-Object -ComObject X509Enrollment.CAlternativeName
        $IAN.InitializeFromString(0x3,$SANstr)
        $IANs.Add($IAN)
    }
    $SAN.InitializeEncode($IANs)
    $bytes = [Convert]::FromBase64String($SAN.RawData(1))
    $pvarvalue = ConvertTo-DERstring $bytes
    $CertAdmin = New-Object -ComObject CertificateAuthority.Admin
    $CertAdmin.SetCertificateExtension($BSTRCA,$RequestID,"2.5.29.17",0x3,0x0,$pvarvalue)
}

and function usage examples:

Add-SANCertificateExtension "CA1\company-CA" 48 "custom name1","custom name2","custom name 3"

in first argument you specify CA configuration string in the following format: CAComputerName\CAName. As the second argument you specify Requiest ID. The request ID can be determined by CertReq.exe utility or by looking to Pending Request node in CertSrv.msc MMC snap-in. As the last argument you specify SAN names. Currently this script supports DNS type names. If you wish to add more than one SAN name you may specify them and each name must be separated by comma.

This script doesn't perform any error handling.

Greetings!


Share this article:

Comments:

Unknown Identity

Thanks Vadims very useful and interesting article! Arman.

Andy Arismendi

I stumbled upon your article while researching how to create a alternative name that is an IP address using the CertEnroll API. From the MSDN documentation: http://msdn.microsoft.com/en-us/library/aa374981%28v=vs.85%29.aspx Here's what i've got: (New-Object -ComObject X509Enrollment.CAlternativeName).InitializeFromRawData(8, 0x1, $rawData) What i'm having trouble with is $rawData... I'm not sure how to convert "10.0.1.2" to "A BSTR variable that contains the DER-encoded data." as the documentation says... Can you help?

Andy Arismendi

After a bit more experimentation I was able to get it using this: $ip = [Convert]::ToBase64String(([System.Net.IPAddress] "10.0.1.2").GetAddressBytes()) (New-Object -ComObject X509Enrollment.CAlternativeName).InitializeFromRawData(8, 0x1, $ip) -Andy Arismendi

AlexD

....You may ask: where this requirement is described? I really don't know and there is no MSDN article that will describe this.... MSDN says that this parameter is UNICODE string. That defines this requirement you stumbled upon. So, everything is documented correctly in MSDN.

Alberto de_la_Torre

Isn't it far easier to just add the san info to the certreq command line. Example: certreq -submit -attrib "CertificateTemplate:HPilo\nSAN:dns=ilo -veeam.domain.com&ipaddress=10.0.10.27&url=https://ilo-veeam" ilo-veeam.req.txt

Vadims Podans

Unfortunately � no. Because it requires to allow SAN passing as attribute (that is disabled by default), that is unsafe.

Duncan Wilkins

Hi Vadmins, how would I go about customising this script to add RFC822 SANs. I've tried changing the initialize value from 0x3 to 0x2 and that works for some addresses; but if the address is name@domain.co.nz for example, a dot is appended to the SAN in the certificate. Any ideas? Cheers D

Vadims Podans

I will look into this.

Vadims Podans

Hi! I've tried your example and it works well. I can't repro your issue. That means that you can switch 0x3 to 0x2 to use RFC822 names.

Dr.Sigmund

there is a method RawData() that accepts only one argument that specifies output encoding as shown here: EncodingType Enumeration. Unfortunately there is no standard encoding for pure byte array.

Well, there is XCN_CRYPT_STRING_BINARY = 2, which immediately returns BSTR with what we need - DER representation of CSR extension value. No need to do base64 decode, no need for conversion via ConvertTo-DERstring function. The result of RawData(2) can be directly passed to SetCertificateExtension. Unfortunately, as per my tests, it seems to be buggy, at least on Windows Server 2008 R2: if data length is odd, then the last byte will be missing. That is, if extension DER size is, say, 13 bytes, then RawData(2) returns a string consisting of just 6 chars, instead of 7 chars with hi-order byte of last char set to 0 (or any value), as it should be. For even lengths it works normally. The base64-encoded value returned by RawData(1) is correct for both even and odd data sizes.

 

Now we need to reformat this array to a string where each character is constructed from two bytes in little-endian format.

I would rephrase it like "SetCertificateExtension needs BSTR which contains DER-representation of extension value". Still tricky, but a little bit more understandable. They just use BSTR as an untyped data buffer.

 

So there are to choices for you — trust me or to not trust me.

Not that I don't trust you ;-) , but the content of ConvertTo-DERstring function looks like an excersise in writing uselessly complex and strange code. You get an array of bytes, convert it to array of strings with hex representation of byte values, then concatenate them in pairs to get string hex representation of byte pairs (that is, WORDs), convert strings back to integer values, and finally cast to char as UTF-16 codes. Wow! And - why? That would be much more simple to do that without intermediate conversion to strings:

function ConvertTo-DERstring ([byte[]]$bytes) {
    $SB = New-Object System.Text.StringBuilder
    for ($n = 0; $n -lt $bytes.count; $n = $n + 2) {
        [void]$SB.Append([char]($bytes[$n+1] * 256 + $bytes[$n]))
    }
    $SB.ToString()
}

The function does nothing more than a kind of typecast - for a given byte array returns a string which is represented by exactly the same byte sequence (excluding length and terminating NULL, of course). Yes, I tested it, and yes, it works.

Vadims Podāns

> it seems to be buggy, at least on Windows Server 2008 R2

actually, it is buggy on all supported platforms (including Windows Server 2016). This is why I developed my own solution.

> but the content of ConvertTo-DERstring function looks like an excersise in writing uselessly complex and strange code

Ok, the code is 6 years old and it was my first attempt. And my recent version of the function is oneliner:

[Text.Encloding]::Unicode.GetString($bytes)

This code will give BSTR string.

Dr.Sigmund

And my recent version of the function is oneliner:

I knew there should be simpler solution ;-) But that doesn't handle odd byte number. Something like this should be done first:

if ($bytes.count % 2 -eq 1) {

  $x += (0)

}

Dr.Sigmund

Sorry, "$bytes += (0)" of course instead of "$x += (0)"

Dr.Sigmund

That's perfect!

BTW, it looks like the bug with returning binary DER value is not limited just to
CX509ExtensionAlternativeNames.GetRawData method, it appers in other places. For example, CX500DistinguishedName.EncodedName property exhibits the same problem.

Dr.Sigmund

Here is my current version:

That's perfect!

Unfortunately, it doesn't work that simple :-( . There is a problem with Unicode high- and low-surrogate code points which occupy ranges 0xD800-0xDBFF and 0xDC00-0xDFFF. As a result, any byte pair where second byte is in range 216-223 is replaced with Unicode replacement character 0xFFFD during conversion:

PS C:\Users\...> [int][Text.Encoding]::Unicode.GetString( (37, 223) )[0]
65533
PS C:\Users\...> [int][Text.Encoding]::Unicode.GetString( (190, 216) )[0]
65533

And your C# code is probably also affected, though I didn't check. Not sure, but maybe that's possible to override this behavior using properties of UnicodeEncoding class, but for now I'll just switch back to manual conversion of byte pair to Word.

Vadims Podāns

I wasn't aware of this and yes, my C# code is affected too. I'll adjust my code to use your version.

Thanks!

Alex

Hello guys.

Article is great, eveything is clear and understandable. And it works, with one excepton.

When we have FQDN containing odd number of symbols, let's say Odd.domain.eu (13 symbols), if we'll not add a "0" to the end of byte[], the conversion to Unicode will produce for us broken string with FDFF in the end instead of last "u" symbol (0x75), as following:

$bytes = [convert]::FromBase64String($SAN.RawData(1)) will give us following: 30 0f 82 0d 4f 64 64 2e 64 6f 6d 61 69 6e 2e 65 75

30 0f - Sequence, Length 15
   82 0d 4f64642e646f6d61696e2e6575 - IA5String, length 13

and ASN.1 decoded it will be following:

SEQUENCE(1 elem)
  [2]Odd.domain.eu

If we'll try to pass this array to Unicode.GetString() it produces the string, but last "u" symbol is replaced with "FD FF"as following:

30 0f -  - Sequence, Length 15 (but in reality 16)
   82 0d 4f64642e646f6d61696e2e65fdff

Passing this value to SetCertificateExtension() will produce certificate with empty SAN extension, while actual data still will be in file.

Even number of symbols.

If we'll provide FQDN with even number of symbols, like Odd1.domain.eu, we'll get correct string and ASN.1 encoded OCTET STRING will look as following:

04 12 - OCTET STRING
   30 10 - SEQUENCE
      82 0E 4F6464312E646F6D61696E2E6575 - our data

Decoded:

OCTET STRING(1 elem)
  SEQUENCE(1 elem)
    [2]Odd1.domain.eu
So, everything looks absolutely fine.

 

Now let's go back to our Odd string.

After fixing byte array odd count by adding "0" to the end we getting correct result from Unicode.GetString(), SAN is correct and appears (we can see it now) in certificate, it works, but not everywhere. OpenShift (GoLang praticularly) fails decoding SAN extension because of fillowing.

After passing amended byte array to Unicode.GetString() we getting following result with "00" at the end (expected, we added it)

04 12 - OCTET STRING
   30 10 - SEQUENCE
      82 0E 4F64642E646F6D61696E2E657500

And this double zero fucks-up decoding of OCTET STRING, while all data is still there, and then just seen as octet string, which does not encapsulates other data:

OCTET STRING (18 byte) 300F820D4F64642E646F6D61696E2E657500

While OpenShift, according to https://www.ietf.org/rfc/rfc5280.txt, section 4.2.1.6, expecting following:

OCTET STRING(1 elem)
    SEQUENCE(1)
      [2] Odd.domain.eu

 

So the Summary.

To pass data to SetCertificateExtension() we must encode it as Unicode. If number of characters in provided FQDN is odd we must add 0 to the end, if we add o to the and it appears at the end of OCTET STRING, and this prevents OpenShift from successful decoding of SAN extension. It seems that Windows machines successfully decodes mentioned OCTET STRING, probably ignores 00 at the end, and can see the SAN and it works.

If SAN added to CSR during it's generation - no problems occurs. I see that 13 characters length SAN FQDN is in mentioned OCTET STRING and there is no trailing 00:

04 11

    30 0F

        82 0D 4F 64 64 2E 64 6F 6D 61 69 6E 2E 65 75


OCTET STRING(1 elem)
  SEQUENCE(1 elem)
    [2]Odd.domain.eu
 

What do you think guys, is it possible to solve this problem with odd length? How it works when SAN included into request during CSR generation? What is the difference comparing to method used by SetCertificateExtension()?

Tried workarounds to fix ASN.1 decoding, but was not tested if such cert will work:

Add 0x20 (space) to the end of line: it works, but i do not know how such certificate will be treated by clients of the server, where such cert is installed.

Add 0x13 (CR) to the end of line: ASN decoder shows octets instead of readable text.

Links:

Odd1.domain.eu: https://lapo.it/asn1js/#04123010820E4F6464312E646F6D61696E2E6575

Odd.domain.eu with fixed byte array length: https://lapo.it/asn1js/#04123010820E4F64642E646F6D61696E2E657500

Odd.domain.eu with MANUALLY (explained below) amended lengths and removed 00: https://lapo.it/asn1js/#0411300F820D4F64642E646F6D61696E2E6575

04 12 30 10 82 0E 4F64642E646F6D61696E2E657500 changed to

04 11 30 0F 82 0D 4F64642E646F6D61696E2E6575

 

 

Raffaele

Hi Vadmins, May you change the script to insert IP address instead of DNS name?

Vadims Podāns

You really should not use IP addresses. Instead, you should use DNS names. If you don't have DNS, use HOSTS file to create Name->IP mapping.

DmitryNN

Add to pending certificate request:
1. Extension Alternative Names - DNS and IP address
2. Extension Key Usage
3. Extension Enhanced Key Usage

function Edit-CertificateRequest {
<#
.SYNOPSIS
Modify pending certificate request on Microsoft Certification Authority server
.DESCRIPTION
Add (replace existing) to pending certificate request on Microsoft Certification Authority server:
1. Extension Alternative Names - DNS and IP address
2. Extension Key Usage
3. Extension Enhanced Key Usage
.PARAMETER CAName
Microsoft Certification Authority server connection as string ("Computer name\CA name").
"temp-itil\NNOV-HSE-ROOT-CA" is the default.
.PARAMETER RequestID
ID of pending certificate request as integer value.
.PARAMETER AlternativeNames
FQDN DNS name(s) and/or computer name(s) and/or IP address(es) for replace existing or create new "2.5.29.17" extension as string with values separated by comma with no comma at the end.
None is the default.
For example:
"example.corp.local","example","10.0.0.1"
For example:
"example.corp.local"
For example:
"10.0.0.1"
.PARAMETER ExtensionKeyUsage
Policy(ies) for replace existing or create new "2.5.29.15" extension as HEX value ("0x..").
None is the default.
Key Usage is the composition of HEX Values of requested policies.
For example:
HEX value for policies DATA ENCIPHERMENT, KEY ENCIPHERMENT and DIGITAL SIGNATURE is 0x10 + 0x20 + 0x80 = 0xB0
Policy: HEX:
define CERT_DIGITAL_SIGNATURE_KEY_USAGE 0x80
define CERT_NON_REPUDIATION_KEY_USAGE 0x40
define CERT_KEY_ENCIPHERMENT_KEY_USAGE 0x20
define CERT_DATA_ENCIPHERMENT_KEY_USAGE 0x10
define CERT_KEY_AGREEMENT_KEY_USAGE 0x08
define CERT_KEY_CERT_SIGN_KEY_USAGE 0x04
define CERT_OFFLINE_CRL_SIGN_KEY_USAGE 0x02
define CERT_CRL_SIGN_KEY_USAGE 0x02
define CERT_ENCIPHER_ONLY_KEY_USAGE 0x01
.PARAMETER ExtensionEnhancedKeyUsages
Policy(ies) for replace existing or create new "2.5.29.37" extension as string with values separated by comma with no comma at the end.
None is the default.
For example:
"1.3.6.1.5.5.7.3.1","1.3.6.1.5.5.7.3.2"
For example:
"1.3.6.1.5.5.7.3.2"
Policies ID list - https://docs.microsoft.com/ru-ru/windows/desktop/api/certenroll/nn-certenroll-ix509extensionenhancedkeyusage
.INPUTS
None. You cannot pipe objects to Add-Extension.
.OUTPUTS
System.String. Edit-CertificateRequest returns a modifications log.
.EXAMPLE
C:\PS> Edit-CertificateRequest -RequestID 67 -AlternativeNames "bladeencl-01-11.nnov.corp.local","bladeencl-01-11","172.17.0.37" -ExtensionKeyUsage "0xB0" -ExtensionEnhancedKeyUsages "1.3.6.1.5.5.7.3.1","1.3.6.1.5.5.7.3.2"
Microsoft Certification Authority server - temp-itil\NNOV-HSE-ROOT-CA
Alternative Name - DNS:bladeencl-01-11.nnov.corp.local
Alternative Name - DNS:bladeencl-01-11
Alternative Name - IP:172.17.0.37
Key Usage - 00000000 B0
Enhanced Key Usage - 1.3.6.1.5.5.7.3.1
Enhanced Key Usage - 1.3.6.1.5.5.7.3.2
.EXAMPLE
C:\PS> Edit-CertificateRequest -CAName "Computer-Name\CA-Name" -RequestID 67 -AlternativeNames "example.corp.local","example","example2.local","example2","10.0.0.1","172.17.0.37" -ExtensionKeyUsage "0xB0" -ExtensionEnhancedKeyUsages "1.3.6.1.5.5.7.3.1","1.3.6.1.5.5.7.3.2"
Microsoft Certification Authority server - Computer-Name\CA-Name
Alternative Name - DNS:example.corp.local
Alternative Name - DNS:example
Alternative Name - DNS:example2.local
Alternative Name - DNS:example2
Alternative Name - IP:10.0.0.1
Alternative Name - IP:172.17.0.37
Key Usage - 00000000 B0
Enhanced Key Usage - 1.3.6.1.5.5.7.3.1
Enhanced Key Usage - 1.3.6.1.5.5.7.3.2
.EXAMPLE
C:\PS> Edit-CertificateRequest -RequestID 67 -AlternativeNames "example.corp.local"
Microsoft Certification Authority server - temp-itil\NNOV-HSE-ROOT-CA
Alternative Name - DNS:example.corp.local
.EXAMPLE
C:\PS> Edit-CertificateRequest -RequestID 67 -ExtensionKeyUsage "0xB0"
Microsoft Certification Authority server - temp-itil\NNOV-HSE-ROOT-CA
Key Usage - 00000000 B0
.EXAMPLE
C:\PS> Edit-CertificateRequest -RequestID 67 -ExtensionEnhancedKeyUsages "1.3.6.1.5.5.7.3.1","1.3.6.1.5.5.7.3.2"
Microsoft Certification Authority server - temp-itil\NNOV-HSE-ROOT-CA
Enhanced Key Usage - 1.3.6.1.5.5.7.3.1
Enhanced Key Usage - 1.3.6.1.5.5.7.3.2
.LINK
https://docs.microsoft.com/ru-ru/windows/desktop/api/certadm/nf-certadm-icertadmin-setcertificateextension
.LINK
https://docs.microsoft.com/ru-ru/windows/desktop/SecCertEnroll/supported-extensions
.LINK
https://docs.microsoft.com/ru-ru/windows/desktop/api/certenroll/
.LINK
https://docs.microsoft.com/ru-ru/windows/desktop/api/certenroll/nn-certenroll-ix509extensionenhancedkeyusage
#>
#requires -Version 2.0
param(
[Parameter(Mandatory = $false)]
[string]$CAName = 'temp-itil\NNOV-HSE-ROOT-CA',
[Parameter(Mandatory = $true,
HelpMessage = "Enter ID of pending certificate request")]
[int]$RequestID,
[Parameter(Mandatory = $false)]
[String[]]$AlternativeNames,
[Parameter(Mandatory = $false)]
[int]$ExtensionKeyUsage,
[Parameter(Mandatory = $false)]
[String[]]$ExtensionEnhancedKeyUsages
)
[string]$ResultLogTmpStr = ""
[string[]]$ResulLog = "Microsoft Certification Authority server - " + $CAName
function ConvertTo-DERstring ([byte[]]$bytes) {
if ($bytes.Length % 2 -eq 1) {$bytes += 0}
$SB = New-Object System.Text.StringBuilder
for ($n = 0; $n -lt $bytes.count; $n += 2) {
[void]$SB.Append([char]([int]$bytes[$n + 1] -shl 8 -bor $bytes[$n]))
}
$SB.ToString()
}
If ($AlternativeNames) {
$CAlternativeNames = New-Object -ComObject X509Enrollment.CAlternativeNames
foreach ($AlternativeName in $AlternativeNames) {
$IsIP = ($AlternativeName -As [IPAddress]) -As [Bool]
$CAlternativeName = New-Object -ComObject X509Enrollment.CAlternativeName
if ($IsIP) {
$ResultLogTmpStr = "Alternative Name - IP:" + $AlternativeName
$ResulLog = $ResulLog + $ResultLogTmpStr
$AlternativeName = [Convert]::ToBase64String(([System.Net.IPAddress] $AlternativeName).GetAddressBytes())
$CAlternativeName.InitializeFromRawData(8, 0x1, $AlternativeName)
}
else {
$ResultLogTmpStr = "Alternative Name - DNS:" + $AlternativeName
$ResulLog = $ResulLog + $ResultLogTmpStr
$CAlternativeName.InitializeFromString(0x3, $AlternativeName)
}
$CAlternativeNames.Add($CAlternativeName)
}
$SAN = New-Object -ComObject X509Enrollment.CX509ExtensionAlternativeNames
$SAN.InitializeEncode($CAlternativeNames)
$bytes = [Convert]::FromBase64String($SAN.RawData(1))
$pvarvalue = ConvertTo-DERstring $bytes
$CertAdmin = New-Object -ComObject CertificateAuthority.Admin
$CertAdmin.SetCertificateExtension($CAName, $RequestID, "2.5.29.17", 0x3, 0x0, $pvarvalue)
}
If ($ExtensionKeyUsage) {
$ResultLogTmpStr = "Key Usage - " + ($ExtensionKeyUsage | Format-Hex)
$ResulLog = $ResulLog + $ResultLogTmpStr
$CX509ExtensionKeyUsage = New-Object -com "X509Enrollment.CX509ExtensionKeyUsage.1"
$CX509ExtensionKeyUsage.InitializeEncode($ExtensionKeyUsage)
$bytes = [Convert]::FromBase64String($CX509ExtensionKeyUsage.RawData(1))
$pvarvalue = ConvertTo-DERstring $bytes
$CertAdmin = New-Object -ComObject CertificateAuthority.Admin
$CertAdmin.SetCertificateExtension($CAName, $RequestID, "2.5.29.15", 0x3, 0x0, $pvarvalue)
}
If ($ExtensionEnhancedKeyUsages) {
$ekuoids = New-Object -com "X509Enrollment.CObjectIds.1"
foreach ($ExtensionEnhancedKeyUsage in $ExtensionEnhancedKeyUsages) {
$ResultLogTmpStr = "Enhanced Key Usage - " + $ExtensionEnhancedKeyUsage
$ResulLog = $ResulLog + $ResultLogTmpStr
$serverauthoid = New-Object -com "X509Enrollment.CObjectId.1"
$serverauthoid.InitializeFromValue($ExtensionEnhancedKeyUsage)
$ekuoids.Add($serverauthoid)
}
$CX509ExtensionEnhancedKeyUsage = New-Object -com "X509Enrollment.CX509ExtensionEnhancedKeyUsage.1"
$CX509ExtensionEnhancedKeyUsage.InitializeEncode($ekuoids)
$bytes = [Convert]::FromBase64String($CX509ExtensionEnhancedKeyUsage.RawData(1))
$pvarvalue = ConvertTo-DERstring $bytes
$CertAdmin = New-Object -ComObject CertificateAuthority.Admin
$CertAdmin.SetCertificateExtension($CAName, $RequestID, "2.5.29.37", 0x3, 0x0, $pvarvalue)
}
$ResulLog
}
 

Werther§ Lusuardi

Hi

 

With your script i can add the follwing SAN Informations:

DNS Name=

IP Address=

Is there any possibility to also add further types of informations like

RFC822 Name=

URL=

Directory Address=

Other Name: DS Object Guid=

Other Name: Principal Name=

Vadims Podāns

@Werther§ Lusuardi

Take a look at IAlternativeName COM interface for additional alternative name options. The syntax is very similar.

 

Austin

Thanks for the tips, but since I am not a big script guy. How do you put this together to to add the IP in a complete script?

Johnny Pan

Hi Vadims,

I'm trying to append SAN to a existing CSR that has not been submitted to a CA. My plan is to do it using the 'certreq -policy' command.

However, I need to be able to do it via c#. Do you know which method I should use? Is the method you descibred here the same as 'certreq -policy'?


Thank you.


Post your comment:

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