6 votes

PowerShell Importer Pfx, et Clé Privée "Perdue"

J'ai utilisé la fonction PowerShell suivante pour importer PFX dans le magasin de certificats de mon serveur Windows 2008 R2

function Import-PfxCertificate ([String]$certPath,[String]$certificateStoreLocation = "CurrentUser",[String]$certificateStoreName = "My",$pfxPassword = $null)
{
    $pfx = new-object System.Security.Cryptography.X509Certificates.X509Certificate2    

    $pfx.Import($certPath, $pfxPassword, "Exportable,PersistKeySet")    

    $store = new-object System.Security.Cryptography.X509Certificates.X509Store($certificateStoreName,$certificateStoreLocation)    
    $store.open("MaxAllowed")    
    $store.add($pfx)    
    $store.close()
    return $pfx
}

L'appelant de la fonction ressemble à $importedPfxCert = Import-PfxCertificate $pfxFile "LocalMachine" "My" $password Je l'ai installé dans le My store de la machine locale. J'ai ensuite accordé le droit de lecture à mon pool d'applications IIS.

J'ai un service WCF qui doit l'utiliser.

<behaviors>
  <serviceBehaviors>
    <behavior>
      <serviceCredentials>
        <serviceCertificate findValue="MyCertName" x509FindType="FindBySubjectName" />
        <userNameAuthentication userNamePasswordValidationMode="Custom"
          customUserNamePasswordValidatorType="MyValidator" />
      </serviceCredentials>
    </behavior>
  </serviceBehaviors>
</behaviors>

Quand j'utilise un client pour appeler le service, je reçois une exception de WCF. It is likely that certificate 'CN=MyCertName' may not have a private key that is capable of key exchange or the process may not have access rights for the private key.

Si je le supprime de la MMC, et que j'importe manuellement le même fichier PFX depuis la MMC du certificat, dans le même magasin et que j'accorde la même permission, mon client peut appeler le service sans problème.

Cela m'amène à penser que, pour une raison quelconque, si j'utilise PowerShell, la clé privée est foutue d'une manière ou d'une autre.

Ce qui est amusant, c'est que dans les deux cas, si je vais dans MMC et que je double-clique sur mon certificat installé, je vois que You have a private key that corresponds to the certificate. il semble donc que la clé privée soit chargée même dans PowerShell. les paramètres de permission sont identiques.

Un indice ou une expérience ?

6voto

Sergey Azarkevich Points 618

J'ai le même problème. Le prochain script fonctionne :

