Creating PEM file through PowerShell - certificate

I am trying to code a script to create a PEM certificate file in powershell. I am not sure if what I did is totally wrong, but when I tried to use the PEM file in and socat OPENSSL it returned the error:
$ socat OPENSSL-LISTEN:1337,cert=cert.pem,verify=0 -
socat[1209] E SSL_CTX_use_certificate_file(): error:0D0680A8:asn1 encoding routines:asn1_check_tlen:wrong tag
#create certificate
$cert = New-SelfSignedCertificate `
-Subject "MYHOSTNAME" `
-TextExtension #("2.5.29.17={text}DNS=MYHOSTNAME&IPAddress=192.168.1.100") `
-KeySpec Signature `
-HashAlgorithm SHA256 `
-KeyExportPolicy Exportable
#publicKey
$PublicKey = $cert.GetPublicKey();
$PublicKeyB64 = [Convert]::ToBase64String($PublicKey,"InsertLineBreaks");
#privateKey
$RSACng = [System.Security.Cryptography.X509Certificates.RSACertificateExtensions]::GetRSAPrivateKey($cert);
$PrivateKey = $RSACng.Key;
$PrivateKeyByte = $PrivateKey.Export("PRIVATEBLOB");
$PrivateKeyB64 = [Convert]::ToBase64String($PrivateKeyByte,"InsertLineBreaks");
#createFile
$out = New-Object string[] -ArgumentList 6;
$out[0] = "-----BEGIN PRIVATE KEY-----";
$out[1] = $PrivateKeyB64;
$out[2] = "-----END PRIVATE KEY-----"
$out[3] = "-----BEGIN CERTIFICATE-----"
$out[4] = $publicKeyB64;
$out[5] = "-----END CERTIFICATE-----"
[IO.File]::WriteAllLines("C:\Users\Public\cert.pem",$out)
I am not sure if what I did is totally wrong, but I could not find any resource to help me proceed.
Some script that perform similar action of creating a PEM file in powershell or some tip about how to proceed could be value to help me fix this one.

You can also use CertUtil.exe. Go through this once
Certutil.exe is a command-line program, installed as part of Certificate Services.
So simple script for creating .PEM with powershell is as follows:
$certpwd = '***'
$certpwd1 = ConvertTo-SecureString -String $certpwd -Force –AsPlainText
$certpwd2 = 'pass:' + $certpwd
$certname = $env:COMPUTERNAME
del c:\temp\$certname.*
$cert = New-SelfSignedCertificate -DnsName $certname -certStorelocation cert:\localmachine\my -KeyLength 2048 -KeyFriendlyName $certname -FriendlyName $friendlyName -HashAlgorithm sha256 -keyexportpolicy exportableencrypted -keyspec KeyExchange -NotAfter (Get-Date).AddYears(2) | Out-Null
$cert2=Get-ChildItem Cert:\LocalMachine\my |
Where-Object { $_.FriendlyName -match $friendlyName }
$path = ‘cert:\localMachine\my\’ + $cert2.thumbprint
Export-Certificate -cert $path -FilePath c:\temp\$certname.der -type CERT –noclobber | Out-Null
certutil -encode c:\temp\$certname.der c:\temp\$certname.pem | Out-Null
What the script does is
1)First it creates self signed certificate
2)Then Export it to some physical path in form of .der
3)Use Certutil for encoding and thus creatig .pem file(.pem will be saved in c:\temp\ in this example)

As commented, the data you are putting in the body of both PEM files is wrong.
For the certificate, it's easy, just use $cert.RawData (converted to base64 with linebreaks, as you already have).
For the privatekey, according to the doc the RSA abstract returned by GetRSAPrivateKey($cert) (not .Key) and thus its implementations like RSACng have methods ExportPkcs8PrivateKey() which should be the correct data to put in a PEM file of type BEGIN/END PRIVATE KEY and also ExportRSAPrivateKey() which should be the older format that is correct for a PEM file of type BEGIN/END RSA PRIVATE KEY -- but only in Core 3.0+ and Five which I don't have and thus can't test.
A workaround if you have openssl commandline is to Export-PfxCertificate to a file, which openssl pkcs12 [-nodes] can then convert to the PEM formats OpenSSL (and thus socat) likes. But if you have openssl commandline you can easily use it to generate the privatekey and (selfsigned/dummy) cert directly, without futzing with powershell.

