Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 14 additions & 1 deletion public/Set-DbaNetworkCertificate.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -316,7 +316,20 @@ function Set-DbaNetworkCertificate {
if (-not $detailedCertTest.CertificateFound) { $failedChecks += "CertificateNotFound" }
if ($detailedCertTest.CertificateFound -and -not $detailedCertTest.KeyUsagesValid) { $failedChecks += "KeyUsagesInvalid" }
if ($detailedCertTest.CertificateFound -and -not $detailedCertTest.DnsNamesValid) { $failedChecks += "DnsNamesInvalid" }
if ($detailedCertTest.CertificateFound -and -not $detailedCertTest.PrivateKeyValid) { $failedChecks += "PrivateKeyInvalid" }
if ($detailedCertTest.CertificateFound -and -not $detailedCertTest.PrivateKeyValid) {
# SQL Server can only use a private key from a legacy Cryptographic Service Provider with KeySpec
# AT_KEYEXCHANGE; a Key Storage Provider (CNG) key - what New-SelfSignedCertificate creates by
# default - is refused by SQL Server itself. Say so, because "invalid" alone sends people looking
# at permissions.
$privateKeyDescription = "not accessible or a CNG (Key Storage Provider) key"
if ($detailedCertTest.PrivateKeyType) {
$privateKeyDescription = $detailedCertTest.PrivateKeyType
if ($null -ne $detailedCertTest.PrivateKeyNumber) {
$privateKeyDescription += " with KeyNumber $($detailedCertTest.PrivateKeyNumber)"
}
}
$failedChecks += "PrivateKeyInvalid (SQL Server needs a legacy CSP key with KeySpec AT_KEYEXCHANGE; this private key is $privateKeyDescription)"
}
if ($detailedCertTest.CertificateFound -and -not $detailedCertTest.PublicKeyValid) { $failedChecks += "PublicKeyInvalid" }
if ($detailedCertTest.CertificateFound -and -not $detailedCertTest.SignatureAlgorithmValid) { $failedChecks += "SignatureAlgorithmInvalid" }
if ($detailedCertTest.CertificateFound -and -not $detailedCertTest.EnhancedKeyUsageValid) { $failedChecks += "EnhancedKeyUsageInvalid" }
Expand Down
2 changes: 1 addition & 1 deletion public/Test-DbaNetworkCertificate.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ function Test-DbaNetworkCertificate {
- CertificateFound: Boolean indicating if the certificate was found in LocalMachine\My
- KeyUsagesValid: Boolean indicating if the certificate has the required key usages (DigitalSignature and KeyEncipherment)
- DnsNamesValid: Boolean indicating if the certificate's DNS names include the server's network name
- PrivateKeyValid: Boolean indicating if the private key is RSACryptoServiceProvider with KeyNumber Exchange
- PrivateKeyValid: Boolean indicating if the private key is RSACryptoServiceProvider with KeyNumber Exchange (a legacy CSP key with KeySpec AT_KEYEXCHANGE, which SQL Server requires; CNG / Key Storage Provider keys are not supported by SQL Server)
- PublicKeyValid: Boolean indicating if the public key is RSA with at least 2048 bits
- SignatureAlgorithmValid: Boolean indicating if the signature algorithm is SHA-256, SHA-384, or SHA-512
- EnhancedKeyUsageValid: Boolean indicating if the certificate has the Server Authentication enhanced key usage
Expand Down
28 changes: 28 additions & 0 deletions tests/Set-DbaNetworkCertificate.Tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,34 @@ Describe $CommandName -Tag IntegrationTests {
$WarnVar | Should -Match "No suitable certificate found"
}

It "Says why a certificate with a CNG key is unsuitable" {
# New-SelfSignedCertificate creates a Key Storage Provider (CNG) key by default. SQL Server cannot use
# such a key, and the refusal has to name that reason instead of a bare PrivateKeyInvalid.
$newCngCertificate = {
param ($DnsName)
$splatCertificate = @{
DnsName = $DnsName
CertStoreLocation = "Cert:\LocalMachine\My"
FriendlyName = "dbatoolsci_cng_key"
}
(New-SelfSignedCertificate @splatCertificate).Thumbprint
}
$splatCreateCng = @{
ComputerName = $computerName
ScriptBlock = $newCngCertificate
ArgumentList = $computerName
# Raw, because Invoke-Command2 otherwise wraps the string in an object that only has a Length.
Raw = $true
}
$cngThumbprint = Invoke-Command2 @splatCreateCng
$script:createdNetworkCertificateThumbprints += $cngThumbprint

$result = Set-DbaNetworkCertificate -SqlInstance $TestConfig.InstanceRestart -Thumbprint $cngThumbprint -WarningAction SilentlyContinue
$result | Should -BeNullOrEmpty
$WarnVar | Should -Match "PrivateKeyInvalid"
$WarnVar | Should -Match "legacy CSP key with KeySpec AT_KEYEXCHANGE"
}

It "applies an unsuitable certificate when Force is used" {
$splatNewUnsuitableCertificate = @{
ComputerName = $computerName
Expand Down