Configure Encryption for a Virtual Subnet

Applies To: Windows Server

Virtual network encryption allows encryption of virtual network traffic between virtual machines that communicate with each other within subnets marked as ‘Encryption Enabled.’ It also utilizes Datagram Transport Layer Security (DTLS) on the virtual subnet to encrypt packets. DTLS protects against eavesdropping, tampering, and forgery by anyone with access to the physical network.

Virtual network encryption requires:

  • Encryption certificates installed on each of the SDN-enabled Hyper-V hosts.
  • A credential object in the Network Controller referencing the thumbprint of that certificate.
  • Configuration on each of the Virtual Networks contain subnets that require encryption.

Once you enable encryption on a subnet, all network traffic within that subnet is encrypted automatically, in addition to any application-level encryption that may also take place. Traffic that crosses between subnets, even if marked as encrypted, is sent unencrypted automatically. Any traffic that crosses the virtual network boundary also gets sent unencrypted.

Tip

If you must restrict applications to only communicate on the encrypted subnet, you can use Access Control Lists (ACLs) only to allow communication within the current subnet. For more information, see Use Access Control Lists (ACLs) to Manage Datacenter Network Traffic Flow.

Creating the Encryption Certificate

Each host must have an encryption certificate installed. You can use the same certificate for all tenants or generate a unique one for each tenant.

Step 1: Generate the certificate

    $subjectName = "EncryptedVirtualNetworks"
    $cryptographicProviderName = "Microsoft Base Cryptographic Provider v1.0";
    [int] $privateKeyLength = 1024;
    $sslServerOidString = "1.3.6.1.5.5.7.3.1";
    $sslClientOidString = "1.3.6.1.5.5.7.3.2";
    [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 
    $key.Create()

    #Configure Eku
    $serverauthoid = new-object -com "X509Enrollment.CObjectId.1"
    $serverauthoid.InitializeFromValue($sslServerOidString)
    $clientauthoid = new-object -com "X509Enrollment.CObjectId.1"
    $clientauthoid.InitializeFromValue($sslClientOidString)
    $ekuoids = new-object -com "X509Enrollment.CObjectIds.1"
    $ekuoids.add($serverauthoid)
    $ekuoids.add($clientauthoid)
    $ekuext = new-object -com "X509Enrollment.CX509ExtensionEnhancedKeyUsage.1"
    $ekuext.InitializeEncode($ekuoids)

    # 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.X509Extensions.Add($ekuext)
    $cert.HashAlgorithm = $hashAlgorithmObject
    $cert.Encode()

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

After running the script, a new certificate appears in the My store:

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 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"}
[System.io.file]::WriteAllBytes("c:\$subjectName.pfx", $cert.Export("PFX", "secret"))
Export-Certificate -Type CERT -FilePath "c:\$subjectName.cer" -cert $cert

Step 3: Install the certificates 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 4: 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
    $certificate.import($certFullPath)

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

    $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")
    $store.open("MaxAllowed")
    $store.add($certificate)
    $store.close()

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

Step 5: Repeat for each server in your environment

After repeating for each server, you should have a certificate installed in the root and my store of each Hyper-V host.

Step 6: Verify the installation of the certificate

Verify the certificates 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

Step 7: Make note of the Thumbprint

You must make a note of the thumbprint because you need it to create the certificate credential object in the network controller.

Creating the Certificate Credential

After you install the certificate on each of the Hyper-V hosts connected to the network controller, you must now configure the network controller to use it. To do this, you must create a credential object containing the certificate thumbprint from the machine with the Network Controller PowerShell modules installed.

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

# Replace with your Network Controller URI
$uri = "https://nc.contoso.com"

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

Tip

You can reuse this credential for each encrypted virtual network, 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

$vnet.properties.EncryptionCredential = $certcred

# Replace the Subnets index with the value corresponding to the subnet you want encrypted.  
# Repeat for each subnet where encryption is needed
$vnet.properties.Subnets[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

You’re done once you complete these steps.

Note

When communicating with another VM on the same subnet, whether its currently connected or connected at a later time, the traffic gets encrypted automatically.