I had trouble with both previous answers. After much searching and testing, I found a method that works with vanilla powershell and few (no?) permissions.
This script also gives the self signed cert as a pfx, der, and pem file.
$dir = "c:\temp\"
$subj = "CN=YOUR_SUBJECT" #need either DnsName or Subject when calling New-SelfSignedCertificate
$friendlyName = "YOUR_FRIENDLY_NAME"
$pfxFilePath = $dir, $friendlyName, ".pfx" -join ""
$derFilePath = $dir, $friendlyName, ".der" -join ""
$pemFilePath = $dir, $friendlyName, ".pem" -join ""
#create new certificate in cert:\currentuser\my and store in variable $cert
$cert = New-SelfSignedCertificate -CertStoreLocation cert:\currentuser\my -Subject $subj -KeyAlgorithm RSA -KeyLength 2048 -Provider "Microsoft Enhanced RSA and AES Cryptographic Provider" -KeyExportPolicy Exportable -FriendlyName $friendlyName -NotAfter (Get-Date).AddYears(2)
#get password for pfx file
$pw = Read-Host "Enter Password:"
#export cert to pfx and write to file
[io.file]::WriteAllBytes($pfxFilePath,$cert.Export(3, $pw))
#export cert to der and write to file
Export-Certificate -FilePath $derFilePath -Cert $cert
#encode der file as pem file and write to file
certutil -encode $derFilePath $pemFilePath | Out-Null

You are close, this is how it will work:
$cert = ...
# Public key to Base64
$CertBase64 = [System.Convert]::ToBase64String($cert.RawData, [System.Base64FormattingOptions]::InsertLineBreaks)
# Private key to Base64
$RSACng = [System.Security.Cryptography.X509Certificates.RSACertificateExtensions]::GetRSAPrivateKey($cert)
$KeyBytes = $RSACng.Key.Export([System.Security.Cryptography.CngKeyBlobFormat]::Pkcs8PrivateBlob)
$KeyBase64 = [System.Convert]::ToBase64String($KeyBytes, [System.Base64FormattingOptions]::InsertLineBreaks)
# Put it all together
$Pem = #"
-----BEGIN PRIVATE KEY-----
$KeyBase64
-----END PRIVATE KEY-----
-----BEGIN CERTIFICATE-----
$CertBase64
-----END CERTIFICATE-----
"#
# Output to file
$Pem | Out-File -FilePath cert.pem -Encoding Ascii
If you want the equivalent of openssl pkcs12 -in cert.pfx -out cert.pem -nodes (e. g. including the whole certificate chain), here you go:
$cert = ...
# Collect certificates
$CertChain = New-Object -TypeName System.Security.Cryptography.X509Certificates.X509Chain
$CertChain.Build($cert)
$certs = $CertChain.ChainElements | ForEach-Object {$_.Certificate}
[System.Array]::Reverse($certs)
# Private key to Base64
$RSACng = [System.Security.Cryptography.X509Certificates.RSACertificateExtensions]::GetRSAPrivateKey($cert)
$KeyBytes = $RSACng.Key.Export([System.Security.Cryptography.CngKeyBlobFormat]::Pkcs8PrivateBlob)
$KeyBase64 = [System.Convert]::ToBase64String($KeyBytes, [System.Base64FormattingOptions]::InsertLineBreaks)
# Put it all together
$Pem = #"
-----BEGIN PRIVATE KEY-----
$KeyBase64
-----END PRIVATE KEY-----
"#
foreach ($cert in $certs) {
$Pem += #"
-----BEGIN CERTIFICATE-----
$([System.Convert]::ToBase64String($cert.RawData, [System.Base64FormattingOptions]::InsertLineBreaks))
-----END CERTIFICATE-----
"#
}
# Output to file
$Pem | Out-File -FilePath cert.pem -Encoding Ascii

Related

Getting private keys to work on windows 7 powershell version 2

