Using Azure Automation to generate a certificate

Some time back I had cause to demonstrate the possibility of using Azure Automation in generating time limited certificates for use with Azure.  It turned out to be more difficult than I thought as certificate creation on a local server or desktop uses the COM based CryptoAPI... which isnt available for use with Automation Runbooks.

This example script used the brilliant "Bouncy Castle" library for creating certificates.

The particular concept never progressed - but having some working code to reference (below) will be handy for anyone else also working with this idea. :)

<#

Module Name:

    AzureCreateCert.psm1

Description:

    Example 

Version History

    0.1    - 7 October 2017    Laurie Rhodes       Demonstration Code

#>
 
 <#
  .Synopsis
    Creates a Self Signed Certificate
   .Description
    Creates a Self Signed Certificate
   .Example
    New-RequestCertificate -RequestNumber 2435
   .Inputs
    [string]
   .OutPuts
    [string]
   .Notes
    NAME:  New-RequestCertificate
    AUTHOR: Laurie Rhodes
    LASTEDIT: 24/06/2015
    KEYWORDS:
   .Link
     Http://www.laurierhodes.info
 #Requires -Version 2.0
 #>
Function New-RequestCertificate
{
[CmdletBinding()]
param(
      [string]$RequestNumber
) #end param

   
 
$datestart = [System.DateTime]::Parse("7/10/2017 11:00:00 PM",[System.Globalization.CultureInfo]::CurrentCulture, [System.Globalization.DateTimeStyles]::AssumeLocal)
$dateend   = [System.DateTime]::Parse("7/10/2017 11:01:00 PM",[System.Globalization.CultureInfo]::CurrentCulture, [System.Globalization.DateTimeStyles]::AssumeLocal)

$ScriptDir = Split-Path $script:MyInvocation.MyCommand.Path

Add-Type -Path "$($ScriptDir)\BouncyCastle.Crypto.dll" 

############## Generate Random Password for Cert

$Private:OFS = ""
$PasswordLength = 10
$InclChars = ‘abcdefghkmnprstuvwxyzABCDEFGHKLMNPRSTUVWXYZ123456789’
$RandomNums = 1..$PasswordLength | ForEach-Object { Get-Random -Maximum $InclChars.length }
$RandomPassowrd = [String]$InclChars[$RandomNums]
###############

$random       = [Org.BouncyCastle.Crypto.Prng.CryptoApiRandomGenerator]::new()
$securerandom = [Org.BouncyCastle.Security.SecureRandom]::new($random)
$keygenparam  = [Org.BouncyCastle.Crypto.KeyGenerationParameters]::new($securerandom,2048)
$keypairgen  = new-object Org.BouncyCastle.Crypto.Generators.RsaKeyPairGenerator


if ($random -ne $null) { write-debug  " random created"} else {throw  "random creation failed"; exit}
if ($securerandom -ne $null) { write-debug  "securerandom created"} else {throw  "securerandom creation failed"; exit}
if ($keygenparam -ne $null) { write-debug "keygenparam created"} else {throw  "keygenparam creation failed"; exit}

$keypairgen  = new-object Org.BouncyCastle.Crypto.Generators.RsaKeyPairGenerator
$keypairgen.Init($keygenparam)
$certkeypair = $keypairgen.GenerateKeyPair()

$CertGenerator = [Org.BouncyCastle.X509.X509V3CertificateGenerator]::new()

$customrange = [Org.BouncyCastle.Utilities.BigIntegers]::CreateRandomInRange([Org.BouncyCastle.Math.BigInteger]::One , [Org.BouncyCastle.Math.BigInteger]::ValueOf([int64]::MaxValue), $securerandom )

$cn     = new-object Org.BouncyCastle.Asn1.X509.X509Name("CN=TestCert" )
$issuer = new-object Org.BouncyCastle.Asn1.X509.X509Name("CN=TestFirm" )

$CertGenerator.SetSerialNumber($customrange)
$CertGenerator.SetIssuerDN($issuer)
$CertGenerator.SetNotBefore($datestart)
$CertGenerator.SetNotAfter($dateend)
$CertGenerator.SetSubjectDN($cn)
$CertGenerator.SetPublicKey($certkeypair.Public)
$CertGenerator.SetSignatureAlgorithm("SHA256withRSA")


$basconst = new-object Org.BouncyCastle.Asn1.X509.BasicConstraints($false)
$CertGenerator.AddExtension([Org.BouncyCastle.Asn1.X509.X509Extensions]::BasicConstraints, $true, $basconst )

$authident = new-object Org.BouncyCastle.Asn1.X509.AuthorityKeyIdentifier( [Org.BouncyCastle.X509.SubjectPublicKeyInfoFactory]::CreateSubjectPublicKeyInfo($certkeypair.Public) )
$CertGenerator.AddExtension([Org.BouncyCastle.Asn1.X509.X509Extensions]::AuthorityKeyIdentifier, $true, $authident )

[Org.BouncyCastle.Crypto.AsymmetricCipherKeyPair]$issuerKeyPair = $certkeypair

[Org.BouncyCastle.Crypto.ISignatureFactory]$signatureFactory = [Org.BouncyCastle.Crypto.Operators.Asn1SignatureFactory]::new("SHA256withRSA", [Org.BouncyCastle.Crypto.AsymmetricKeyParameter]$issuerKeyPair.Private)

$cert = $CertGenerator.Generate($signatureFactory)



#########################
# RSA Private Key Import
# Cryptographic Service Provider properties

$cspParams = [System.Security.Cryptography.CspParameters]::new()
$cspParams.KeyContainerName = [guid]::NewGuid()
$cspParams.KeyNumber = 1
$cspParams.Flags = [System.Security.Cryptography.CspProviderFlags]::NoPrompt


$rsaProvider      = [System.Security.Cryptography.RSACryptoServiceProvider]::new($cspParams)
$dotNetPrivateKey = [Org.BouncyCastle.Crypto.Parameters.RsaPrivateCrtKeyParameters]$issuerKeyPair.Private
$rsaParameters    = new-object System.Security.Cryptography.RSAParameters

$rsaParameters.Modulus  = ($issuerKeyPair.Private).Modulus.ToByteArrayUnsigned()
$rsaParameters.Exponent = ($issuerKeyPair.Private).PublicExponent.ToByteArrayUnsigned()
$rsaParameters.P        = ($issuerKeyPair.Private).P.ToByteArrayUnsigned()
$rsaParameters.Q        = ($issuerKeyPair.Private).Q.ToByteArrayUnsigned()
$rsaParameters.D        = ($issuerKeyPair.Private).Exponent.ToByteArrayUnsigned()
$rsaParameters.DP       = ($issuerKeyPair.Private).DP.ToByteArrayUnsigned()
$rsaParameters.DQ       = ($issuerKeyPair.Private).DQ.ToByteArrayUnsigned()
$rsaParameters.InverseQ = ($issuerKeyPair.Private).QInv.ToByteArrayUnsigned()

$rsaProvider.ImportParameters($rsaParameters)


##################################
# Export X509 Cert

$x509cert = [System.Security.Cryptography.X509Certificates.X509Certificate2]::new()
$x509cert.Import($cert.GetEncoded())
$x509cert.FriendlyName = "test certificate"
$x509cert.PrivateKey = [System.Security.Cryptography.AsymmetricAlgorithm]$rsaProvider

#create a pfx password 
$Secure_String_Pwd = ConvertTo-SecureString $RandomPassowrd -AsPlainText -Force

#Export the certificate
$bytes = $x509cert.Export([System.Security.Cryptography.X509Certificates.X509ContentType]::Pfx,$Secure_String_Pwd)

#Write the byte array to file
[system.convert]::ToBase64String($bytes)  | Out-File "$($env:TEMP)\tempcert.pfx" -Force

write-output [system.convert]::ToBase64String($bytes)
}