2

We had an issue with the Secure Store Service on SharePoint 2016 when we tried to create a new instance in partition mode for a multi-tenancy farm.

The main issue, it was impossible to generate a master key from powershell by using Update-SPSecureStoreMasterKey command - no error, and some times

Didn't perform that operation...

or via the central administration

"Failed to generate/refresh key due to error: bla bla blaaa.

For your information, the account used to perform those operations is Farm Admin and follow Microsoft guidelines in terms of permissions, then the farm topology contains 2 WFE, 2 Apps & 2 SQL Servers.

After a lot of investigation, for an unknown reason, the method

"ChangeKey" in the "Microsoft.Office.SecureStoreService.Server.SecureStoreServiceApplicationProxy" doesn't work as expected.

So, to fix that first part you have to execute

"ChangeMasterSecretKey" from the instance of "Microsoft.Office.SecureStoreService.Server.SecureStoreServiceApplication".

Well, you think it's fine you can can see from the central admin that the key is defined, nooooope a new error will occur when you tries to add a target application:

Unable to get the master key.

It's a joke, absolutely not... Fine, if you try now to "refresh the master key" by the central admin with the same passphrase, yataaa, the secure store works as expected.

By now, how to replicate those actiona to automate that for the next deployment ? Have a look on the class definition "Microsoft.Office.SharePoint.ClientExtensions.SecureStoreAdministration.ChangeDBKey" at the method "**OkButton_Click**", you'll see activities performed to generate or refresh a master key are not the same performed by PowerShell commands (**Update-SPSecureStoreMasterKey**/Update-**SPSecureStoreApplicationServerKey**).

Finally, the powershell code bellow could save your day. Enjoy.

$sa = Get-SPServiceApplication <Your_SercureStoreApplication_Identity>;
$proxy = Get-SPServiceApplicationProxy <Your_SercureStoreApplicationProxy_Identity>;
$sp_secure_store_passpharse_new = "<Your_PassPhrase>";
try{
    ##BUG with Update-SPSecureStoreMasterKey => Didn't work properly

    #HotFix: Reverse engineering on classes:
    #-> Microsoft.Office.SecureStoreService.Server.SecureStoreServiceApplication
    #-> Microsoft.Office.SecureStoreService.Server.SecureStoreServiceApplicationProxy
    #-> Microsoft.Office.SecureStoreService.Server.KeyManagement.KeyManager
    #-> Microsoft.Office.SecureStoreService.Server.CryptoHelper

    $ass = $sa.GetType().Assembly
    $CryptoHelperType = $ass.GetType("Microsoft.Office.SecureStoreService.Server.CryptoHelper")
    $GetPassPhraseHashMethod=$CryptoHelperType.GetMethod("GetPassPhraseHash");

    $proxyType = $proxy.GetType();
    $IsMasterSecretKeyPopulated = $proxyType.GetMethod("IsMasterSecretKeyPopulated",[Reflection.BindingFlags]"NonPublic,Instance")
    $SetChangeKeyPassphrase = $proxyType.GetMethod("SetChangeKeyPassphrase",[Reflection.BindingFlags]"NonPublic,Instance")
    $SetKey = $proxyType.GetMethod("SetKey",[Reflection.BindingFlags]"NonPublic,Instance", $null, [type[]]@([string]), $null)

    if(-not $IsMasterSecretKeyPopulated.invoke($proxy,$null)){

        #ChangeKey with the proxy is buggy
        #$proxy.ChangeKey( $proxy.GetChangeKeyToken(), $sp_secure_store_passpharse_new );
        #Fallback by using the service application directly
        $token = $sa.GetChangeMasterSecretKeyToken();
        $sa.ChangeMasterSecretKey($token, $GetPassPhraseHashMethod.Invoke($null, $sp_secure_store_passpharse_new) );

        $c=0;

        while(-not $IsMasterSecretKeyPopulated.invoke($proxy,$null)){
            $c++;
            if($c -ge 20){
                Write-Error $("The master key cannot be populated!");
            }
            sleep 1;
        }
    }

    $SetChangeKeyPassphrase.invoke($proxy, @($sp_secure_store_passpharse_new));
    $SetKey.invoke($proxy,$sp_secure_store_passpharse_new);


}catch{
    Write-Error $("Cannot update the secure store master key.`n{0}" -f $_.Exception.Message);
}
1
  • 1
    we don't receive enough of these knowledgebase type questions. Thank you! Can you put the solution in the form of an answer below, then mark it as the solution? Thank you @Vladimir
    – Mike
    Commented Oct 26, 2017 at 17:11

