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:
Fortunately both statements are not correct:
Here is a PowerShell script that solves a task:
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:
Warning: the script SHOULD NOT be used in the production environment.
Enjoy the Hyper-V Time Machine with the power of Windows PowerShell!
Post your comment:
Comments: