Functions/New-SignedCertificateRequest.ps1

<#
    .SYNOPSIS
    Appends a Signature to a PKCS#10 Certificate Request
 
    .PARAMETER CertificateRequest
    Takes a BASE64 encoded PKCS#10 Certificate Request
 
    .PARAMETER SigningCert
    Takes a Signing Certificate in Form of an X509Certificate2 Object
 
    .PARAMETER RequesterName
    Optional Parameter to include a Requester Name in Form of DOMAIN\Username here
    This effectively makes it an Enroll on Behalf of (EOBO) Request
 
    .OUTPUTS
    BASE64-encoded PKCS#7 Certificate Request containing the signed PKCS#10 Certificate request
#>

function New-SignedCertificateRequest {

    [CmdletBinding()]
    param (

        [Parameter(
            Mandatory=$True,
            ValuefromPipeline=$True
            )]
        [ValidateNotNullOrEmpty()]
        [String]
        $CertificateRequest,

        [Parameter(Mandatory=$True)]
        [ValidateScript({($_.HasPrivateKey) -and ($null -ne $_.PSParentPath)})]
        [System.Security.Cryptography.X509Certificates.X509Certificate2]
        $SigningCert,

        [Parameter(Mandatory=$False)]
        [ValidatePattern("^.*(\\|/)")]
        [String]
        $RequesterName

    )

    begin {
        # Signing a Certificate Request means wrapping the PKCS10 request inside a PKCS7 request
        # https://docs.microsoft.com/en-us/windows/desktop/seccertenroll/certificate-request-functions
        $CertRequestPkcs10 = New-Object -ComObject 'X509Enrollment.CX509CertificateRequestPkcs10'
        $CertRequestPkcs7 = New-Object -ComObject 'X509Enrollment.CX509CertificateRequestPkcs7'
    }

    process {

        # First we must load the given Certificate Request
        # https://docs.microsoft.com/en-us/windows/win32/api/certenroll/nf-certenroll-ix509certificaterequestpkcs10-initializedecode
        Try {
            $CertRequestPkcs10.InitializeDecode(
                $CertificateRequest,
                $EncodingType.XCN_CRYPT_STRING_BASE64_ANY
                )
        }
        Catch {
            Write-Error -Message $PSItem.Exception.Message
            return
        }
    
        # Build the PKCS#7 Message based on the PKCS#10 Certificate Request
        # https://stackoverflow.com/questions/7824408/programmatically-communicating-with-a-certificate-authority
        $CertRequestPkcs7.InitializeFromInnerRequest($CertRequestPkcs10);

        # Create a Signer Certificate Structure
        # https://msdn.microsoft.com/en-us/library/windows/desktop/aa376832(v=vs.85).aspx
        $SignerCertificate =  New-Object -ComObject 'X509Enrollment.CSignerCertificate'
        $SignerCertificate.Initialize(
            [int]($SigningCert.PSParentPath -match "Machine"),
            $X509PrivateKeyVerify.VerifyNone, # We did this already during Parameter Validation
            $EncodingType.XCN_CRYPT_STRING_BASE64,
            [Convert]::ToBase64String($SigningCert.RawData)
        )
        $CertRequestPkcs7.SignerCertificate = $SignerCertificate

        # Append the Requester Name in Form of DOMAIN\Username here
        If ($RequesterName) {
            $CertRequestPkcs7.RequesterName = $RequesterName
        }
        
        # The request is encoded by using Distinguished Encoding Rules (DER) as defined by the Abstract Syntax Notation One (ASN.1) standard.
        # The encoding process creates a byte array. You can retrieve the byte array by calling the RawData property.
        # https://docs.microsoft.com/en-us/windows/win32/api/certenroll/nf-certenroll-ix509certificaterequest-encode
        $CertRequestPkcs7.Encode()

        # Return the signed Certificate Signing Request
        # https://docs.microsoft.com/en-us/windows/win32/api/certenroll/nf-certenroll-ix509certificaterequest-get_rawdata
        "-----BEGIN PKCS #7 SIGNED DATA-----`n" + 
        $CertRequestPkcs7.RawData($RequestFlags.CR_OUT_BASE64) + 
        "-----END PKCS #7 SIGNED DATA-----"

    }

    end {
        [void]([System.Runtime.Interopservices.Marshal]::ReleaseComObject($CertRequestPkcs10))
        [void]([System.Runtime.Interopservices.Marshal]::ReleaseComObject($CertRequestPkcs7))
    }

}