3 Answers 3

2

Anwser:

$sa = Get-SPServiceApplication <Your_SercureStoreApplication_Identity>;
$proxy = Get-SPServiceApplicationProxy <Your_SercureStoreApplicationProxy_Identity>;
$sp_secure_store_passpharse_new = "<Your_PassPhrase>";
try{
    ##BUG with Update-SPSecureStoreMasterKey => Didn't work properly

    #HotFix: Reverse engineering on classes:
    #-> Microsoft.Office.SecureStoreService.Server.SecureStoreServiceApplication
    #-> Microsoft.Office.SecureStoreService.Server.SecureStoreServiceApplicationProxy
    #-> Microsoft.Office.SecureStoreService.Server.KeyManagement.KeyManager
    #-> Microsoft.Office.SecureStoreService.Server.CryptoHelper

    $ass = $sa.GetType().Assembly
    $CryptoHelperType = $ass.GetType("Microsoft.Office.SecureStoreService.Server.CryptoHelper")
    $GetPassPhraseHashMethod=$CryptoHelperType.GetMethod("GetPassPhraseHash");

    $proxyType = $proxy.GetType();
    $IsMasterSecretKeyPopulated = $proxyType.GetMethod("IsMasterSecretKeyPopulated",[Reflection.BindingFlags]"NonPublic,Instance")
    $SetChangeKeyPassphrase = $proxyType.GetMethod("SetChangeKeyPassphrase",[Reflection.BindingFlags]"NonPublic,Instance")
    $SetKey = $proxyType.GetMethod("SetKey",[Reflection.BindingFlags]"NonPublic,Instance", $null, [type[]]@([string]), $null)

    if(-not $IsMasterSecretKeyPopulated.invoke($proxy,$null)){

        #ChangeKey with the proxy is buggy
        #$proxy.ChangeKey( $proxy.GetChangeKeyToken(), $sp_secure_store_passpharse_new );
        #Fallback by using the service application directly
        $token = $sa.GetChangeMasterSecretKeyToken();
        $sa.ChangeMasterSecretKey($token, $GetPassPhraseHashMethod.Invoke($null, $sp_secure_store_passpharse_new) );

        $c=0;

        while(-not $IsMasterSecretKeyPopulated.invoke($proxy,$null)){
            $c++;
            if($c -ge 20){
                Write-Error $("The master key cannot be populated!");
            }
            sleep 1;
        }
    }

    $SetChangeKeyPassphrase.invoke($proxy, @($sp_secure_store_passpharse_new));
    $SetKey.invoke($proxy,$sp_secure_store_passpharse_new);


}catch{
    Write-Error $("Cannot update the secure store master key.`n{0}" -f $_.Exception.Message);
}
2
  • Thanks a lot. The problem seems to apply to the preview version of SP 2019 as well. The solution / workaround works however either.
    – pholpar
    Commented May 23, 2019 at 13:47
  • Thanks for your feedback. I’m not alone in that situation :) Commented May 24, 2019 at 15:58
2

In SharePoint 2016 I had to run Get-SPServiceApplication and Get-SPServiceApplicationProxy to get the GUID for the service in order to run this.

It might be obvious to some, but I didn't understand that at first.

Therefore the commands look like this:

$sa = Get-SPServiceApplication 'd64a59ab-5f04-40ac-9058-462770ba0000';

where the GUID matches your service guid

0

Just wanted to add in what I saw from a brand new farm install, I created the secure store service with no issues, but when initially setting the passphrase, it constantly failed, after digging through ULS I saw crypto exceptions, "This implementation is not part of the Windows Platform FIPS validated cryptographic algorithms." Then looked at RSOP data from the machines and noticed under local policy->Security Options; System Cryptography had FIPS enabled. Once disabled I was able to set the passphrase, def have to disable on all servers in the farm. Certainly need to talk to AD Admins to confirm corp policy is ok with this being disabled for sharepoint farm

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.