Password Decryption error

I’ve been trying to add a credential set to a DSC process, and run into a roadblock. The DSC server is a Windows 2012R2 system in Pull mode, and configured using WMF5.1 :

Name                           Value
----                           -----
PSVersion                      5.1.14409.1005
PSEdition                      Desktop
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0...}
BuildVersion                   10.0.14409.1005
CLRVersion                     4.0.30319.42000
WSManStackVersion              3.0
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1
configuration DSCPullServer
{ 
    param  
    ( 
        [string[]]$NodeName = 'localhost', 
 
        [ValidateNotNullOrEmpty()] 
        [string] $certificateThumbPrint 
    ) 
 
    Import-DSCResource -ModuleName xPSDesiredStateConfiguration -ModuleVersion 3.10.0.0
    Import-DscResource -ModuleName PSDesiredStateConfiguration
    Import-DscResource -ModuleName xWebAdministration
 
 $ensureState = "present"
 $PullServerName = "FQDN of pull server"
 $RegKey = "--guid for registration--"


    Node $NodeName 
    { 
	    # this all runs on top of IIS
	    WindowsFeature WebServer
	    {
		    Ensure = $ensureState
		    Name = "Web-WebServer"
	    }

        # Next 2 items provide the IIS UI and depemd on having base IIS component installed
        WindowsFeature WebManagement
        {
            Ensure                 = $ensureState
            name                   = "Web-Mgmt-Tools"
            DependsOn              = "[WindowsFeature]WebServer"
        }

        WindowsFeature IISConsole
        {
            Ensure                 = $ensureState
            name                   = "Web-Mgmt-Console"
            DependsOn              = "[WindowsFeature]WebServer"
        }

	    # obviously the DSC Pull server needs the DSC-Service
        WindowsFeature DSCServiceFeature 
        { 
    	    Ensure                = $ensureState 
       	    Name                  = 'DSC-Service'             
            DependsOn             = "[WindowsFeature]WebServer"
        } 

	    #NetHTTPActivation is requires in order to start a stopped website
	    WindowsFeature NETHTTPActivation
	    { 
        	    Ensure            = $ensureState
        	    Name              = 'NET-HTTP-Activation'
                DependsOn         = "[WindowsFeature]WebServer"
	    }
    
	    # enable32bitAppOnWin64 is required otherwise the app pool crashes on access
	    xWebAppPool DefaultAppPool
	    {
            Ensure                = $ensureState
		    Name                  = 'PSWS'
		    State                 = 'Started'
		    enable32BitAppOnWin64 = $True
            DependsOn             = "[WindowsFeature]WebServer"
	    } 
 
        xDscWebService DSCPullSRV
        { 
                Ensure                  = $ensureState
                EndpointName            = 'DSCPullSRV' 
                Port                    = 8080 
                PhysicalPath            = "$env:SystemDrive\inetpub\DSCPullSRV" 
                CertificateThumbPrint   = $certificateThumbPrint          
                ModulePath              = "$env:PROGRAMFILES\WindowsPowerShell\DscService\Modules" 
                ConfigurationPath       = "$env:PROGRAMFILES\WindowsPowerShell\DscService\Configuration"
                RegistrationKeyPath     = "$env:PROGRAMFILES\WindowsPowerShell\DscService"   
                AcceptSelfSignedCertificates = $true    
                State                   = 'Started' 
                DependsOn               = '[WindowsFeature]DSCServiceFeature'
                #UseSecurityBestPractices = $false                <-- no longer a valid member
        }

        xWebsite FileRepo
        {
            Ensure                      = $ensureState
            Name                        = "Filerepo"
            State                       = "Started"
            PhysicalPath                = "$env:SystemDrive\inetpub\wwwroot\filerepo"
            BindingInfo                 = 
                            @( MSFT_xWebBindingInformation
                                 {
                                   Protocol              = "HTTP"
                                   Port                  = 80
                                 }
                             )
            DependsOn       = "[WindowsFeature]WebServer" 
        }
        
        File RegistrationKeyFile
        {
            Ensure          = $ensureState
            Type            = 'File'
            DestinationPath = 'C:\Program Files\WindowsPowerShell\DscService\RegistrationKeys.txt'
            Contents        = $RegKey 
        }
    }
}
 
