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.
In this section I will discuss about CA server configuration for request manual approval by CA Manager or CA Administrator.
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.
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:
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.
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.
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
In this we will explore ICertAdmin2::SetCertificateExtension method that will add additional extensions to certificate request. This method requires the following arguments:
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:
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!
Thanks Vadims very useful and interesting article! Arman.
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?
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
....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.
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
Unfortunately � no. Because it requires to allow SAN passing as attribute (that is disabled by default), that is unsafe.
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
I will look into this.
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.
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.
> 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.
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)
}
Sorry, "$bytes += (0)" of course instead of "$x += (0)"
Here is my current version: https://github.com/Crypt32/pkix.net/blob/master/PKI/Utils/CryptographyUtils.cs#L86
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.
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.
I wasn't aware of this and yes, my C# code is affected too. I'll adjust my code to use your version.
Thanks!
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
Hi Vadmins, May you change the script to insert IP address instead of DNS name?
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.
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
}
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=
@Werther§ Lusuardi
Take a look at IAlternativeName COM interface for additional alternative name options. The syntax is very similar.
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?
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:
Comments: