Pregunta

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);
}
¿Fue útil?

Solución

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);
}

Otros consejos

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

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

Licenciado bajo: CC-BY-SA con atribución
scroll top