DSCPullServer -certificateThumbPrint '--fully signed cert--'
Start-DscConfiguration -path .\DSCPullServer -verbose -wait -force

I have a PowerShell script that is triggered from TFS Release Management that creates the MOF files for machines based on release requirements. I wont bore you with all the details of the script, but each node does contain envrypted credentials, and the resulting Configuration references a Node block:

    Node $AllNodes.Nodename
    {
        Write-Host "Adding Local Configuration Manager config to MOF"
        #Configuration of Local Configuration Manager needed to use Encrypted Credentials to support accessing domain resources
        LocalConfigurationManager
        {
            CertificateId = $node.Thumbprint
        }
    }

The resulting MOF files contain encrypted credential sets such as

instance of MSFT_Credential as $MSFT_Credential1ref
{
Password = "-----BEGIN CMS-----\nMIIBsgYJKoZIhvcNAQcDoIIBozCCAZ8CAQAxggFa................P2OR3QmDb69\n-----END CMS-----";
 UserName = "---username---";

};

for each reference of the credential. All seems good on the server side.

On the Client side I have Windows 2008R2 systems, also using WMF5.1

Name                           Value
----                           -----
PSVersion                      5.1.14409.1005
PSEdition                      Desktop
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0...}
BuildVersion                   10.0.14409.1005
CLRVersion                     4.0.30319.18063
WSManStackVersion              3.0
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1

And the client is registered using this script

#Server to configure for
$hostname = $env:COMPUTERNAME + "." + $env:USERDNSDOMAIN

#pull server to configure against
$pullServer = "FQDN of pull server"

#Certificate CN that restricts to the certificate to be used for encryption
$encryCertCN = "CN=$($pullServer)"

#DSC Registration key - without this, the DSC server wont let you in
$regkey = "--reg key that matches pull server--"

# Get the certificate that works for encryption 
function Get-LocalEncryptionCertificateThumbprint 
{ 
    (dir Cert:\LocalMachine\Root) | %{
        # Verify the certificate is for Encryption and valid 
        If ($_.PrivateKey.KeyExchangeAlgorithm -and ($_.Issuer -eq $encryCertCN ) -and ($_.Subject -eq $encryCertCN )  )
        { 
            Write-Output "Thumbprint $($_.Thumbprint)"
            return $_.Thumbprint 
        } 
    } 
}

[DscLocalConfigurationManager()]
Configuration RegistrationMetaConfig
{
    Node localhost
    {
        Settings
        {
            ActionafterReboot="ContinueConfiguration";
            AllowModuleOverwrite  = $true;
            CertificateID ='---same thumbprint as pull server--' # Get-LocalEncryptionCertificateThumbprint
            ConfigurationMode = "ApplyAndAutocorrect";
            ConfigurationModeFrequencyMins = 15;
            RebootNodeIfNeeded = $true;
            RefreshFrequencyMins = 30;
            RefreshMode = "Pull";
        }

        ConfigurationRepositoryWeb ConfigurationManager
        {
            ServerURL = "https://$($pullServer):8080/PSDSCPullServer.svc"
            RegistrationKey = $regkey  
            ConfigurationNames = @($hostname)

        }

        ReportServerWeb DSCPull
        {
            ServerURL = "https://$($pullServer):8080/PSDSCPullServer.svc"
            RegistrationKey = $regkey
        }

    }

}
        $ConfigData= @{ 
            AllNodes = @(     
                        @{  
                            # Common to all nodes
                            NodeName = "*"

                            # The path to the .cer file containing the 
                            # public key of the Encryption Certificate 
                            # used to encrypt credentials for this node 
                            CertificateFile = "C:\DSC\DscPublicKey.cer"


                            # The thumbprint of the Encryption Certificate 
                            # used to decrypt the credentials on target node 
                            Thumbprint ='--same thumbprint as on pull server--'# Get-LocalEncryptionCertificateThumbprint
                        }
                    )
                 }

