我有一台可从外部访问的 RD 网关服务器,用于访问一组服务器。它目前使用采用外部 FQDN 的自签名证书,即MyServer.PrimaryLocation.Provider.com,当系统托管在主要位置时,这会起到很好的作用。
当我们需要故障转移到 DR 时,就会出现问题,此时我们无法迁移与服务器关联的 FQDN。这给我们留下了一个新的 FQDNMyServer.SecondaryLocation.Provider.com。
我正在寻找一种方法来支持通过两个 URL 进行的连接,而无需更改证书,这会令用户感到沮丧,而且不幸的是,由于证书的自签名性质,这又是必要的(我们显然不拥有域“Provider.com”,因此无法购买外部 CA 证书)。
非常感谢任何帮助。
答案1
事实证明,使用带有 SAN(主体备用名称)条目的证书可以实现这一点。对于任何感兴趣的人,以下是执行此操作的 PowerShell:
<#
.Synopsis
Creates a new Certificate for an RD Gateway Server
.DESCRIPTION
Creates a self-signed Certificate and associates it with the local RD Gateway Server
.EXAMPLE
New-RDGCertificate -SubjectName
'CN=MyServer.PrimaryLocation.Provider.com'
Creates a new self-signed certificate which can be used to to authenticate incoming RD Gateway traffic
to the DNS name MyServer.PrimaryLocation.Provider.com
.EXAMPLE
[string[]] $SAN = "MyServer.SecondaryLocation.Provider.com"
$SAN += "MyServer.TertiaryLocation.Provider.com"
$SAN += "MyServer.local"
New-RDGCertificate -SubjectName
'CN=MyServer.PrimaryLocation.Provider.com' -SAN $SAN
Creates a new self-signed certificate which can be used to to authenticate incoming RD Gateway traffic
to the following DNS names:
1) MyServer.PrimaryLocation.Provider.com
2) MyServer.SecondaryLocation.Provider.com
3) MyServer.TertiaryLocation.Provider.com
4) MyServer.local
#>
function New-RDGCertificate
{
[CmdletBinding()]
Param
(
# Name of the certificate to create
[Parameter(Mandatory=$true)]
[string]$SubjectName,
# Subject-alternative name(s) to add to the certificate
[Parameter(Mandatory=$false)]
[ValidateNotNullOrEmpty()]
[string[]]$SAN
)
Write-Verbose "Creating Certificate: $SubjectName"
$Name = New-Object -Com 'X509Enrollment.CX500DistinguishedName.1'
$Name.Encode($SubjectName, 0)
$Key = New-Object -Com 'X509Enrollment.CX509PrivateKey.1'
$Key.ProviderName = 'Microsoft RSA SChannel Cryptographic Provider'
$Key.KeySpec = 1
$Key.Length = 1024
$Key.SecurityDescriptor = 'D:PAI(A;;0xd01f01ff;;;SY)(A;;0xd01f01ff;;;BA)(A;;0x80120089;;;NS)'
$Key.MachineContext = 1
$Key.Create()
$ServerAuthoID = New-Object -Com 'X509Enrollment.CObjectId.1'
$ServerAuthoID.InitializeFromValue('1.3.6.1.5.5.7.3.1')
$ekuoids = New-Object -Com 'X509Enrollment.CObjectIds.1'
$ekuoids.add($ServerAuthoID)
$ekuext = New-Object -Com 'X509Enrollment.CX509ExtensionEnhancedKeyUsage.1'
$ekuext.InitializeEncode($ekuoids)
$Cert = New-Object -Com 'X509Enrollment.CX509CertificateRequestCertificate.1'
$Cert.InitializeFromPrivateKey(2, $Key, '')
$Cert.Subject = $Name
$Cert.Issuer = $Cert.Subject
$Cert.NotBefore = (Get-Date).AddHours(-2)
$Cert.NotAfter = $Cert.NotBefore.AddDays(2998)
#SAN
if ($SAN) {
$IAlternativeNames = New-Object -ComObject X509Enrollment.CAlternativeNames
foreach ($AN in $SAN) {
# Instantiate a IAlternativeName object
$AltName = New-Object -ComObject X509Enrollment.CAlternativeName
# Initialize the object by using current element
$AltName.InitializeFromString(0x3,$AN)
# Add created object to an object of collection of IAlternativeNames
$IAlternativeNames.Add($AltName)
}
$SubjectAlternativeName = New-Object -ComObject X509Enrollment.CX509ExtensionAlternativeNames
$SubjectAlternativeName.InitializeEncode($IAlternativeNames)
$Cert.X509Extensions.Add($SubjectAlternativeName)
}
$Cert.X509Extensions.Add($ekuext)
$Cert.Encode()
$Enrollment = New-Object -Com 'X509Enrollment.CX509Enrollment.1'
$Enrollment.InitializeFromRequest($Cert)
$CertData = $Enrollment.CreateRequest(0)
$Enrollment.InstallResponse(2, $CertData, 0, '')
}
<#
.Synopsis
Searches certificate store for a given Subject Name, returning the Certificate
.DESCRIPTION
Searches through the LocalMachine or CurrentUser store location for a certificate whose Subject Name matches the given value
.EXAMPLE
Find-RDSCertificate -SubjectName "CN=MyDNS.com"
This will search the LocalMachine\My store for a certificate whose Subject Name matches MyDNS.com.
It returns the certificate or $Null, based on whether or not a matching certificate is found.
.EXAMPLE
Find-RDSCertificate -SubjectName "CN=MyOtherDNS.com" -Store "TrustedPublisher" -Location "CurrentUser"
This will search the CurrentUser\TrustedPublisher store for a certificate whose Subject Name matches MyDNSOther.com.
It returns the certificate or $Null, based on whether or not a matching certificate is found.
#>
function Find-RDSCertificate
{
[CmdletBinding()]
Param
(
# Name of the certificate to search for
[Parameter(Mandatory=$true)]
$SubjectName,
# Certificate store to search in
[Parameter(Mandatory=$false)]
[ValidateSet("AddressBook", "AuthRoot", "CertificateAuthority", "Disallowed", "My", "Root", "TrustedPeople", "TrustedPublisher")]
$Store = "My",
# Location to search in
[Parameter(Mandatory=$false)]
[ValidateSet("LocalMachine", "CurrentUser")]
$Location = "LocalMachine"
)
$CertStore = New-Object System.Security.Cryptography.X509Certificates.X509Store($Store,$Location)
$CertStore.Open('ReadOnly')
$CertStore.Certificates | where {$_.Subject -Eq $SubjectName}
}
Import-Module RemoteDesktopServices
$RDGCertificateSubject = 'CN=MyServer.PrimaryLocation.Provider.com'
[string[]] $SAN = 'MyServer.SecondaryLocation.Provider.com'
$SAN += 'MyServer.TertiaryLocation.Provider.com'
$SAN += 'MyServer.local'
New-RDGCertificate -SubjectName $RDGCertificateSubject -SAN $SAN
$Cert = Find-RDSCertificate -SubjectName $RDGCertificateSubject
#Apply cert
Set-Item -Path "RDS:\GatewayServer\SSLCertificate\Thumbprint" -Value $Cert.Thumbprint