Firstly, I know that very similar questions have been asked, but having read through the solutions and modifying my own workflow has not produced the correct results.
I am hoping that by showing the code I am using, someone may be able to point out where I am going wrong. I am trying to import a priate key from my windows 10 machine to my legacy windows 7 with powershell version 2 installed.
The error I am facing is that $cert.hasprivatekey returns true but $cert.privatekey always returns null. However this happens on the windows 10 machine as well so it seems to be a problem generating a key but every guide I have found uses the same syntax!
Here is the code to create the key:
$store = "cert:\CurrentUser\My"
$params = #{
CertStoreLocation = $store
Subject = "CN=Test1"
KeyLength = 2048
KeyAlgorithm = "RSA"
KeyUsage = "DataEncipherment"
Type = "DocumentEncryptionCert"
KeyExportPolicy = 'Exportable'
}
# generate new certificate and add it to certificate store
$cert = New-SelfSignedCertificate #params
Get-ChildItem -path $store
$pwd = ("P#ssword" | ConvertTo-SecureString -AsPlainText -Force)
$privateKey = "$home\Documents\Test1.pfx"
$publicKey = "$home\Documents\Test1.cer"
# Export private key as PFX certificate, to use those Keys on different machine/user
Export-PfxCertificate -FilePath $privateKey -Cert $cert -Password $pwd
# Export Public key, to share with other users
Export-Certificate -FilePath $publicKey -Cert $cert
I have aso tried this to no avail:
$TestCertificate = New-SelfSignedCertificate -Subject 'TestCertificate' -KeyExportPolicy 'Exportable'
Export-PfxCertificate -Cert $TestCertificate -FilePath .\TestCertificate.pfx -Password (ConvertTo-SecureString 'TestPassword' -AsPlainText -Force)
I am then trying to import the private pfx certificate on windows 7 using the following:
$PfxFilePath = "C:\Users\user\Desktop\Test1.pfx"
$pwd = ("P#ssword" | ConvertTo-SecureString -AsPlainText -Force)
$Password = $pwd
$absolutePfxFilePath = Resolve-Path -Path $PfxFilePath
Write-Output "Importing store certificate '$absolutePfxFilePath'..."
Add-Type -AssemblyName System.Security
$cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2
$cert.Import($absolutePfxFilePath, $Password, [System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]"PersistKeySet")
$Store = New-Object System.Security.Cryptography.X509Certificates.X509Store(
[System.Security.Cryptography.X509Certificates.StoreName]::My,
[System.Security.Cryptography.X509Certificates.StoreLocation]::LocalMachine)
$store.Open([System.Security.Cryptography.X509Certificates.OpenFlags]::"ReadWrite")
$store.Add($cert)
$store.Close()
All ideas welcome!
The key is stored in Key Storage Provider which is not supported by PrivateKey property which is completely obsolete. Instead you need to use newer syntax to obtain private key:
$key = [System.Security.Cryptography.X509Certificates.RSACertificateExtensions]::GetRSAPrivateKey($cert)

How to remove Enhanced Key Usage using New-SelfSignedCertificate