function InstallCert ($certPath, [System.Security.Cryptography.X509Certificates.StoreName] $storeName)
{
    [Reflection.Assembly]::Load("System.Security, Version=2.0.0.0, Culture=Neutral, PublicKeyToken=b03f5f7f11d50a3a")

    $flags = [System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::MachineKeySet -bor [System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::PersistKeySet

    $cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2($certPath, "", $flags)

    $store = New-Object System.Security.Cryptography.X509Certificates.X509Store($storeName, [System.Security.Cryptography.X509Certificates.StoreLocation]::LocalMachine)

    $store.Open([System.Security.Cryptography.X509Certificates.OpenFlags]::ReadWrite);

    $store.Add($cert);

    $store.Close();
}

2voto

powerdude Points 21

J'ai mis à jour la réponse de Sergey à ce qui suit. Notez que le using namespace ... Cette syntaxe n'est valable que pour PS 5.0 et les versions ultérieures. Si vous en avez besoin pour une version antérieure, vous devrez ajouter l'espace de nom complet, System.Security.Cryptography.X509Certificates selon les besoins.

using namespace System.Security

[CmdletBinding()]
param (
    [parameter(mandatory=$true)] [string] $CertificateFile,
    [parameter(mandatory=$true)] [securestring] $PrivateKeyPassword,
    [parameter(mandatory=$true)] [string] $AllowedUsername
)

# Setup certificate
$Flags = [Cryptography.X509Certificates.X509KeyStorageFlags]::MachineKeySet `
    -bor [Cryptography.X509Certificates.X509KeyStorageFlags]::PersistKeySet `
    -bor [Cryptography.X509Certificates.X509KeyStorageFlags]::Exportable
$Certificate = New-Object Cryptography.X509Certificates.X509Certificate2($CertificateFile, $PrivateKeyPassword, $Flags)

# Install certificate into machine store
$Store = New-Object Cryptography.X509Certificates.X509Store(
    [Cryptography.X509Certificates.StoreName]::My, 
    [Cryptography.X509Certificates.StoreLocation]::LocalMachine)
$Store.Open([Cryptography.X509Certificates.OpenFlags]::ReadWrite)
$Store.Add($Certificate)
$Store.Close()

# Allow read permission of private key by user
$PKFile = Get-ChildItem "$env:ProgramData\Microsoft\Crypto\RSA\MachineKeys\$($Certificate.PrivateKey.CspKeyContainerInfo.UniqueKeyContainerName)"
$PKAcl = $PKFile.GetAccessControl("Access")
$ReadAccessRule = New-Object AccessControl.FileSystemAccessRule(
    $AllowedUsername,
    [AccessControl.FileSystemRights]::Read,
    [AccessControl.AccessControlType]::Allow
)
$PKAcl.AddAccessRule($ReadAccessRule)
Set-Acl $PKFile.FullName $PKAcl

Sauvegarder ce script sur InstallCertificate.ps1 puis exécutez-le en tant qu'administrateur :

PS C:\Users\me> .\InstallCertificate.ps1

cmdlet InstallCertificate.ps1 at command pipeline position 1
Supply values for the following parameters:
CertificateFile: c:\my\path\mycert.pfx
PrivateKeyPassword: *********************
AllowedUsername: me
PS C:\Users\me> ls Cert:\LocalMachine\My
<Observe that your cert is now listed here.  Get the thumbprint>
PS C:\Users\me> (ls Cert:\LocalMachine\My | ? { $_.Thumbprint -eq $Thumbprint }).PrivateKey

Après le redémarrage, la dernière ligne devrait montrer que la clé privée est toujours installée même en tant que non-administrateur.

Modifié pour ajouter l'étape ACL comme décrit dans https://stackoverflow.com/a/37402173/7864889 .

1voto

Angel Yordanov Points 713

J'ai atterri sur ce fil de discussion SO, car l'intégration dans le système de gestion de l'information de l'entreprise est en cours. Import-PfxCertificate n'importait pas correctement les certificats CAPI. Malheureusement, powerdude Le cmdlet de réponse de l'auteur n'a pas fonctionné pour moi car il a jeté The property 'CspKeyContainerInfo' cannot be found on this object. Verify that the property exists. lors de la définition des autorisations.

Après l'avoir combiné avec ce merveilleux gist de milesgratz ça a marché.

Voici la version finale modifiée pour ressembler à un Import-PfxCertificate substitution.

# Import-CapiPfxCertificate.ps1
# for CNG certificates use built in Import-PfxCertificate

using namespace System.Security

[CmdletBinding()]
param (
    [parameter(mandatory=$true)] [string] $FilePath,
    [parameter(mandatory=$true)] [securestring] $Password,
    [parameter(mandatory=$true)] [string] $CertStoreLocation,
    [parameter(mandatory=$false)] [string] $AllowedUsername
)

if (-not ($CertStoreLocation -match '^Cert:\\([A-Z]+)\\([A-Z]+)$')) {
    Write-Host "Incorrect CertStoreLocation. See usage in the Import-PfxCertificate documentation" -ForegroundColor Red
    exit 1;
}

$StoreName = $Matches.2
$StoreLocation = $Matches.1

# Setup certificate
$Flags = [Cryptography.X509Certificates.X509KeyStorageFlags]::MachineKeySet `
    -bor [Cryptography.X509Certificates.X509KeyStorageFlags]::PersistKeySet `
    -bor [Cryptography.X509Certificates.X509KeyStorageFlags]::Exportable
$Certificate = New-Object Cryptography.X509Certificates.X509Certificate2($FilePath, $Password, $Flags)

# Install certificate into the specified store
$Store = New-Object Cryptography.X509Certificates.X509Store(
    $StoreName, 
    $StoreLocation)
$Store.Open([Cryptography.X509Certificates.OpenFlags]::ReadWrite)
$Store.Add($Certificate)
$Store.Close()

if (-not ([string]::IsNullOrEmpty($AllowedUsername))) {
    # Allow read permission of private key by user
    $PKUniqueName = ([System.Security.Cryptography.X509Certificates.RSACertificateExtensions]::GetRSAPrivateKey($Certificate)).key.UniqueName
    $PKFile = Get-Item "$env:ProgramData\Microsoft\Crypto\RSA\MachineKeys\$PKUniqueName"
    $PKAcl = Get-Acl $PKFile
    $PKAcl.AddAccessRule((New-Object AccessControl.FileSystemAccessRule($AllowedUsername, "Read", "Allow")))
    Set-Acl $PKFile.FullName $PKAcl
}

0voto

mobese46 Points 181

J'ai rencontré un problème similaire sur l'un de nos serveurs de développement lors de l'importation d'un certificat via la MMC. Le problème était que le groupe Administrateurs n'avait pas de droits sur le dossier MachineKeys.

C:\Users\All Utilisateurs \Microsoft\Crypto\RSA\MachineKeys

J'ai ajouté le contrôle total sur le dossier MachineKeys aux administrateurs et il a pu créer avec succès la clé privée lors de l'importation du certificat.

Assurez-vous que l'utilisateur sous lequel vous exécutez Powershell a le droit d'écrire dans le dossier MachineKeys.

0voto

Fares Noueihed Points 356

Le code suivant référencé ci-dessous, par Sergey Azarkevich, est ce qui a fait l'affaire pour moi :

$flags = [System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::MachineKeySet -bor [System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::PersistKeySet

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X