Configure Encryption for a Virtual Subnet

Applies To: Windows Server

This topic contains the following sections which describe the steps required to enable encryption on a Virtual Network.

Creating the Encryption Certificate

An encryption certificate is required to be installed on each host where encryption is to be used. You can use the same certificate for all tenants, or generate a unique certificate per-tenant if required.

Step 1: Generate the certificate

$subjectName = "EncryptedVirtualNetworks"
$cryptographicProviderName = "Microsoft Base Cryptographic Provider v1.0";
[int] $privateKeyLength = 1024;
$sslServerOidString = "";
$sslClientOidString = "";
[int] $validityPeriodInYear = 5;

$name = new-object -com "X509Enrollment.CX500DistinguishedName.1"
$name.Encode("CN=" + $SubjectName, 0)

#Generate Key
$key = new-object -com "X509Enrollment.CX509PrivateKey.1"
$key.ProviderName = $cryptographicProviderName
$key.KeySpec = 1 #X509KeySpec.XCN_AT_KEYEXCHANGE
$key.Length = $privateKeyLength
$key.MachineContext = 1
$key.ExportPolicy = 0x2 #X509PrivateKeyExportFlags.XCN_NCRYPT_ALLOW_EXPORT_FLAG 

#Configure Eku
$serverauthoid = new-object -com "X509Enrollment.CObjectId.1"
$clientauthoid = new-object -com "X509Enrollment.CObjectId.1"
$ekuoids = new-object -com "X509Enrollment.CObjectIds.1"
$ekuext = new-object -com "X509Enrollment.CX509ExtensionEnhancedKeyUsage.1"

# Set the hash algorithm to sha512 instead of the default sha1
$hashAlgorithmObject = New-Object -ComObject X509Enrollment.CObjectId
$hashAlgorithmObject.InitializeFromAlgorithmName( $ObjectIdGroupId.XCN_CRYPT_HASH_ALG_OID_GROUP_ID, $ObjectIdPublicKeyFlags.XCN_CRYPT_OID_INFO_PUBKEY_ANY, $AlgorithmFlags.AlgorithmFlagsNone, "SHA512")

#Request Certificate
$cert = new-object -com "X509Enrollment.CX509CertificateRequestCertificate.1"

$cert.InitializeFromPrivateKey(2, $key, "")
$cert.Subject = $name
$cert.Issuer = $cert.Subject
$cert.NotBefore = (get-date).ToUniversalTime()
$cert.NotAfter = $cert.NotBefore.AddYears($validityPeriodInYear);
$cert.HashAlgorithm = $hashAlgorithmObject

$enrollment = new-object -com "X509Enrollment.CX509Enrollment.1"
$certdata = $enrollment.CreateRequest(0)
$enrollment.InstallResponse(2, $certdata, 0, "")

After running the above you will see a new certificate in the My store of the machine where you ran the script:

PS D:\> dir cert:\\localmachine\my

PSParentPath: Microsoft.PowerShell.Security\Certificate::localmachine\my

Thumbprint                                Subject
----------                                -------
84857CBBE7A1C851A80AE22391EB2C39BF820CE7  CN=MyNetwork
5EFF2CE51EACA82408572A56AE1A9BCC7E0843C6  CN=EncryptedVirtualNetworks

Step 2: Export the certificate to a file You will need two copies of the certificate, one with the private key and one without.

$subjectName = "EncryptedVirtualNetworks"
$cert = Get-ChildItem cert:\localmachine\my | ? {$_.Subject -eq "CN=$subjectName"}
[]::WriteAllBytes("c:\$subjectName.pfx", $cert.Export("PFX", "secret"))
Export-Certificate -Type CERT -FilePath "c:\$subjectName.cer" -cert $cert

After running the above you will now have two certificate files. These need to be installed on each of your hyper-v hosts.

PS C:\> dir c:\$subjectname.*

    Directory: C:\

Mode                LastWriteTime         Length Name
----                -------------         ------ ----
-a----        9/22/2017   4:54 PM            543 EncryptedVirtualNetworks.cer
-a----        9/22/2017   4:54 PM           1706 EncryptedVirtualNetworks.pfx

