Hello, PowerShell Crypto Guy is here again. A time ago I went through one article: http://www.solution-soft.com/whitepapers/Hyper-V/TimeMachine_Hyper-V_guide.htm which is pretty interesting. However it complains that a related TechNet article Configuring Certificates for Virtual Machine Connection won't work due of the following limitations:

  • PowerShell or DOS cannot seem to create a Qword value.
  • The VMMS service requires two critical certificates Extension Fields These are:
    Field= “Key Usage” Value = “Key Encipherment, Data Encipherment (30)”
    Field= “1.3.6.1.4.1.311.62.1.1.1 Value = “02 01 04"
    These fields are present in the local host certificates created locally by VMMS. As of the writing of this paper & working with Microsoft support we have been unable to create a certificate with these values using any other means.

Fortunately both statements are not correct:

  • PowerShell definitely can create QWORD registry value. Though native ways are complicated and reg.exe would be much easier.
  • PowerShell definitely can create a certificate that meets all these requirements.

Here is a PowerShell script that solves a task:

  • Configures VMMS service to not use default self-signed certificates (which are valid only for 1 year);
  • Generates a new self-signed certificate which is valid for 50 years and meets all certificate requirements;
  • assigns correct permissions for a private key;
  • assigns this certificate to use by VMMS service.

As always (in such cases) we will use dramatically-extremely powerful CertEnroll interfaces. I really love them because they allows to generate any certificate/certificate request as you want. The most important benefit is that these interfaces are self-explanatory and you may deal with them even if you have only basic knowledge. Former XErnoll interfaces wasn't pretty easy.

#####################################################################
# New-VMMSCertificate.ps1
# Version 1.0
#
# Creates and configures a self-signed certificate for VMMS service
# with 50 years validity.
#
# Vadims Podans (c) 2011
# http://en-us.sysadmins.lv/
#####################################################################
#requires -Version 2.0

# set registry path shortcut
$regPath = "HKLM\Software\Microsoft\Windows NT\CurrentVersion\Virtualization"
#############################################################
# disable default self-signed certificate usage             #
#############################################################

reg add $regPath /v "DisableSelfSignedCertificateGeneration" /f /t REG_QWORD /d 1

#############################################################
# generate a certificate                                    #
#############################################################

# actual NotBefore (or Valid From) value is set to 1 day earlier. This is because by default CertEnroll
# interfaces generates a certificate that will balid only after 3-4 hours after certificate generation.
$NotBefore = (Get-Date).AddDays(-1)
$NotAfter = (Get-Date).AddYears(50)
try {$domain = ([System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()).Name}
catch {}
if ($domain -eq $null) {$fqdn = $Env:COMPUTERNAME} else {$fqdn = $env:COMPUTERNAME + "." + $domain}
$SubjectDN = New-Object -ComObject X509Enrollment.CX500DistinguishedName
$SubjectDN.Encode("CN=$fqdn", 0x0)

$OID = New-Object -ComObject X509Enrollment.CObjectID
$OID.InitializeFromValue("1.3.6.1.5.5.7.3.1")
$OIDs = New-Object -ComObject X509Enrollment.CObjectIDs
$OIDs.Add($OID)
$EKU = New-Object -ComObject X509Enrollment.CX509ExtensionEnhancedKeyUsage
$EKU.InitializeEncode($OIDs)

$KU = New-Object -ComObject X509Enrollment.CX509ExtensionKeyUsage
$KU.InitializeEncode(0x30)
$KU.Critical = $false

# here is a trick how to generate a custom certificate extension. Just instantiate
# IX%09Extension interface object and initialize it from the desired values.
$Unknown = New-Object -ComObject X509Enrollment.CX509Extension
$OID = New-Object -ComObject X509Enrollment.CObjectID
$OID.InitializeFromValue("1.3.6.1.4.1.311.62.1.1.1")
# AgEE represents a extension value in the Base64 form. Original value is a
# string of octets (02, 01, 04).
$Unknown.Initialize($OID,0x1,"AgEE")

$PrivateKey = New-Object -ComObject X509Enrollment.CX509PrivateKey
$PrivateKey.ProviderName = "Microsoft RSA SChannel Cryptographic Provider"
$PrivateKey.KeySpec = 0x1
# here we set Key Encipherment and Data Encipherment key usage values.
$PrivateKey.KeyUsage = 0x30
$PrivateKey.Length = 2048
$PrivateKey.MachineContext = 0x1
# since the certificate is used for testing purposes and not intended for productional
# environment we allow private key export.
$PrivateKey.ExportPolicy = 0x2
$PrivateKey.Create()

$Cert = New-Object -ComObject X509Enrollment.CX509CertificateRequestCertificate
$Cert.InitializeFromPrivateKey(0x2,$PrivateKey,"")
$Cert.Subject = $SubjectDN
$Cert.Issuer = $Cert.Subject
$Cert.NotBefore = $NotBefore
$Cert.NotAfter = $NotAfter
$Cert.X509Extensions.Add($EKU)
$Cert.X509Extensions.Add($KU)
$Cert.X509Extensions.Add($Unknown)
# completing certificate request template building
$Cert.Encode()

$Request = New-Object -ComObject X509Enrollment.CX509enrollment
# process request
$Request.InitializeFromRequest($Cert)
# retrieve certificate encoded in Base64.
$endCert = $Request.CreateRequest(0x1)
$Cert = [Security.Cryptography.X509Certificates.X509Certificate2][Convert]::FromBase64String($endCert)
# install certificate to user store
$Request.InstallResponse(0x2,$endCert,0x1,"")
$Cert = dir cert:\localmachine\my | ?{$_.Thumbprint -eq $Cert.Thumbprint}

#############################################################
# configure private key access permissions                  #
#############################################################
$PKPath = "$env:ALLUSERSPROFILE\Microsoft\Crypto\RSA\MachineKeys\$($Cert.PrivateKey.CspKeyContainerInfo.UniqueKeyContainerName)"
icacls $PKPath /grant "*S-1-5-83-0:(R)"

#############################################################
# configure VMMS service to use a new generated certificate #
#############################################################

reg add $regPath /v "AuthCertificateHash" /f /t REG_BINARY /d $cert.Thumbprint
Restart-Service vmms

The script was tested with Windows Server 2008 R2 and should work fine (at least it works for me). Save the script to a file and run the script without any parameters. Or just copy/paste the code to the *elevated* PowerShell console. And here is an example of the certificate:

VMMS Certificate

Warning: the script SHOULD NOT be used in the production environment.

Enjoy the Hyper-V Time Machine with the power of Windows PowerShell!


Share this article:

Comments:


Post your comment:

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