windows - tildeling af tilladelse til certifikatets private nøgle via Powershell (Win 2012 R2)

Indlæg af Hanne Mølgaard Plasc

Problem



På en Windows Server 2012 R2-maskine, der er knyttet til et domæne, kører jeg følgende udsagn:


$target\_machine\_fqdn = [System.Net.Dns]::GetHostByName($env:computerName)

$certificate\_request = Get-Certificate `
    -Template 'AcmeComputer' `
    -DnsName $target\_machine\_fqdn `
    -CertStoreLocation 'Cert:LocalMachineMy'


Jeg anmoder om et certifikat til værten fra domænenavnets CA. Erklæringen returnerer uden fejl. Et certifikat genereres for maskinen og placeres i det 's' Cert: \ LocalMachine \ My 'som ønsket.


Problem: Jeg kan ikke regne ud, hvordan jeg giver en servicekonto rettigheder til certifikatets private nøgle.


Nu er der ca. 1.000 artikler, der instruerer folk om at give tilladelse ved at hente UniqueKeyContainerName med en kode, der starter som følgende:


$certificate.PrivateKey.CspKeyContainerInfo.UniqueKeyContainerName


Det fungerede ikke her. Mens certifikatet har en privat nøgle, er det private nøgledatamedlem nullt:


Indtast billedbeskrivelse her



I de tilfælde, hvor den løsning, jeg netop har fjernet, virker, er den private nøgle på filsystemet. Men i dette tilfælde er den private nøgle i registreringsdatabasen på følgende måde: [19]


HKEY\_LOCAL\_MACHINESOFTWAREMicrosoftSystemCertificatesMYKeys


Når jeg bruger certifikatet MMC-snapin, kan jeg se certifikatet. Jeg kan administrere tilladelser på den private nøgle. Så jeg ved, at det er der. Desværre skal jeg automatisere tilladelsesopgaven, så du kan bruge certifikaterne MMC-snapin ikke en mulighed. Jeg har brug for at gøre dette gennem Powershell nogle hvordan.

Bedste reference


Jeg gik for nylig gennem automatisering af adgang til certifikat privat nøgle selv. Jeg har også fundet en række steder, der fortæller mig at ændre ACL'erne af nøgledataene på harddisken, men det var ikke tilfredsstillende, da jeg kontrollerede tilladelserne til den private nøgle ved hjælp af PowerShell, den bruger jeg tilføjede, var ikke angivet. Så meget websøgning, et par artikler, og en retfærdig smule forsøg og fejl førte mig til dette.


Jeg begynder med at definere brugerobjektet og den adgang, jeg vil give dem:


# Create NTAccount object to represent the account
$AccountName = 'DomainUserName'
$User = New-Object System.Security.Principal.NTAccount($AccountName)
# Define AccessRule to be added to the private key, could use 'GenericRead' if all you need is read access
$AccessRule = New-Object System.Security.AccessControl.CryptoKeyAccessRule($User, 'FullControl', 'Allow')


Derefter åbner jeg det lokale maskine certifikat butik som Læs/Skriv, og find det certifikat, jeg søger:


# Define the thumbprint of the certificate we are interested in
$Thumb = '63CFDDE9A748345CD77C106DAA09B805B33951BF'
# Open Certificate store as read/write
$store = New-Object System.Security.Cryptography.X509Certificates.X509Store("My","LocalMachine")
$store.Open("ReadWrite")
# Look up the certificate's reference object in the store
$RWcert = $store.Certificates | where {$\_.Thumbprint -eq $Thumb}


Derefter laver jeg et nyt CSP (Crypto Service Provider) -parametersæt, baseret på det eksisterende certifikat, tilføj den nye adgangsregel til parametersættet.


# Create new CSP parameter object based on existing certificate provider and key name
$csp = New-Object System.Security.Cryptography.CspParameters($RWcert.PrivateKey.CspKeyContainerInfo.ProviderType, $RWcert.PrivateKey.CspKeyContainerInfo.ProviderName, $RWcert.PrivateKey.CspKeyContainerInfo.KeyContainerName)

# Set flags and key security based on existing cert
$csp.Flags = "UseExistingKey","UseMachineKeyStore"
$csp.CryptoKeySecurity = $RWcert.PrivateKey.CspKeyContainerInfo.CryptoKeySecurity
$csp.KeyNumber = $RWcert.PrivateKey.CspKeyContainerInfo.KeyNumber

# Add access rule to CSP object
$csp.CryptoKeySecurity.AddAccessRule($AccessRule)


Derefter etablerer vi en ny CSP med de parametre, som vil anvende den nye adgangsregel til det eksisterende certifikat baseret på de flag, vi definerede, og nøgleinfoen vi gav den.


# Create new CryptoServiceProvider object which updates Key with CSP information created/modified above
$rsa2 = New-Object System.Security.Cryptography.RSACryptoServiceProvider($csp)


Så lukker vi bare certifikatbutikken, og vi er alle færdige.


# Close certificate store
$store.Close() 


 Efter at have kigget rundt indså jeg, at jeg havde et par certs, der er de samme. Jeg tror, ​​at dette skyldes, at en ikke-RSA-kryptering bruges til at kryptere den private nøgle. Jeg brugte nogle af info fra dette svar, der forklarer, hvordan man arbejder med en tredjeparts CNG crypto udbyder. Jeg kunne ikke lide at downloade en samling for at gøre tingene i det svar, men jeg brugte lidt af koden for at få vejen til nøglen (ja, der er en nøgle på drevet) og tilføjet en ACL til filen, som fungerede for at delegere rettigheder til den private nøgle. Så her er hvad jeg gjorde ...


Først bekræfter vi, at certifikatet har en CNG-baseret nøgle:


[Security.Cryptography.X509Certificates.X509CertificateExtensionMethods]::HasCngKey($Certificate)


Hvis det vender tilbage True, så går vi igen for at gå videre forbi det. Mine gjorde, jeg gætter også på din vilje. Derefter finder vi nøglen på harddisken ved at læse PrivateKey-dataene (som mangler fra $Certificate) og at få UniqueName til det og derefter søge i Crypto-mappen for den pågældende fil.


$privateKey = [Security.Cryptography.X509Certificates.X509Certificate2ExtensionMethods]::GetCngPrivateKey($Certificate)
$keyContainerName = $privateKey.UniqueName
$keyMaterialFile = gci $env:ALLUSERSPROFILEMicrosoftCrypto*Keys$keyContainerName


Så tog jeg de nuværende ACL'er til filen, lavede en ny AccessRule til at give den ønskede bruger adgang til filen, tilføjede reglen til ACL'erne, jeg lige greb, og brugte den opdaterede ACL tilbage til filen.


$ACL = Get-Acl $keyMaterialFile
$AccountName = 'DomainUser'
$User = New-Object System.Security.Principal.NTAccount($AccountName)
$AccessRule = New-Object System.Security.AccessControl.FileSystemAccessRule($User,'FullControl','None','None','Allow')
$ACL.AddAccessRule($AccessRule)
Set-Acl -Path $keyMaterialFile -AclObject $ACL


Derefter kunne jeg se i certlm.msc og bekræfte, at brugeren havde rettigheder til den private nøgle.

Andre referencer 1


TheMadTechnician svarede på dette spørgsmål som en mf 'n champ. Skulle han nogensinde have brug for mig til at hjælpe ham med at begrave en krop, behøver han kun at ringe. Jeg tilføjer til hans svar med detaljer for folk der ikke kan bygge/indsæt samlingen


BEMÆRK: Clrsecurity-projektet var blevet sendt til CodePlex, som blev lukket i 2017. Projektet blev flyttet til github, hvor det kan downloades. Clrsecurity-forsamlingen, der henvises til i indlæg, understøttes ikke længere. [21]


Også kredit til Vadims Podāns (Crypt32) der skrev artiklen
Hent CNG nøgle container navn og unikt navn, som hjælper læsere adgang til CNG private nøgle ved hjælp af ustyret kode i Powershell. [22]


Hvis du er ligesom mig og ikke kan bruge clrsecurity-samlingen, introducerede .NET 4.5.6-rammen et element, som vi kan udnytte. Overvej følgende:


## Identify the certificate who's private key you want to grant
## permission to
$certificate = $(ls 'cert:LocalMachineMyC51280CE3AD1FEA848308B764DDCFA7F43D4AB1A')

## Identify the user you'll be granting permission to
$grantee\_name = 'foolordAdam'
$grantee = New-Object System.Security.Principal.NTAccount($grantee\_name)

## Get the location and permission-of the cert's private key
$privatekey\_rsa = [System.Security.Cryptography.X509Certificates.RSACertificateExtensions]::GetRSAPrivateKey($certificate)
$privatekey\_file\_name = $privatekey\_rsa.key.UniqueName
$privatekey\_path = "${env:ALLUSERSPROFILE}MicrosoftCryptoKeys${privatekey\_file\_name}"
$privatekey\_file\_permissions = Get-Acl -Path $privatekey\_path

## Grant the user 'read' access to the key
$access\_rule = New-Object System.Security.AccessControl.FileSystemAccessRule($grantee, 'Read', 'None', 'None', 'Allow')
$privatekey\_file\_permissions.AddAccessRule($access\_rule)
Set-Acl -Path $privatekey\_path -AclObject $privatekey\_file\_permissions


Vi bruger metoden GetRSAPrivateKey () i statisk klasse System.Security.Cryptography.X509Certificates.RSACertificateExtensions til at returnere os den private nøgle. Vi henter så UniqueName (Nøgleegenskab har en UniqueName-egenskab). Vi bruger det til at udlede nøglefilens placering . Resten tillader filtilladelser.


Hvis du vil se, hvor private nøgler er gemt, skal du tjekke Key Storage og Retrieval. [23]