After running the following command, the Extended Key Usage / Enhanced Key Usage is showing both client and server authorization, how do I remove this option for Root CAs and Intermediate CAs, as CAs should not have these options. What other parameters should be added into the New-SelfSignedCertificate to remove the option below?
Client Authentication (1.3.6.1.5.5.7.3.2)
Server Authentication (1.3.6.1.5.5.7.3.1)
Windows 10
Power Shell v5
openssl 1.1.1
$RootCA = New-SelfSignedCertificate -Subject 'CN=KeyCARootCN,O=Test Organisation, OU=Test RootCA,C=AU' -KeyLength 2048 -KeyAlgorithm 'RSA' -HashAlgorithm 'SHA256' -KeyExportPolicy Exportable -KeyUsage KeyEncipherment,DataEncipherment,CertSign,DigitalSignature,CRLSign -Provider 'Microsoft Enhanced RSA and AES Cryptographic Provider' -NotAfter (Get-Date).AddYears(40) -KeyUsageProperty All -TextExtension #(“2.5.29.19 ={critical} {text}ca=1&pathlength=5”) -CertStoreLocation Cert:\LocalMachine\My
$RootCA
$RootCAthumbprint = $RootCA.Thumbprint
$CertRootCAPassword = ConvertTo-SecureString -String “Test123” -Force –AsPlainText
$CertRootCAFilePFX = Export-PfxCertificate -Cert cert:\LocalMachine\My\$RootCAthumbprint -FilePath C:\Users\KeyCARoot.pfx -Password $CertRootCAPassword
$CertRootCAFileCER = Export-Certificate -Cert $RootCA -FilePath C:\Users\KeyCARoot.cer
$CertRootCAFileCER
$CertRootCAPath = 'C:\Users\KeyCARoot.cer'
Give this a try:
Import-Module PKI
$params = #{
Type = [Microsoft.CertificateServices.Commands.CertificateType]::Custom
Subject = 'CN=KeyCARootCN,O=Test Organisation, OU=Test RootCA,C=AU'
KeyLength = 2048
KeyAlgorithm = 'RSA'
HashAlgorithm = [System.Security.Cryptography.HashAlgorithmName]::SHA256
KeyExportPolicy = [Microsoft.CertificateServices.Commands.KeyExportPolicy]::Exportable
KeySpec = [Microsoft.CertificateServices.Commands.KeySpec]::Signature
KeyUsage = #([Microsoft.CertificateServices.Commands.KeyUsage]::CertSign,
[Microsoft.CertificateServices.Commands.KeyUsage]::DigitalSignature,
[Microsoft.CertificateServices.Commands.KeyUsage]::CRLSign)
KeyUsageProperty = [Microsoft.CertificateServices.Commands.KeyUsageProperty]::All
TextExtension = #('2.5.29.19={critical}{text}ca=1&pathlength=5')
NotAfter = (Get-Date).AddYears(40)
Provider = 'Microsoft Enhanced Cryptographic Provider v1.0'
CertStoreLocation = 'Cert:\LocalMachine\My'
}
$RootCA = New-SelfSignedCertificate #params
In general, you may have over-specified some options that aren't necessary. As you can see from above, I simply added a Custom certificate type, removed the KeyEncipherment and DataEncipherment Key Usage options, and swapped-out the CSP provider. Retaining all the signing options for Key Usage should suffice for Root and Intermediate CA certs.
Optionally, you could add ,'2.5.29.37={text}2.5.29.37.0' to your TextExtension list if you wanted your Enhanced Key Usage to be "Any Purpose".

Import certificate to the Group Policy store with PowerShell

