500 lines
17 KiB
PowerShell
500 lines
17 KiB
PowerShell
# 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 $thumbPrint2 -TargetSSLStore "Cert:\LocalMachine\My" -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
|
||
}
|
||
|
||
$isThisWin7 = $false
|
||
$exportToSSLStoreName = $null
|
||
$exportToSSLStoreLocation = $null
|
||
$targetSSLStoreName = $null
|
||
$targetSSLStoreLocation = $null
|
||
|
||
if ((Get-Command Export-Certificate 2> out-null) -eq $null)
|
||
{
|
||
$isThisWin7 = $true
|
||
}
|
||
|
||
# if _exportToSSLStore points to a .pfx file
|
||
if ($exportToSSLStore.ToLower().EndsWith(".pfx"))
|
||
{
|
||
if (-not $_password)
|
||
{
|
||
return ("Error!!! _password is required")
|
||
}
|
||
|
||
if ($isThisWin7)
|
||
{
|
||
if ($TargetSSLStore.ToLower().Contains("my"))
|
||
{
|
||
$targetSSLStoreName = "My"
|
||
}
|
||
elseif ($_exportToSSLStore.ToLower().Contains("root"))
|
||
{
|
||
$targetSSLStoreName = "Root"
|
||
}
|
||
else
|
||
{
|
||
throw ("Unsupported store name " + $TargetSSLStore)
|
||
}
|
||
if ($TargetSSLStore.ToLower().Contains("localmachine"))
|
||
{
|
||
$targetSSLStoreLocation = "LocalMachine"
|
||
}
|
||
else
|
||
{
|
||
throw ("Unsupported store location name " + $TargetSSLStore)
|
||
}
|
||
|
||
&certutil.exe @('-exportpfx', '-p', $_password, $targetSSLStoreName, $_targetThumbPrint, $_exportToSSLStore) | out-null
|
||
|
||
if ( Test-Path $_exportToSSLStore )
|
||
{
|
||
# Succeeded to export to .pfx file
|
||
return
|
||
}
|
||
else
|
||
{
|
||
return ("Error!!! Can't export $TargetSSLStore\$_targetThumbPrint to $tempExportFile")
|
||
}
|
||
}
|
||
else
|
||
{
|
||
$securedPassword = ConvertTo-SecureString -String $_password -Force <EFBFBD>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")
|
||
}
|
||
}
|
||
}
|
||
|
||
if ($isThisWin7)
|
||
{
|
||
# Initialize variables for Win7
|
||
if ($_exportToSSLStore.ToLower().Contains("my"))
|
||
{
|
||
$exportToSSLStoreName = [System.Security.Cryptography.X509Certificates.StoreName]::My
|
||
}
|
||
elseif ($_exportToSSLStore.ToLower().Contains("root"))
|
||
{
|
||
$exportToSSLStoreName = [System.Security.Cryptography.X509Certificates.StoreName]::Root
|
||
}
|
||
else
|
||
{
|
||
throw ("Unsupported store name " + $_exportToSSLStore)
|
||
}
|
||
if ($_exportToSSLStore.ToLower().Contains("localmachine"))
|
||
{
|
||
$exportToSSLStoreLocation = [System.Security.Cryptography.X509Certificates.StoreLocation]::LocalMachine
|
||
}
|
||
elseif ($_exportToSSLStore.ToLower().Contains("currentuser"))
|
||
{
|
||
$exportToSSLStoreLocation = [System.Security.Cryptography.X509Certificates.StoreLocation]::CurrentUser
|
||
}
|
||
else
|
||
{
|
||
throw ("Unsupported store location name " + $_exportToSSLStore)
|
||
}
|
||
|
||
# Export-Certificate is not available.
|
||
$isThisWin7 = $true
|
||
$certificate = Get-Item "$TargetSSLStore\$_targetThumbPrint"
|
||
$base64certificate = @"
|
||
-----BEGIN CERTIFICATE-----
|
||
$([Convert]::ToBase64String($certificate.Export('Cert'), [System.Base64FormattingOptions]::InsertLineBreaks)))
|
||
-----END CERTIFICATE-----
|
||
"@
|
||
Set-Content -Path $tempExportFile -Value $base64certificate | Out-Null
|
||
}
|
||
else
|
||
{
|
||
Export-Certificate -Cert $cert -FilePath $tempExportFile | Out-Null
|
||
if (-not (Test-Path $tempExportFile))
|
||
{
|
||
return ("Error!!! Can't export $TargetSSLStore\$_targetThumbPrint to $tempExportFile")
|
||
}
|
||
}
|
||
|
||
if ($isThisWin7)
|
||
{
|
||
[Reflection.Assembly]::Load("System.Security, Version=2.0.0.0, Culture=Neutral, PublicKeyToken=b03f5f7f11d50a3a") | Out-Null
|
||
$cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2($tempExportFile)
|
||
$store = New-Object System.Security.Cryptography.X509Certificates.X509Store($exportToSSLStoreName,$exportToSSLStoreLocation)
|
||
$store.Open([System.Security.Cryptography.X509Certificates.OpenFlags]::ReadWrite) | Out-Null
|
||
$store.Add($cert) | Out-Null
|
||
}
|
||
else
|
||
{
|
||
# 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
|
||
}
|
||
|
||
Sleep 3
|
||
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"
|
||
}
|
||
}
|