0

I'm using the certreq command line utility to formulate a CSR amendment as follows (I simplified the syntax for clarity)

$CertReqPath = "C:\Windows\System32\certreq.exe"
$arg1="-config"
$arg1a= $IssuingCA
$arg2="-policy"
$arg2a= $CSR
$arg3=$Inf
$arg4=$NewSANCSR

& $CertReqPath $arg1 $arg1a $arg2 $arg2a $arg3 $arg4

The first field requres a text string (the Issuing Authortiy) - easy enough.

The remaining three fields are supposed to take text based file and will not work when substituted with a variable. The first two arguments ($arg2a and $arg3) are inputs (as text files) The last argument ($arg4) is an output. It will always opens a gui prompt for a save location unless I make it a file location (e.g. "c:\temp\outfile.csr")

Is there a way I can trick certutil into believing it's receiving from/sending to a text file as appropriate so that I can keep the information in an object for further manipulation?

Obviously I can write the input and outputs to a file in system temp directory and read the data back in, but it would be preferable to not have to do that.

Many thanks

Scepticalist
  • 3,737
  • 1
  • 13
  • 30
  • 1
    I attempted a similar thing a few years back. Worked on off and on for months, and never found a solution. – WayneA Aug 10 '18 at 19:53

1 Answers1

1

As I mention in my comment, I never found a way to do this, but I did manage to hobble together a function that did not call command line tools.

Note: This may not be safe to run in production.

It is taken largely from: https://blogs.msdn.microsoft.com/alejacma/2008/09/05/how-to-create-a-certificate-request-with-certenroll-and-net-c/

Function New-CertificateFromCA{
   [CmdletBinding()]
   Param (

      [String[]]$CommonName = @([System.Net.Dns]::GetHostEntry($env:computerName).hostname),

      [string]$OrganizationalUnit = "CustomOUname",

      [string]$Organization = "YourOrganizationHere",

      [string]$City = "YourCityHere",

      [string]$State = "YourStateHere",

      [string]$Country = "YourCountryHere",

      [int]$KeyLength = 4096,

      [string]$ProviderName = "Microsoft Enhanced Cryptographic Provider v1.0",

      [string]$TemplateName = "YourTemplateHere",

      [string]$CertificateAuthority = $null

   )

   begin {
      # contexts
      New-Variable -Name UserContext -Value 0x1 -Option Constant
      New-Variable -Name MachineContext -Value 0x2 -Option Constant
      # installation options
      New-Variable -Name AllowNone -Value 0x0 -Option Constant
      New-Variable -Name AllowNoOutstandingRequest -Value 0x1 -Option Constant
      New-Variable -Name AllowUntrustedCertificate -Value 0x2 -Option Constant
      New-Variable -Name AllowUntrustedRoot -Value 0x4 -Option Constant
      # encoding
      New-Variable -Name Base64Header -Value 0x0 -Option Constant
      New-Variable -Name Base64 -Value 0x1 -Option Constant
   }

   Process {
      [string]$Subject = "CN=$($CommonName), OU=$($OU), O=$($Organization), L=$($City), S=$($State), C=$($Country)"

      $objCSP = New-Object -ComObject "X509Enrollment.CCspInformation"
      $objCSP.InitializeFromName($ProviderName)

      $objCSPs = New-Object -ComObject "X509Enrollment.CCspInformations"
      $objCSPs.Add($objCSP)

      $objPrivateKey = new-Object -ComObject "X509Enrollment.CX509PrivateKey"
      $objPrivateKey.Length = $KeyLength
      $objPrivateKey.KeySpec = 2 #X509KeySpec.XCN_AT_SIGNATURE
      $objPrivateKey.KeyUsage = 16777215 #0xffffff #X509PrivateKeyUsageFlags.XCN_NCRYPT_ALLOW_ALL_USAGES
      $objPrivateKey.MachineContext = $True
      $objPrivateKey.CspInformations = $objCSPs
      $objPrivateKey.ExportPolicy = 1 #Exportable
      $objPrivateKey.Create()

      $objPkcs10 = New-Object -ComObject X509Enrollment.CX509CertificateRequestPkcs10
      $objPkcs10.InitializeFromPrivateKey(2, $objPrivateKey, "$TemplateName")


      $objAlternativeName = New-Object -ComObject "X509Enrollment.CAlternativeName"
      $objAlternativeName.InitializeFromString(3, $CommonName)

      $objAlternativeNames = New-Object -ComObject "X509Enrollment.CAlternativeNames"
      $objAlternativeNames.Add($objAlternativeName)

      $objExtensionAlternativeNames = New-Object -ComObject "X509Enrollment.CX509ExtensionAlternativeNames"
      $objExtensionAlternativeNames.InitializeEncode($objAlternativeNames)
      $objPkcs10.X509Extensions.Add($objExtensionAlternativeNames)

      $objDN = New-Object -ComObject "X509Enrollment.CX500DistinguishedName"
      $objDN.Encode($Subject, 0)
      $objPkcs10.Subject = $objDN

      # Enroll locally
      $objEnroll = new-object -com "X509Enrollment.CX509Enrollment"
      $objEnroll.InitializeFromRequest($objPkcs10)
      $DERString = $objEnroll.CreateRequest(0x1)

      # Identify and Submit to CA
      $CAQuery = certutil -templateCAs $TemplateName
      If ($CAQuery -match "completed successfully") { $strCAConfig = $CAQuery[0] }

      $objCertRequest = New-Object -ComObject "CertificateAuthority.Request"
      $intDisposition = $objCertRequest.Submit(1, $DERString, $null, $strCAConfig)

      If ($intDisposition -eq 3) {
         Write-Verbose "Request Submitted successfully"
         $strCert = $objCertRequest.GetCertificate(257)
         $objEnroll = new-object -com "X509Enrollment.CX509Enrollment"
         $objEnroll.Initialize($MachineContext)
         $objEnroll.InstallResponse($AllowUntrustedRoot, $strCert, $Base64, $null)
         $Flags = [System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::Exportable
         $Flags = $Flags -bor [System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::MachineKeySet
         $Flags = $Flags -bor [System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::PersistKeySet

         $Cert = New-Object Security.Cryptography.X509Certificates.X509Certificate2
         $Cert.Import([System.Convert]::FromBase64String($objEnroll.Certificate(1)),$null,$Flags)
         $result = gci -Recurse cert:\*$($Cert.Thumbprint)
         Write-Verbose "Certificate with thumbprint $($Cert.Thumbprint) installed"
         $result
      }
   }
}
WayneA
  • 339
  • 1
  • 7
  • Ok, at least I know it;s not a simple thing - I''d suspected as much. There's the PSPKI Powershell module now for simple requests, but here I'm adding SANs using an Enrollment Agent certificate. I'll look into this way of doing it if I have time, else just keep using the system temp directory, thanks – Scepticalist Aug 12 '18 at 08:01