# Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT license. See LICENSE file in the project root for full license information. <############################################################################## Example ############################################################################### # Create a new root certificate on "Cert:\LocalMachine\My" and export it to "Cert:\LocalMachine\Root" # FYI, you can do the same thing with one of the following commands: # %sdxroot\tools\amd64\MakeCert.exe -r -pe -n "CN=ANCMTest_Root" -b 12/22/2013 -e 12/23/2020 -ss root -sr localmachine -len 2048 -a sha256 # $thumbPrint = (New-SelfSignedCertificate -DnsName "ANCMTest_Root", "ANCMTest_Roo3" -CertStoreLocation "cert:\LocalMachine\My").Thumbprint ############################################################################### $rootSubject = "ANCMTest_Root" $thumbPrint = .\certificate.ps1 -Command Create-SelfSignedCertificate -Subject $rootSubject .\certificate.ps1 -Command Export-CertificateTo -TargetThumbPrint $thumbPrint -TargetSSLStore "Cert:\LocalMachine\My" -ExportToSSLStore "Cert:\LocalMachine\Root" .\certificate.ps1 -Command Get-CertificateThumbPrint -Subject $rootSubject -TargetSSLStore "Cert:\LocalMachine\Root" ############################################################################### # Create a new certificate setting issuer with the root certicate's subject name on "Cert:\LocalMachine\My" and export it to "Cert:\LocalMachine\Root" # FYI, you can do the same thing with one of the following commands: # %sdxroot\tools\amd64\MakeCert.exe -pe -n "CN=ANCMTestWebServer" -b 12/22/2013 -e 12/23/2020 -eku 1.3.6.1.5.5.7.3.1 -is root -ir localmachine -in $rootSubject -len 2048 -ss my -sr localmachine -a sha256 # %sdxroot\tools\amd64\MakeCert.exe -pe -n "CN=ANCMTest_Client" -eku 1.3.6.1.5.5.7.3.2 -is root -ir localmachine -in ANCMTest_Root -ss my -sr currentuser -len 2048 -a sha256 ############################################################################### $childSubject = "ANCMTest_Client" $thumbPrint2 = .\certificate.ps1 -Command Create-SelfSignedCertificate -Subject $childSubject -IssuerName $rootSubject ("Result: $thumbPrint2") .\certificate.ps1 -Command Export-CertificateTo -TargetThumbPrint $thumbPrint2 -TargetSSLStore "Cert:\LocalMachine\My" -ExportToSSLStore "Cert:\CurrentUser\My" .\certificate.ps1 -Command Export-CertificateTo -TargetThumbPrint F97AB75DCF1C62547E4B5E7025D60001892A6A60 -ExportToSSLStore C:\gitroot\AspNetCoreModule\tools\test.pfx -PfxPassword test # Clean up .\certificate.ps1 -Command Delete-Certificate -TargetThumbPrint $thumbPrint2 -TargetSSLStore "Cert:\LocalMachine\My" .\certificate.ps1 -Command Delete-Certificate -TargetThumbPrint $thumbPrint2 -TargetSSLStore "Cert:\CurrentUser\Root" .\certificate.ps1 -Command Delete-Certificate -TargetThumbPrint $thumbPrint -TargetSSLStore "Cert:\LocalMachine\My" .\certificate.ps1 -Command Delete-Certificate -TargetThumbPrint $thumbPrint -TargetSSLStore "Cert:\LocalMachine\Root" ###############################################################################> Param( [parameter(Mandatory=$true , Position=0)] [ValidateSet("Create-SelfSignedCertificate", "Delete-Certificate", "Export-CertificateTo", "Get-CertificateThumbPrint", "Get-CertificatePublicKey")] [string] $Command, [parameter()] [string] $Subject, [parameter()] [string] $IssuerName, [Parameter()] [string] $FriendlyName = "", [Parameter()] [string[]] $AlternativeNames = "", [Parameter()] [string] $TargetSSLStore = "", [Parameter()] [string] $ExportToSSLStore = "", [Parameter()] [string] $PfxPassword = "", [Parameter()] [string] $TargetThumbPrint = "" ) function Create-SelfSignedCertificate($_subject, $_friendlyName, $_alternativeNames, $_issuerName) { if (-not $_subject) { return ("Error!!! _subject is required") } # # $_issuerName should be set with the value subject and its certificate path will be root path if (-not $_issuerName) { $_issuerName = $_subject } # # Create $subjectDn and $issuerDn $subjectDn = new-object -com "X509Enrollment.CX500DistinguishedName" $subjectDn.Encode( "CN=" + $_subject, $subjectDn.X500NameFlags.X500NameFlags.XCN_CERT_NAME_STR_NONE) $issuerDn = new-object -com "X509Enrollment.CX500DistinguishedName" $issuerDn.Encode("CN=" + $_issuerName, $subjectDn.X500NameFlags.X500NameFlags.XCN_CERT_NAME_STR_NONE) # # Create a new Private Key $key = new-object -com "X509Enrollment.CX509PrivateKey" $key.ProviderName = "Microsoft Enhanced RSA and AES Cryptographic Provider" # XCN_AT_SIGNATURE, The key can be used for signing $key.KeySpec = 2 $key.Length = 2048 # MachineContext 0: Current User, 1: Local Machine $key.MachineContext = 1 $key.Create() # # Create a cert object with the newly created private key $cert = new-object -com "X509Enrollment.CX509CertificateRequestCertificate" $cert.InitializeFromPrivateKey(2, $key, "") $cert.Subject = $subjectDn $cert.Issuer = $issuerDn $cert.NotBefore = (get-date).AddMinutes(-10) $cert.NotAfter = $cert.NotBefore.AddYears(2) #Use Sha256 $hashAlgorithm = New-Object -ComObject X509Enrollment.CObjectId $hashAlgorithm.InitializeFromAlgorithmName(1,0,0,"SHA256") $cert.HashAlgorithm = $hashAlgorithm # # Key usage should be set for non-root certificate if ($_issuerName -ne $_subject) { # # Extended key usage $clientAuthOid = New-Object -ComObject "X509Enrollment.CObjectId" $clientAuthOid.InitializeFromValue("1.3.6.1.5.5.7.3.2") $serverAuthOid = new-object -com "X509Enrollment.CObjectId" $serverAuthOid.InitializeFromValue("1.3.6.1.5.5.7.3.1") $ekuOids = new-object -com "X509Enrollment.CObjectIds.1" $ekuOids.add($clientAuthOid) $ekuOids.add($serverAuthOid) $ekuExt = new-object -com "X509Enrollment.CX509ExtensionEnhancedKeyUsage" $ekuExt.InitializeEncode($ekuOids) $cert.X509Extensions.Add($ekuext) # #Set Key usage $keyUsage = New-Object -com "X509Enrollment.cx509extensionkeyusage" # XCN_CERT_KEY_ENCIPHERMENT_KEY_USAGE $flags = 0x20 # XCN_CERT_DIGITAL_SIGNATURE_KEY_USAGE $flags = $flags -bor 0x80 $keyUsage.InitializeEncode($flags) $cert.X509Extensions.Add($keyUsage) } # # Subject alternative names if ($_alternativeNames -ne $null) { $names = new-object -com "X509Enrollment.CAlternativeNames" $altNames = new-object -com "X509Enrollment.CX509ExtensionAlternativeNames" foreach ($n in $_alternativeNames) { $name = new-object -com "X509Enrollment.CAlternativeName" # Dns Alternative Name $name.InitializeFromString(3, $n) $names.Add($name) } $altNames.InitializeEncode($names) $cert.X509Extensions.Add($altNames) } $cert.Encode() #$locator = $(New-Object "System.Guid").ToString() $locator = [guid]::NewGuid().ToString() $enrollment = new-object -com "X509Enrollment.CX509Enrollment" $enrollment.CertificateFriendlyName = $locator $enrollment.InitializeFromRequest($cert) $certdata = $enrollment.CreateRequest(0) $enrollment.InstallResponse(2, $certdata, 0, "") # Wait for certificate to be populated $end = $(Get-Date).AddSeconds(1) do { $Certificates = Get-ChildItem Cert:\LocalMachine\My foreach ($item in $Certificates) { if ($item.FriendlyName -eq $locator) { $CACertificate = $item } } } while ($CACertificate -eq $null -and $(Get-Date) -lt $end) $thumbPrint = "" if ($CACertificate -and $CACertificate.Thumbprint) { $thumbPrint = $CACertificate.Thumbprint.Trim() } return $thumbPrint } function Delete-Certificate($_targetThumbPrint, $_targetSSLStore = $TargetSSLStore) { if (-not $_targetThumbPrint) { return ("Error!!! _targetThumbPrint is required") } if (Test-Path "$_targetSSLStore\$_targetThumbPrint") { Remove-Item "$_targetSSLStore\$_targetThumbPrint" -Force -Confirm:$false } if (Test-Path "$_targetSSLStore\$_targetThumbPrint") { return ("Error!!! Failed to delete a certificate of $_targetThumbPrint") } } function Export-CertificateTo($_targetThumbPrint, $_exportToSSLStore, $_password) { if (-not $_targetThumbPrint) { return ("Error!!! _targetThumbPrint is required") } if (-not (Test-Path "$TargetSSLStore\$_targetThumbPrint")) { return ("Error!!! Export failed. Can't find target certificate: $TargetSSLStore\$_targetThumbPrint") } $cert = Get-Item "$TargetSSLStore\$_targetThumbPrint" $tempExportFile = "$env:temp\_tempCertificate.cer" if (Test-Path $tempExportFile) { Remove-Item $tempExportFile -Force -Confirm:$false } # if _exportToSSLStore points to a .pfx file if ($exportToSSLStore.ToLower().EndsWith(".pfx")) { if (-not $_password) { return ("Error!!! _password is required") } $securedPassword = ConvertTo-SecureString -String $_password -Force –AsPlainText $exportedPfxFile = Export-PfxCertificate -FilePath $_exportToSSLStore -Cert $TargetSSLStore\$_targetThumbPrint -Password $securedPassword if ( ($exportedPfxFile -ne $null) -and (Test-Path $exportedPfxFile.FullName) ) { # Succeeded to export to .pfx file return } else { return ("Error!!! Can't export $TargetSSLStore\$_targetThumbPrint to $tempExportFile") } } Export-Certificate -Cert $cert -FilePath $tempExportFile | Out-Null if (-not (Test-Path $tempExportFile)) { return ("Error!!! Can't export $TargetSSLStore\$_targetThumbPrint to $tempExportFile") } # clean up destination SSL store Delete-Certificate $_targetThumbPrint $_exportToSSLStore if (Test-Path "$_exportToSSLStore\$_targetThumbPrint") { return ("Error!!! Can't delete already existing one $_exportToSSLStore\$_targetThumbPrint") } Import-Certificate -CertStoreLocation $_exportToSSLStore -FilePath $tempExportFile | Out-Null if (-not (Test-Path "$_exportToSSLStore\$_targetThumbPrint")) { return ("Error!!! Can't copy $TargetSSLStore\$_targetThumbPrint to $_exportToSSLStore") } } function Get-CertificateThumbPrint($_subject, $_issuerName, $_targetSSLStore) { if (-not $_subject) { return ("Error!!! _subject is required") } if (-not $_targetSSLStore) { return ("Error!!! _targetSSLStore is required") } if (-not (Test-Path "$_targetSSLStore")) { return ("Error!!! Can't find target store") } $targetCertificate = $null $Certificates = Get-ChildItem $_targetSSLStore foreach ($item in $Certificates) { $findItem = $false # check subject name first if ($item.Subject.ToLower() -eq "CN=$_subject".ToLower()) { $findItem = $true } # check issuerName as well if ($_issuerName -and $item.Issuer.ToLower() -ne "CN=$_issuerName".ToLower()) { $findItem = $false } if ($findItem) { $targetCertificate = $item break } } $result = "" if ($targetCertificate) { $result = $targetCertificate.Thumbprint } else { ("Error!!! Can't find target certificate") } return $result } function Get-CertificatePublicKey($_targetThumbPrint) { if (-not $_targetThumbPrint) { return ("Error!!! _targetThumbPrint is required") } if (-not (Test-Path "$TargetSSLStore\$_targetThumbPrint")) { return ("Error!!! Can't find target certificate") } $cert = Get-Item "$TargetSSLStore\$_targetThumbPrint" $byteArray = $cert.Export([System.Security.Cryptography.X509Certificates.X509ContentType]::Cert) $publicKey = [System.Convert]::ToBase64String($byteArray).Trim() return $publicKey } # Error handling and initializing default values if (-not $TargetSSLStore) { $TargetSSLStore = "Cert:\LocalMachine\My" } else { if ($Command -eq "Create-SelfSignedCertificate") { return ("Error!!! Create-SelfSignedCertificate should use default value for -TargetSSLStore if -Issuer is not provided") } } if (-not $ExportToSSLStore) { $ExportToSSLStore = "Cert:\LocalMachine\Root" } switch ($Command) { "Create-SelfSignedCertificate" { return Create-SelfSignedCertificate $Subject $FriendlyName $AlternativeNames $IssuerName } "Delete-Certificate" { return Delete-Certificate $TargetThumbPrint } "Export-CertificateTo" { return Export-CertificateTo $TargetThumbPrint $ExportToSSLStore $PfxPassword } "Get-CertificateThumbPrint" { return Get-CertificateThumbPrint $Subject $IssuerName $TargetSSLStore } "Get-CertificatePublicKey" { return Get-CertificatePublicKey $TargetThumbPrint } default { throw "Unknown command" } }