Decoding CA Security Descriptors from the Registry
Converting the Security registry key from hex to an understandable format
Converting AD CS “Security” Registry Values into Readable ACLs
When digging into the registry of a Certification Authority (CA), you will eventually find a key that defines who can manage, issue, read, or request certificates:
1
HKLM\SYSTEM\CurrentControlSet\Services\CertSvc\Configuration\<CAName>\Security
That single Security value controls the entire CA’s access permissions, but it’s stored as a blob of hex, meaning that it is not immediately readable or digestable.
This post explains what that value actually is, how to decode it, and how to make sense of it using some PowerShell snippets.
Understanding What’s in the “Security” Value
The Security value is a Windows Security Descriptor stored in self-relative binary form. It’s the same structure Windows uses for file and service permissions, it just lives in the registry instead of on disk.
A security descriptor includes:
- Owner – The account that “owns” the object
- Group – The primary group
- DACL – Discretionary Access Control List: who can do what
- SACL – System Access Control List: who gets audited (if auditing is enabled)
When you view it in the CA MMC under Properties → Security, you’re actually looking at this same structure, just parsed and rendered by Windows.
The Problem
If you export the CA configuration, you’ll see something like this:
1
"Security"=hex:01,00,04,80,ac,00,00,00,14,00,00,00,30,00,00,00,02,00,1c,00,01,00,00,00,...
That’s the security descriptor in binary form. You can’t directly map that to text, because it’s encoded using internal offsets and length fields.
Fortunately, the .NET framework already knows how to read and interpret these descriptors, and PowerShell gives you direct access to those classes.
Step 1: Read the Raw Bytes from the Registry
PowerShell can return binary registry values as byte arrays. For example:
1
2
$path = "HKLM:\SYSTEM\CurrentControlSet\Services\CertSvc\Configuration\My-CA-01"
$bytes = (Get-ItemProperty -Path $path -Name Security).Security
Now you have a raw byte[] containing the CA’s access control information.
Step 2: Parse the Binary into a Security Descriptor
Use .NET’s System.Security.AccessControl.RawSecurityDescriptor class to interpret the binary:
1
2
$sd = New-Object System.Security.AccessControl.RawSecurityDescriptor($bytes, 0)
$sddl = $sd.GetSddlForm("All")
That one-liner converts the hex into SDDL (Security Descriptor Definition Language) — the compact, string-based format Windows uses internally:
1
O:BAG:SYD:(A;;LCRP;;;BA)(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;DA)
Much better than hex, but still hard to read.
Step 3: Decode the DACL (Who Can Do What)
You can take it a step further and enumerate the Access Control Entries (ACEs) inside the DACL. Each ACE contains:
- The security identifier (SID)
- The access mask (bitwise permissions)
- Whether it’s an allow or deny rule
A simple pattern to use:
1
2
3
4
5
6
7
8
9
foreach ($ace in $sd.DiscretionaryAcl) {
$account = try { $ace.SecurityIdentifier.Translate([System.Security.Principal.NTAccount]) } catch { $ace.SecurityIdentifier }
[pscustomobject]@{
Type = $ace.AceType
Rights = ('0x{0:X}' -f $ace.AccessMask)
Account = $account
}
}
This outputs a table showing each account and its access mask.
To make it more meaningful, map those bit values to AD CS-specific rights — for example:
| Bitmask | Right | Description |
|---|---|---|
| 0x00000001 | ManageCA | Full control of CA configuration |
| 0x00000002 | ManageCertificates | Issue or revoke certificates |
| 0x00000004 | Enroll | Request new certificates |
| 0x00000008 | Read | View CA configuration |
Once you translate those masks, you get an easy view of who has what kind of access.
Step 4: Converting Hex from a .REG File
Sometimes you only have a .reg export. You can still decode it, just turn the comma-separated hex values into bytes:
1
2
3
4
5
$hex = "01,00,04,80,ac,00,00,00,14,00,00,00,30,00,00,00"
$bytes = $hex.Split(',') | ForEach-Object { [byte]("0x$_") }
$sd = New-Object System.Security.AccessControl.RawSecurityDescriptor($bytes, 0)
$sd.GetSddlForm("All")
That’s all it takes to decode a CA’s permissions offline.
Step 5: (Optional) Writing It Back
You can also reverse the process, for example, to apply a known-good security descriptor to another CA.
If you need to do it, always:
- Export the current binary value for backup
- Convert your updated SDDL or
CommonSecurityDescriptorback into binary - Write it back with
Set-ItemProperty - Restart the CA service
Example:
1
2
3
4
$bytesOut = New-Object byte[] ($sd.BinaryLength)
$sd.GetBinaryForm($bytesOut, 0)
Set-ItemProperty -Path $path -Name Security -Value $bytesOut
Restart-Service certsvc
Use with caution, and only after verifying the SDDL is valid.