I am building ARM-templates to set up test-environments in Azure. I am using DSC to set up the different machines. One thing I want to automate is to import a certificate to the group-policies. You can do it like this manually on the domain-controller (Active-Directory server):
Group Policy Management -> Forest: mydomain.net -> Domains -> mydomain.net -> Group Policy Objects -> Default Domain Policy
Right click -> Edit
Default Domain Policy -> Computer Configuration -> Policies -> Windows Settings -> Security Settings -> Public Key Policies -> Trusted Root Certification Authorities
Right click -> Import
I have laborated with Import-PfxCertificate, CertUtil.exe and .NET C# to accomplish it but haven’t succeeded. What I have tested you can see below, I have put some comments about my thoughts.
Can anyone help me? How should I do this?
First we create a certificate and export it and finally we delete it (we keep the exported one):
$certificateStoreLocation = "CERT:\LocalMachine\My";
$password = ConvertTo-SecureString -String "P#ssword12" -Force -AsPlainText;
$certificate = New-SelfSignedCertificate -CertStoreLocation $certificateStoreLocation -DnsName "Test-Certificate";
$certificateLocation = "$($certificateStoreLocation)\$($certificate.Thumbprint)";
$result = Export-PfxCertificate -Cert $certificateLocation -FilePath "C:\Data\Certificates\Test-Certificate.pfx" -Password $password;
Get-ChildItem $certificateLocation | Remove-Item;
List the certificate stores
foreach($item in Get-ChildItem "CERT:\")
{
Write-Host " - CERT:\$($item.Location)\";
foreach($store in $item.StoreNames.GetEnumerator())
{
Write-Host " - CERT:\$($item.Location)\$($store.Name)";
}
}
PowerShell – Import-PfxCertificate
$certificateStoreLocation = "CERT:\LocalMachine\Root";
$password = ConvertTo-SecureString -String "P#ssword12" -Force -AsPlainText;
Import-PfxCertificate -CertStoreLocation $certificateStoreLocation -FilePath "C:\Data\Certificates\Test-Certificate.pfx" -Password $password;
Get-ChildItem $certificateStoreLocation;
# Now you can find the certificate in the MMC Certificate Snapin:
# [Console Root\Certificates (Local Computer)\Trusted Root Certification Authorities\Certificates]
# Now you can find the certificate in the registry.
# Get-ChildItem "REGISTRY::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\SystemCertificates\Root\Certificates\";
# I want to put the certificate here:
# Get-ChildItem "REGISTRY::HKEY_LOCAL_MACHINE\Software\Policies\Microsoft\SystemCertificates\Root\Certificates\";
PowerShell – CertUtil
CertUtil -p "P#ssword12" -ImportPfx "Root" "C:\Data\Certificates\Test-Certificate.pfx";
Get-ChildItem "CERT:\LocalMachine\Root";
CertUtil -p "P#ssword12" -ImportPfx -GroupPolicy "Root" "C:\Data\Certificates\Test-Certificate.pfx"; # No error but the same result as CertUtil -p "P#ssword12" -ImportPfx "Root" "C:\Data\Certificates\Test-Certificate.pfx".
.NET C#
using(var certificate = new X509Certificate2(#"C:\Data\Certificates\Test-Certificate.pfx", "P#ssword12"))
{
// We only have StoreLocation.CurrentUser and StoreLocation.LocalMachine.
// Can I use System.Management.Automation.Security.NativeMethods+CertStoreFlags.CERT_SYSTEM_STORE_LOCAL_MACHINE_GROUP_POLICY
// somehow to create/open a store by calling new X509Store(IntPtr storeHandle).
using (var store = new X509Store(StoreName.Root, StoreLocation.LocalMachine))
{
store.Open(OpenFlags.ReadWrite);
store.Add(certificate);
}
}
My imaginary solution
Thought this was a possible solution:
Import the certificate to “CERT:\LocalMachine\Root”
Move the registry-key “HKLM:\SOFTWARE\Microsoft\SystemCertificates\Root\Certificates\THUMBPRINT” to “HKLM:\Software\Policies\Microsoft\SystemCertificates\Root\Certificates\THUMBPRINT”
Restart the machine
The registry-keys get correct but the localmachine-root-certificate is still in the certificate-mmc-snapin and no root-certificate is found in the Group Policy Management console.
$certificateRegistryKeyPathPrefix = "HKLM:\SOFTWARE\Microsoft\SystemCertificates\Root\Certificates\";
$certificateStoreLocation = "CERT:\LocalMachine\Root";
$password = ConvertTo-SecureString -String "P#ssword12" -Force -AsPlainText;
$pfxCertificatePath = "C:\Data\Certificates\Test-Certificate.pfx";
$policyCertificateRegistryKeyPathPrefix = "HKLM:\Software\Policies\Microsoft\SystemCertificates\Root\Certificates\";
# Get the thumbprint from the pfx-file so we can check if it's already in the registry.
$certificate = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2;
$certificate.Import($pfxCertificatePath, $password, "DefaultKeySet");
$policyCertificateRegistryKeyPath = "$($policyCertificateRegistryKeyPathPrefix)$($certificate.Thumbprint)";
$policyCertificateRegistryKey = Get-Item -ErrorAction SilentlyContinue -Path $policyCertificateRegistryKeyPath;
if(!$policyCertificateRegistryKey)
{
$certificateRegistryKeyPath = "$($certificateRegistryKeyPathPrefix)$($certificate.Thumbprint)";
$certificateRegistryKey = Get-Item -ErrorAction SilentlyContinue -Path $certificateRegistryKeyPath;
if(!$certificateRegistryKey)
{
$certificate = Import-PfxCertificate -CertStoreLocation $certificateStoreLocation -FilePath $pfxCertificatePath -Password $password;
$certificateRegistryKey = Get-Item -Path $certificateRegistryKeyPath;
}
Move-Item -Destination $policyCertificateRegistryKeyPath -Path $certificateRegistryKeyPath;
# And then we need to reboot the machine.
}
Instead of trying to directly modify the registry under the 'Policies' path, create or modify an 'Registry.pol' file to populate it.
You could use the 'PolicyFileEditor' module from the PowerShell Gallery to do this, but the easiest way is to use the native GroupPolicy module to create and set a Registry.pol file as part of a domain GPO, which will also push out the certificate to member servers.
On a domain controller, import/create a cert so it has a blob value stored in the registry, create a GPO, then run something like the following to configure the GPO (named "DistributeRootCerts" here):
$certsGpoName = 'DistributeRootCerts'
$certThumbprint = '3A8E60952E2CDB7A31713258468A8F0C7FB3C6F6'
$certRegistryKeyPath = 'HKLM:\SOFTWARE\Microsoft\SystemCertificates\MY\Certificates\{0}' -f $certThumbprint
$certBlob = Get-ItemProperty -Path $certRegistryKeyPath -Name 'Blob' | Select -Expand 'Blob'
$certPoliciesRegistryKey = 'HKLM\SOFTWARE\Policies\Microsoft\SystemCertificates\Root\Certificates\{0}' -f $certThumbprint
$null = Set-GPRegistryValue -Name $certsGpoName -Key $certPoliciesRegistryKey -ValueName 'Blob' -Type Binary -Value $certBlob
Then you just need to link the GPO into production with 'New-GPLink'.

Generate Self-signed certificate with Root CA Signer

Scenario: I am using PowerShell on Windows Server 2012r2 to generate a Root certificate and want to use that to sign a newly created Intermediate and Web certificate in dynamic generated (and destroyed) dev/test environments. The scripts are deployed remotely, and the intent is to keep it pure PowerShell if possible. In Windows 10/2016 this is relatively easy, after generating the Root certificate:
$Cert = New-SelfSignedCertificate -Signer $Root -Subject "CN=$Subject"
I've generated the Root certificate using COM X509Enrollment.CX509CertificateRequestCertificate and Security.Cryptography.X509Certificates.X509Certificate2 in a bastardized PS that I've had for some time, mainly because I needed to ensure that the Subject and Usage were set very specifically. I am not quite certain how to use this to sign the standard certificate without the above (which I have used before).
There are some examples using Bouncy Castle (see below) in C# that I could tie into PowerShell, but then I would need to deploy this additionally on the dynamic dev/test environments and I want to be able to do this in Powershell (via COM if needed) with the least dependencies.
C# Generate Self Signed Certificates on the Fly
How do I use Bouncy Castle in Powershell
The ultimate solution in my case, avoiding makecert and openssl was to use Powershell and BouncyCastle. I forked the PSBouncyCastle repo from PSBouncyCastle by RLipscombe and pushed 1.8.1 Bouncy Castle in. My forked version is the one I've used for the script, the fork resides at Forked: PSBouncyCastle.New.
I then used StackOverflow: C# Generate Certificates on the Fly as inspiration to write the following powershell below, I will be adding this to my GitHub and commenting, and I will amend this as soon as I do:
Import-Module -Name PSBouncyCastle.New
function New-SelfSignedCertificate {
[CmdletBinding()]
param (
[string]$SubjectName,
[string]$FriendlyName = "New Certificate",
[object]$Issuer,
[bool]$IsCA = $false,
[int]$KeyStrength = 2048,
[int]$ValidYears = 2,
[hashtable]$EKU = #{}
)
# Needed generators
$random = New-SecureRandom
$certificateGenerator = New-CertificateGenerator
if($Issuer -ne $null -and $Issuer.HasPrivateKey -eq $true)
{
$IssuerName = $Issuer.IssuerName.Name
$IssuerPrivateKey = $Issuer.PrivateKey
}
# Create and set a random certificate serial number
$serial = New-SerialNumber -Random $random
$certificateGenerator.SetSerialNumber($serial)
# The signature algorithm
$certificateGenerator.SetSignatureAlgorithm('SHA256WithRSA')
# Basic Constraints - certificate is allowed to be used as intermediate.
# Powershell requires either a $null or reassignment or it will return this from the function
$certificateGenerator = Add-BasicConstraints -isCertificateAuthority $IsCA -certificateGenerator $certificateGenerator
# Key Usage
if($EKU.Count -gt 0)
{
$certificateGenerator = $certificateGenerator | Add-ExtendedKeyUsage #EKU
}
# Create and set the Issuer and Subject name
$subjectDN = New-X509Name -Name ($SubjectName)
if($Issuer -ne $null) {
$IssuerDN = New-X509Name -Name ($IssuerName)
}
else
{
$IssuerDN = New-X509Name -Name ($SubjectName)
}
$certificateGenerator.SetSubjectDN($subjectDN)
$certificateGenerator.SetIssuerDN($IssuerDN)
# Authority Key and Subject Identifier
if($Issuer -ne $null)
{
$IssuerKeyPair = ConvertTo-BouncyCastleKeyPair -PrivateKey $IssuerPrivateKey
$IssuerSerial = [Org.BouncyCastle.Math.BigInteger]$Issuer.GetSerialNumber()
$authorityKeyIdentifier = New-AuthorityKeyIdentifier -name $Issuer.IssuerName.Name -publicKey $IssuerKeyPair.Public -serialNumber $IssuerSerial
$certificateGenerator = Add-AuthorityKeyIdentifier -certificateGenerator $certificateGenerator -authorityKeyIdentifier $authorityKeyIdentifier
}
# Validity range of the certificate
[DateTime]$notBefore = (Get-Date).AddDays(-1)
if($ValidYears -gt 0) {
[DateTime]$notAfter = $notBefore.AddYears($ValidYears)
}
$certificateGenerator.SetNotBefore($notBefore)
$certificateGenerator.SetNotAfter($notAfter)
# Subject public key ~and private
$subjectKeyPair = New-KeyPair -Strength $keyStrength -Random $random
if($IssuerPrivateKey -ne $null)
{
$IssuerKeyPair = [Org.BouncyCastle.Security.DotNetUtilities]::GetKeyPair($IssuerPrivateKey)
}
else
{
$IssuerKeyPair = $subjectKeyPair
}
$certificateGenerator.SetPublicKey($subjectKeyPair.Public)
# Create the Certificate
$IssuerKeyPair = $subjectKeyPair
$certificate = $certificateGenerator.Generate($IssuerKeyPair.Private, $random)
# At this point you have the certificate and need to convert it and export, I return the private key for signing the next cert
$pfxCertificate = ConvertFrom-BouncyCastleCertificate -certificate $certificate -subjectKeyPair $subjectKeyPair -friendlyName $FriendlyName
return $pfxCertificate
}
A few examples of usage for this powershell would be:
Generate a Root CA
$TestRootCA = New-SelfSignedCertificate -subjectName "CN=TestRootCA" -IsCA $true
Export-Certificate -Certificate $test -OutputFile "TestRootCA.pfx" -X509ContentType Pfx
Generate a Standard Self Signed
$TestSS = New-SelfSignedCertificate -subjectName "CN=TestLocal"
Export-Certificate -Certificate $TestSS -OutputFile "TestLocal.pfx" -X509ContentType Pfx
Generate a certificate, signing with a root certificate
$TestRootCA = New-SelfSignedCertificate -subjectName "CN=TestRootCA" -IsCA $true
$TestSigned = New-SelfSignedCertificate -subjectName "CN=TestSignedByRoot" -issuer $TestRootCA
Export-Certificate -Certificate $test -OutputFile "TestRootCA.pfx" -X509ContentType Pfx
Export-Certificate -Certificate $test -OutputFile "TestRootCA.pfx" -X509ContentType Pfx
Generate a Self-Signed with Specific Usage
$TestServerCert = New-SelfSignedCertificate -subjectName "CN=TestServerCert" -EKU #{ "ServerAuthentication" = $true }
Note that the -EKU parameter accepts via splatting, it does this to ensure that anything added to Add-ExtendedKeyUsage is validly passed. It accepts the following certificate usages:
DigitalSignature
NonRepudiation
KeyEncipherment
DataEncipherment
KeyAgreement
KeyCertSign
CrlSign
EncipherOnly
DecipherOnly
This fits my need and seems to work across all Windows Platforms we are using for dynamic environments.
How about simply doing this:
$cert = New-SelfSignedCertificate -FriendlyName "MyCA"
-KeyExportPolicy ExportableEncrypted
-Provider "Microsoft Strong Cryptographic Provider"
-Subject "SN=TestRootCA" -NotAfter (Get-Date).AddYears($ExpiryInYears)
-CertStoreLocation Cert:\LocalMachine\My -KeyUsageProperty All
-KeyUsage CertSign, CRLSign, DigitalSignature
Important parameters are -KeyUsageProperty and -KeyUsage.
"Itiverba Self-Signed certificate generator" (http://www.itiverba.com/en/software/itisscg.php) is a free GUI tool for Windows that allows you to create your own CA certificates and sign end-certificates with it. You can export the certificates in PEM, CER, DER, PFX file formats.
It's just 3 lines to encode :
Subject: CN="Testcorp - Private CA"
Basic Constraints: V (checked)
Basic Constraints / Subject Type: CA
Give a file name and select a file format, then click on the "create certificate" button. Your Custom CA certificate is done.
The easy way of creating a root certificate would be to do the following. Please note the text extension which makes sure that the certificate is a root certificate. Such a certificate must be placed in a root certificate store to indicate trust. E.g. The 'cert:\LocalMachine\My' store.
Make sure that the KeyUsage is what you want. This can of course be changed, but Microsoft is not that good at documenting why you should do what they suggest.
The moving/copying of the certificate must be done done by exporting the certificate and importing it again. Or create the certificate in the correct place. Note that in general, the certificate will only be created in a My store. Some support commands are described in Certificate Provider PowerShell functions.
The certificate will be exportable by default.
$rootCert = New-SelfSignedCertificate -CertStoreLocation Cert:\CurrentUser\My `
-DnsName "RootCA" `
-TextExtension #("2.5.29.19={text}CA=true") `
-KeyUsage CertSign,CrlSign,DigitalSignature;
The code was lifted from https://learn.microsoft.com/en-us/dotnet/framework/wcf/feature-details/how-to-create-temporary-certificates-for-use-during-development

Export Certificate with private key including all certificates in path using powershell

I am working on power shell script to export certificate with private key which also includes all the certificates in the path. I wrote a script for that, it is not including the certificates in the path or the root certificate. Below is script. Kindly suggest me if there is any changes to make in my script.
Thanks in Advance.
$Password="#de08nt2128"; #password to access certificate after expting
$CertName="WMSvc-WIN-9KC7DG31JBV"; # name of the certificate to export
$RootCertName="WMSvc-WIN-9KC7DG31JBV"; # root certificate
$DestCertName="testcert"
$ExportPathRoot="C:\DestinationFolder"
$CertListToExport=Get-ChildItem -Path cert:\LocalMachine\My | ?{ $_.Subject -Like "*CN=$CertName*" -and $_.Issuer -eq "CN=$RootCertName" }
foreach($CertToExport in $CertListToExport | Sort-Object Subject)
{
$DestCertName=$CertToExport.Subject.ToString().Replace("CN=","");
$CertDestPath=Join-Path -Path $ExportPathRoot -ChildPath "$DestCertName.pfx"
$type = [System.Security.Cryptography.X509Certificates.X509Certificate]::pfx
$SecurePassword = ConvertTo-SecureString -String $Password -Force –AsPlainText
$bytes = $CertToExport.export($type, $SecurePassword)
[System.IO.File]::WriteAllBytes($CertDestPath, $bytes)
}
"Completed"
Updated script to export all certificates matching a particular name and issuer (along with the private key). Make sure you run this with admin privileges:
# Script to export certificate from LocalMachine store along with private key
$Password = "#de08nt2128"; #password to access certificate after exporting
$CertName = "WMSvc-WIN-9KC7DG31JBV"; # name of the certificate to export
$RootCertName = "WMSvc-WIN-9KC7DG31JBV"; # root certificate (the Issuer)
$ExportPathRoot = "C:\DestinationFolder"
$CertListToExport = Get-ChildItem -Path cert:\LocalMachine\My | ?{ $_.Subject -Like "*CN=$CertName*" -and $_.Issuer -Like "CN=$RootCertName*" }
foreach($CertToExport in $CertListToExport | Sort-Object Subject)
{
# Destination Certificate Name should be CN.
# Since subject contains CN, OU and other information,
# extract only upto the next comma (,)
$DestCertName=$CertToExport.Subject.ToString().Replace("CN=","");
$DestCertName = $DestCertName.Substring(0, $DestCertName.IndexOf(","));
$CertDestPath = Join-Path -Path $ExportPathRoot -ChildPath "$DestCertName.pfx"
$SecurePassword = ConvertTo-SecureString -String $Password -Force -AsPlainText
# Export PFX certificate along with private key
Export-PfxCertificate -Cert $CertToExport -FilePath $CertDestPath -Password $SecurePassword -Verbose
}
Updates from your scrip
For the check $_.Issuer -eq "CN=$RootCertName" to work you will have to include OU, O, S information as well so for it to work correctly so I modified it to be $_.Issuer -Like "CN=$RootCertName*" so that it matches all Issuer's who's name starts with variable $RootCertName
Using $CertToExport.Subject.ToString().Replace("CN=","") for generating pfx file name will cause the name to be of the format some-cert-name, OU=sometext, O=org, C=country.pfx so it is better to restrict upt o the next comma (,) so I added $DestCertName.Substring(0, $DestCertName.IndexOf(","))
Finally using Export-PfxCertifcate to export with private key