Search code examples
windowsaclcng

Set ACL on private key in HSM


On Windows Server A, we can generate a key in an HSM, and make it available to AD CS. The key is used to generate a CSR, which is signed by a public CA, and imported into the cert store on the same host. Can use certutil -repairstore to link the signed cert to the private key, and use the key for signing artifacts.

The problem is that the private key resource has an ACL on it, and if we move to a Server N, and try to use the signing key in the HSM, there is a permissions error event due to the ACL on the key not recognizing the environment from which it is being accessed.

It seems that the powershell tools Get-Acl and Set-Acl (called on Server A) would work to change the ACL, but I lack knowledge of what params are required, what should go into the parameters, and how that information should be formatted. That said, the doc for Get-Acl says

Because Get-Acl is supported by the file system and registry providers, you can use Get-Acl to view the ACL of file system objects, such as files and directories, and registry objects, such as registry keys and entries.

(text in italics highlighted by me)

So, is Get-Acl correct? or is there something else that will work?

If anyone has done this, can we get some guidance?

There is a key in the HSM:

------------------------------------------------------------

Provider          : Utimaco CryptoServer Key Storage Provider
Device            : [email protected]
Group             : utimaco
Mode              : Internal Key Storage

------------------------------------------------------------

Index  AlgId        Size   Group            Name                             Spec
---------------------------------------------------------------------------------
1      RSA          2048   utimaco          testacl                          0

Get-Acl -inputobject might give me access to the object, but how to format that in a way that InputObject is happy with?

Thank you for any hints. Would happily accept "won't work" if you include a "do this instead". Will unhappily accept won't work if that's all you say :)


Solution

  • Get-Acl/Set-Acl will not work with CNG keys in general. You can neither use -Path since there is no PowerShell provider for cryptographic keys in contrast to certificates , nor can you use -InputObject since there is no special PowerShell object for cryptographic keys and System.Security.Cryptography.CngKey does not have a GetSecurityDescriptor method, which is called by Get-Acl internally.

    IMO you have to work with .NET classes to get this done:

    # open provider and key, get security descriptor
    $cngProvider = New-Object System.Security.Cryptography.CngProvider("Utimaco CryptoServer Key Storage Provider")
    $cngKey = [System.Security.Cryptography.CngKey]::Open("testacl", $cngProvider)
    $cngProp = $cngKey.GetProperty("Security Descr", [System.Security.Cryptography.CngPropertyOptions]::None)
    $rawSD = New-Object System.Security.AccessControl.RawSecurityDescriptor($cngProp.GetValue(), 0)
    
    # example: add full access for "NetworkService" at the end of the ACL
    $sid = New-Object System.Security.Principal.SecurityIdentifier([System.Security.Principal.WellKnownSidType]::NetworkService, $null)
    $accessRead = 0x80020001
    $accessFull = 0xd01f0003
    $newACE = New-Object System.Security.AccessControl.CommonAce([System.Security.AccessControl.AceFlags]::None, [System.Security.AccessControl.AceQualifier]::AccessAllowed, $accessFull, $sid, $false, $null)
    $rawSD.DiscretionaryAcl.InsertAce($rawSD.DiscretionaryAcl.Count, $newACE)
          
    # finally save the modified security descriptor
    $rawSDbin = New-Object 'byte[]' $rawSD.BinaryLength
    $rawSD.GetBinaryForm($rawSDbin, 0)
    # 4 is DACL_SECURITY_INFORMATION, which is not available in the CngPropertyOptions enumeration
    $daclPropertyOptions = [System.Security.Cryptography.CngPropertyOptions]4
    $cngProp = New-Object System.Security.Cryptography.CngProperty("Security Descr", $rawSDbin, $daclPropertyOptions)
    $cngKey.SetProperty($cngProp)
    

    You can also remove an entry from the ACL using $rawSD.DiscretionaryAcl.RemoveAce(position)