public/Get-PulpCertificate.ps1

# .ExternalHelp powershell-pulp-help.xml
Function Get-PulpCertificate{
  [Cmdletbinding()]
  Param(
    [Parameter(Mandatory=$false)]
    [string]$Server = (Get-PulpLocalConfig -Server).Server,

    [Parameter(Mandatory=$false)]
    [int]$Port = (Get-PulpLocalConfig -Port).Port,

    [Parameter(Mandatory=$false)]
    [string]$Protocol = (Get-PulpLocalConfig -Protocol).Protocol,

    [Parameter(Mandatory=$false)]
    [string]$Username = (Get-PulpLocalConfig -Username).Username,
    
    [Parameter(Mandatory=$false)]
    [switch]$Force
  )
  $acctErr = "Error logging into ${Protocol}://${Server}:${Port}. "+
             "Please check your username and password."
  $sslErr = "If your client computer does not trust the SSL certificate " +
            "presented by server ${server}, please trust this certificate or " +
            "try running: Set-PulpLocalConfig -SkipCertificateCheck"
  
  $uri = "${Protocol}://${Server}:${Port}/pulp/api/v2/actions/login/"
  $certFriendlyName = "Pulp on $server"
  if ($Env:OS -eq "Windows_NT") {
    $certStorePath = "Cert:\CurrentUser\My"
  } else {
    $certStorePath = "~\.pulp\certs"
  }

  # Windows: get relevant certs from the cert store
  if ($Env:OS -eq "Windows_NT") {
    $storedCertificates = (Get-ChildItem $certStorePath |
                           Where-Object {$_.FriendlyName -eq $certFriendlyName} |
                           Where-Object {$_.NotAfter -gt (Get-Date)} |
                           Sort-Object NotAfter -Descending)
  }
  # Linux: Get cert from standard file
  else {
    $storedCertificates = @()
    If ((Test-Path "${certStorePath}\user-cert.pem")) {
      $pemArray = Get-Content "${certStorePath}\user-cert.pem"
      $certString = ''
      $keyString = ''
      $certStart = $false
      $keyStart = $false
      $certEnd = $false
      $keyEnd = $false
      foreach ($line in $pemArray) {
        if ($line -match '-----BEGIN RSA PRIVATE KEY-----') {
          $keyStart = $true
        }
        if ($line -match '-----BEGIN CERTIFICATE-----') {
          $certStart = $true
        }
        if ($keyStart -and !$keyEnd) { $keyString += "${line}`n" }
        if ($certStart -and !$certEnd) { $certString += "${line}`n" }
        if ($line -match '-----END RSA PRIVATE KEY-----') {
          $keyEnd = $true
        }
        if ($line -match '-----END CERTIFICATE-----') {
          $certEnd = $true
        }
      }
      $certBytes = [System.Convert]::FromBase64String(
        $certString.Substring(
          28, $certString.Length - 55))
      $keyBytes = [System.Convert]::FromBase64String(
        $keyString.Substring(32, $keyString.Length-63))
      if (-not ([System.Management.Automation.PSTypeName]'PowershellPulp.Crypto2').Type) {
        Add-Type -TypeDefinition (Get-Content -Raw $PSScriptRoot\..\lib\powershell-pulp-main.cs)
      }
      $decodedKey = [PowershellPulp.Crypto2]::DecodeRSAPrivateKey($keyBytes)
      $certWithoutKey = New-Object `
      System.Security.Cryptography.X509Certificates.X509Certificate2(
        $certBytes,'')
      $certificate =
        [System.Security.Cryptography.X509Certificates.RSACertificateExtensions]::CopyWithPrivateKey($certWithoutKey, $decodedKey)
      if ($certificate.NotAfter -gt (Get-Date)) {
        $storedCertificates += $certificate
      }
    }
  }
  if (($storedCertificates.Count -gt 0) -and (!$Force)) {
    return $storedCertificates[0]
  } else {
    $config = Get-PulpLocalConfig
    $attempt = 0
    while ((!$authResponse) -and ($attempt -lt 3)) {
      if (!$Password){
        $credential = Get-Credential -user $Username -Message `
          "Enter Pulp username and password to obtain certificate"
        If ($credential) {
          $Password = $credential.GetNetworkCredential().password
          $Username = $credential.UserName
        }
      }
      $base64Cred = [System.Convert]::ToBase64String(
        [Text.Encoding]::ASCII.GetBytes(("{0}:{1}" -f $Username,$Password)))
      try {
        # PowerShell 5
        if ($PSVersionTable.PSVersion.Major -lt 6) {
          if ((Get-PulpLocalConfig -SkipCertificateCheck).SkipCertificateCheck) {
            if (-not ([System.Management.Automation.PSTypeName]'PowershellPulp.SkipCertificateCheck').Type) {
              Add-Type -Path $PSScriptRoot\..\lib\powershell-pulp-skip.cs
              [System.Net.ServicePointManager]::CertificatePolicy =
                New-Object PowershellPulp.SkipCertificateCheck
            }
          }
          $authResponse = Invoke-RestMethod -Uri $uri -Method Post `
                          -Headers @{Authorization=("Basic {0}" -f $base64Cred)} `
                          -ErrorAction Stop
        }
        # PowerShell 6
        else {
          if ((Get-PulpLocalConfig -SkipCertificateCheck).SkipCertificateCheck) {
            $authResponse = Invoke-RestMethod -Uri $uri -Method Post `
                        -Headers @{Authorization=("Basic {0}" -f $base64Cred)} `
                         -SkipCertificateCheck -ErrorAction Continue
            } else {
            $authResponse = Invoke-RestMethod -Uri $uri -Method Post `
                        -Headers @{Authorization=("Basic {0}" -f $base64Cred)} `
                        -ErrorAction Continue
            }
          }
        }
        catch {
          $attempt++
          $Password = $null
      }
    }
    if (!$authResponse) {
     if ($Protocol -eq 'https') { Write-Error ($acctErr + " " + $sslErr) }
     else { Write-Error $acctErr }
     throw
    }
    # Windows: Convert and store returned cert and key in cert store
    if ($Env:OS -eq "Windows_NT") {
      $certBytes = [System.Convert]::FromBase64String(
        $authResponse.certificate.Substring(
          28, $authResponse.certificate.Length - 55))
      $keyBytes = [System.Convert]::FromBase64String(
        $authResponse.key.Substring(32, $authResponse.key.Length-63))
      # PowerShell 5
      if ($PSVersionTable.PSVersion.Major -lt 6) {
        if (-not ([System.Management.Automation.PSTypeName]'PowershellPulp.Crypto').Type) {
          Add-Type -Path $PSScriptRoot\..\lib\powershell-pulp-main.cs
        }
        $decodedKey = [PowershellPulp.Crypto]::DecodeRsaPrivateKey($keyBytes)
        $certificate = New-Object `
        System.Security.Cryptography.X509Certificates.X509Certificate2(
          $certBytes,'')
        $certificate.FriendlyName = $certFriendlyName
        $certificate.PrivateKey = $decodedKey
      }
      # PowerShell 6
      else {
        if (-not ([System.Management.Automation.PSTypeName]'PowershellPulp.Crypto2').Type) {
          Add-Type -TypeDefinition (Get-Content -Raw $PSScriptRoot\..\lib\powershell-pulp-main.cs)
        }
        $decodedKey = [PowershellPulp.Crypto2]::DecodeRSAPrivateKey($keyBytes)
        $certWithoutKey = New-Object `
        System.Security.Cryptography.X509Certificates.X509Certificate2(
          $certBytes,'')
        $certWithoutKey.FriendlyName = $certFriendlyName
        $certificate =
          [System.Security.Cryptography.X509Certificates.RSACertificateExtensions]::CopyWithPrivateKey($certWithoutKey, $decodedKey)
      }
      $certStore = Get-Item $certStorePath
      $certStore.Open('ReadWrite')
      $certStore.Add($certificate)
    }
    # Linux: Write returned cert and key directly to file
    else {
      if (!(Test-Path $certStorePath)) {
        $null = New-Item -Type Directory -Path $certStorePath -Force
      }
      $authResponse.key + $authResponse.certificate |
        Out-File -Encoding ASCII -NoNewLine "${certStorePath}\user-cert.pem"
    # Recursion!
    }
    return (Get-PulpCertificate -Server $Server -Port $Port `
                                -Protocol $Protocol -Username $Username)
  }
}