Write-Host "Disabling DSC Debug"
Disable-DscDebug

Write-Host "Removing current DSC config"
Remove-DscConfigurationDocument -Stage Current, Pending, Previous -Verbose

Write-Host "Creating new DSC Config" 
RegistrationMetaConfig -ConfigurationData $ConfigData -OutputPath C:\DSC -Verbose

Write-Host "Configuring LCM to use new config"
Set-DscLocalConfigurationManager -ComputerName localhost -Path C:\DSC -Verbose -Force

Write-Host "Start DSC Configuration"
Start-DSCConfiguration -ComputerName localhost.meta -Path C:\DSC -Verbose 

And the client registers fine. It pulls a configuration, and attempts to apply it… however - the client fails to execute the configuration

Status     StartDate                 Type            Mode  RebootRequested      NumberOfResources             
------     ---------                 ----            ----  ---------------      -----------------             
Failure    2/15/2017 9:30:03 AM      Consistency     Pull  False                5                             

and when I look into the event viewer I find this error in the DSC event log

Job {3CF0DEDD-F38B-11E6-8A11-0050569272C7} : 
MIResult: 1
Error Message: Decryption failed.
Message ID: Windows System Error 13
Error Category: 6
Error Code: 13
Error Type: WIN32

Now, if the certificate which was used for encryption was exported from the pull server, as a pfx, and imported into the client, (and I know it can find the cert, as I’ve seen a different error when the cert can not be found). What is causing the decryption error - and how do I go about fixing it?

Am I seeing it correctly in your client setup script that the PFX gets imported into Cert:\LocalMachine\Root which is the Trusted Root Certification Authorities folder in the MMC? If yes, try to move the certificate to Cert:\LocalMachine\My which would be the Personal folder where most server applications look for encryption/decryption certificates.

Actually, the Get-LocalEncryptionCertificateThumbprint function is no longer being called and the thumbprint is being provided hard coded. The Certificate does live in Cert:\LocalMachine\My right now. I had run into a problem where that function was returning a String[] for some reason (how, I dont know) and PSDesiredStateConfiguration.psm1 was throwing errors - so I just hard coded the thumbprint I needed.

Using the method described on https://msdn.microsoft.com/en-us/powershell/dsc/securemof I see an error “The private key could not be acquired”

The only way I’ve been able to get to the point I am is using an internal CA signed cert using the process defined on https://kamranicus.com/posts/2016-04-04-wmf5-powershell-dsc-generating-encryption-certificate

More info that might point to the error:

There is also a message showing in Trace-xDscOperation that has me wondering about the Cert itself… it says:

The enveloped-data message does not contain the specified recipient

When I look this up online, there are a number of (non-DSC) posts which reference the crypto api not being able to reference the private key along with a seemingly equal number of “it cant find the cert” messages.

$certificates = dir Cert:\LocalMachine\My | Where-Object { $_.Thumbprint -eq 'C1A7A70FEFB5EF42255BB12E4C8F83C6E71362A9' }
$certificates | FL -Property Thumbprint, PrivateKey

            $certificates | %{
                    # Verify the certificate is for Encryption and valid
                    if ( $_.PrivateKey.KeyExchangeAlgorithm )
                    {
                        if ($_.Verify ) { write-output "Cert is valid" }
                    }
                  }


Thumbprint : C1A7A70FEFB5EF42255BB12E4C8F83C6E71362A9
PrivateKey : System.Security.Cryptography.RSACryptoServiceProvider

Cert is valid

This would confirm that the desired thumbprint does indeed live in \LocalMachine\My

Is the Crypto Provider causing the problem?