Step 3: Installing on a Hyper-V host

$server = "Server01"

$subjectname = "EncryptedVirtualNetworks"
copy c:\$SubjectName.* \\$server\c$
invoke-command -computername $server -ArgumentList $subjectname,"secret" {
    param (
        [string] $SubjectName,
        [string] $Secret
    $certFullPath = "c:\$SubjectName.cer"

    # create a representation of the certificate file
    $certificate = new-object System.Security.Cryptography.X509Certificates.X509Certificate2

    # import into the store
    $store = new-object System.Security.Cryptography.X509Certificates.X509Store("Root", "LocalMachine")

    $certFullPath = "c:\$SubjectName.pfx"
    $certificate = new-object System.Security.Cryptography.X509Certificates.X509Certificate2
    $certificate.import($certFullPath, $Secret, "MachineKeySet,PersistKeySet")

    # import into the store
    $store = new-object System.Security.Cryptography.X509Certificates.X509Store("My", "LocalMachine")

    # Important: Remove the certficate files when finished
    remove-item C:\$SubjectName.cer
    remove-item C:\$SubjectName.pfx

Repeat for each server in your environment. You should now have a certificate installed in the root and my store of each Hyper-V host

You can verify the installation of the certificate by checking the contents of the My and Root certificate stores:

PS C:\> enter-pssession Server1

[Server1]: PS C:\> get-childitem cert://localmachine/my,cert://localmachine/root | ? {$_.Subject -eq "CN=EncryptedVirtualNetworks"}

PSParentPath: Microsoft.PowerShell.Security\Certificate::localmachine\my

Thumbprint                                Subject
----------                                -------
5EFF2CE51EACA82408572A56AE1A9BCC7E0843C6  CN=EncryptedVirtualNetworks

PSParentPath: Microsoft.PowerShell.Security\Certificate::localmachine\root

Thumbprint                                Subject
----------                                -------
5EFF2CE51EACA82408572A56AE1A9BCC7E0843C6  CN=EncryptedVirtualNetworks

Make note of the Thumbprint as you will need it for creating the certificate credential object in the network controller.

Creating the Certificate Credential

After the certficate is successfully installed on each of the Hyper-V hosts connected to the network controller, you can configure the network controller to use it.

You will need to create a credential object that contains the certificate thumbprint. You will need to do this from a machine where you have the Network Controller powershell modules installed.

# Replace with thumbprint from your certificate
$thumbprint = "5EFF2CE51EACA82408572A56AE1A9BCC7E0843C6"  

# Replace with your Network Controller URI
$uri = ""

Import-module networkcontroller

$credproperties = new-object Microsoft.Windows.NetworkController.CredentialProperties
$credproperties.Type = "X509Certificate"
$credproperties.Value = $thumbprint
New-networkcontrollercredential -connectionuri $uri -resourceid "EncryptedNetworkCertificate" -properties $credproperties -force

You can reuse this credential for each encrypted virtual netwokr, or you can deploy and use a unique certificate for each tenant.

Configuring a Virtual Network for Encryption

This step assumes you have already created a virtual network name "My Network" and it contains at least one virtual subnet. For information on creating virtual networks, see Create, Delete, or Update Tenant Virtual Networks.

Step 1: Retrieve the Virtual Network and Credential objects from the network controller

$vnet = Get-NetworkControllerVirtualNetwork -ConnectionUri $uri -ResourceId "MyNetwork"
$certcred = Get-NetworkControllerCredential -ConnectionUri $uri -ResourceId "EncryptedNetworkCertificate"

Step 2: Add a reference to the certificate credential and enable encryption on individual subnets.

$ = $certcred

# Replace the Subnets index with the value corresponding to the subnet you want encrypted.  
# Repeat for each subnet where encryption is needed
$[0].properties.EncryptionEnabled = $true

Step 3: Put the updated Virtual Network object into the network controller

New-NetworkControllerVirtualNetwork -ConnectionUri $uri -ResourceId $vnet.ResourceId -Properties $vnet.Properties -force

Once this is complete, no additional steps are required. Any VM that is currently connected and any VM that is later connected to the above subnet will have its traffic encrypted automatically when communicating with another VM on the same subnet.