diff --git a/eng/scripts/X509Certificate2/X509Certificate2.psm1 b/eng/scripts/X509Certificate2/X509Certificate2.psm1 deleted file mode 100644 index f37ba1d584065..0000000000000 --- a/eng/scripts/X509Certificate2/X509Certificate2.psm1 +++ /dev/null @@ -1,339 +0,0 @@ -#Requires -Version 6.0 - -using namespace System.Security.Cryptography -using namespace System.Security.Cryptography.X509Certificates -using namespace System.Text - -<# -.Synopsis -Generate an X509 v3 certificate. - -.Description -Generates an [X509Certificate2] from either a subject name, or individual X500 distinguished names. - -.Parameter SubjectName -The entire X500 subject name. - -.Parameter Country -The country e.g., "US". - -.Parameter State -The state or province e.g., "WA". - -.Parameter City -The city or locality e.g., "Redmond". - -.Parameter Organization -The organization e.g., "Microsoft". - -.Parameter Department -The department e.g., "Azure SDK". - -.Parameter CommonName -A common name e.g., "www.microsoft.com". - -.Parameter SubjectAlternativeNames -Additional subject names from New-X509Certificate2SubjectAlternativeNames. - -.Parameter KeySize -Size of the RSA key. - -.Parameter KeyUsageFlags -Additional key usage flags. - -.Parameter CA -Create self-signed certificate authority. - -.Parameter TLS -Create self-signed certificate suitable for TLS. - -.Parameter NotBefore -The start date when the certificate is valid. The default is the current time. - -.Parameter ValidDays -How many days from NotBefore until the certificate expires. - -.Example -New-X509Certificate2 -SubjectName 'E=opensource@microsoft.com, CN=Azure SDK, OU=Azure SDK, O=Microsoft, L=Redmond, S=WA, C=US' -ValidDays 3652 - -Create a self-signed certificate valid for 10 years from now. - -.Example -New-X509Certificate2 -SubjectName 'CN=Azure SDK' -SubjectAlternativeNames (New-X509Certificate2SubjectAlternativeNames -EmailAddress azuresdk@microsoft.com) -KeyUsageFlags KeyEncipherment, NonRepudiation, DigitalSignature -CA -TLS -ValidDays 3652 - -Create a self-signed certificate valid for 10 years from now with an alternative name, additional key usages including TLS connections, and that can sign other certificate requests. -#> -function New-X509Certificate2 { - [CmdletBinding(DefaultParameterSetName='SubjectName')] - [OutputType([System.Security.Cryptography.X509Certificates.X509Certificate2])] - param ( - [Parameter(ParameterSetName='SubjectName', Mandatory=$true, Position=0)] - [string] $SubjectName, - - [Parameter(ParameterSetName='Builder', HelpMessage='Country Name (2 letter code)')] - [Alias('C')] - [string] $Country, - - [Parameter(ParameterSetName='Builder', HelpMessage='State or Province Name (full name)')] - [Alias('S', 'Province')] - [string] $State, - - [Parameter(ParameterSetName='Builder', HelpMessage='Locality Name (eg, city)')] - [Alias('L', 'Locality')] - [string] $City, - - [Parameter(ParameterSetName='Builder', HelpMessage='Organization Name (eg, company)')] - [Alias('O')] - [string] $Organization, - - [Parameter(ParameterSetName='Builder', HelpMessage='Organizational Unit Name (eg, section)')] - [Alias('OU')] - [string] $Department, - - [Parameter(ParameterSetName='Builder', HelpMessage='Common Name (e.g. server FQDN or YOUR name)')] - [Alias('CN')] - [string] $CommonName, - - [Parameter()] - [ValidateNotNull()] - [SubjectAlternativeNameBuilder] $SubjectAlternativeNames, - - [Parameter()] - [ValidateSet(1024, 2048, 4096)] - [int] $KeySize = 2048, - - [Parameter()] - [X509KeyUsageFlags] $KeyUsageFlags, - - [Parameter()] - [switch] $CA, - - [Parameter()] - [switch] $TLS, - - [Parameter()] - [ValidateNotNullOrEmpty()] - [DateTimeOffset] $NotBefore = [DateTimeOffset]::Now, - - [Parameter()] - [ValidateRange(1, [int]::MaxValue)] - [int] $ValidDays = 365 - ) - - if ($PSCmdlet.ParameterSetName -eq 'Builder') { - $sb = [StringBuilder]::new() - if ($Country) { $null = $sb.Append("C=$Country,") } - if ($State) { $null = $sb.Append("S=$State, ") } - if ($City) { $null = $sb.Append("L=$City, ") } - if ($Organization) { $null = $sb.Append("O=$Organization, ") } - if ($Department) { $null = $sb.Append("OU=$Department, ") } - if ($CommonName) { $null = $sb.Append("CN=$CommonName, ") } - - $SubjectName = [X500DistinguishedName]::new($sb.ToString()).Format($false) - } - - $rsa = [RSA]::Create($KeySize) - try { - $req = [CertificateRequest]::new( - [string] $SubjectName, - $rsa, - [HashAlgorithmName]::SHA256, - [RSASignaturePadding]::Pkcs1 - ) - - $req.CertificateExtensions.Add( - [X509BasicConstraintsExtension]::new( - $CA, - $false, - 0, - $true - ) - ) - - if ($SubjectAlternativeNames) { - $req.CertificateExtensions.Add( - $SubjectAlternativeNames.Build($false) - ) - } - - if ($KeyUsageFlags) { - $req.CertificateExtensions.Add( - [X509KeyUsageExtension]::new( - $KeyUsageFlags, - $true - ) - ) - } - - if ($TLS) { - $oids = [OidCollection]::new() - $null = $oids.Add([Oid]::new('1.3.6.1.5.5.7.3.1')) - - $req.CertificateExtensions.Add( - [X509EnhancedKeyUsageExtension]::new( - $oids, - $false - ) - ) - } - - $req.CreateSelfSigned($NotBefore, $NotBefore.AddDays($ValidDays)) - } - finally { - $rsa.Dispose() - } -} - -<# -.Synopsis -Create subject alternative names for New-X509Certificate2. - -.Description -Subject alternative names include DNS names, email addresses, and IP addresses for which a certificate may also authenticate connections. - -.Parameter DnsName -One or more DNS names e.g., "www.microsoft.com". - -.Parameter EmailAddress -One or more email addresses e.g., "opensource@microsoft.com". - -.Parameter IpAddress -One or more IP addresses. - -.Parameter Uri -Additional URIs not covered by other options. - -.Parameter UserPrincipalName -Additional user names not covered by other options. -#> -function New-X509Certificate2SubjectAlternativeNames { - [CmdletBinding()] - [OutputType([System.Security.Cryptography.X509Certificates.SubjectAlternativeNameBuilder])] - param ( - [Parameter()] - [ValidateNotNullOrEmpty()] - [string[]] $DnsName, - - [Parameter()] - [ValidateNotNullOrEmpty()] - [string[]] $EmailAddress, - - [Parameter()] - [ValidateScript({[System.Net.IPAddress]::TryParse($_, [ref] $null)})] - [string[]] $IpAddress, - - [Parameter()] - [ValidateScript({[System.Uri]::TryParse($_, [ref] $null)})] - [string[]] $Uri, - - [Parameter()] - [ValidateNotNullOrEmpty()] - [string[]] $UserPrincipalName - ) - - $subjectAlternativeNames = [SubjectAlternativeNameBuilder]::new() - - foreach ($value in $DnsName) { - $subjectAlternativeNames.AddDnsName($value) - } - - foreach ($value in $EmailAddress) { - $subjectAlternativeNames.AddEmailAddress($value) - } - - foreach ($value in $IpAddress) { - $subjectAlternativeNames.AddIpAddress($value) - } - - foreach ($value in $Uri) { - $subjectAlternativeNames.AddUri($value) - } - - foreach ($value in $UserPrincipalName) { - $subjectAlternativeNames.AddUserPrincipalName($value) - } - - $subjectAlternativeNames -} - -<# -.Synopsis -Exports a certificate to a file. - -.Description -Exports an X509Certificate2 to a file in one of the given formats. - -.Parameter Path -The path to the file to save. - -.Parameter Type -The type of encoding for the file to save. - -.Parameter Certificate -The certificate to save. -#> -function Export-X509Certificate2 { - [CmdletBinding()] - param ( - [Parameter(Mandatory=$true, Position=0)] - [string] $Path, - - [Parameter(Position=1)] - [ValidateSet('Certificate', 'CertificateBase64', 'Pfx', 'Pkcs1', 'Pkcs12', 'Pkcs12Base64', 'Pkcs8', 'PrivateKey')] - [string] $Type = 'Pfx', - - [Parameter(Mandatory=$true, ValueFromPipeline=$true)] - [X509Certificate2] $Certificate - ) - - if ($Type -in 'Pfx', 'Pkcs12') { - $Certificate.Export([X509ContentType]::Pfx) | Set-Content $Path -AsByteStream - } else { - Format-X509Certificate2 -Type $Type -Certificate $Certificate | Set-Content $Path -Encoding ASCII - } -} - -<# -.Synopsis -Formats a certificate. - -.Description -Formats a certificate and prints it to the output buffer e.g., console. Useful for piping to clip.exe in Windows and pasting into code (additional formatting may be required). - -.Parameter Type -The type of encoding for the output. - -.Parameter Certificate -The certificate to format. -#> -function Format-X509Certificate2 { - [CmdletBinding()] - param ( - [Parameter(Position=0)] - [ValidateSet('Certificate', 'CertificateBase64', 'Pkcs1', 'Pkcs12Base64', 'Pkcs8', 'PrivateKey')] - [string] $Type = 'Certificate', - - [Parameter(Mandatory=$true, ValueFromPipeline=$true)] - [X509Certificate2] $Certificate - ) - - function ConvertTo-Pem($tag, $data) { - @" ------BEGIN $tag----- -$([Convert]::ToBase64String($data, 'InsertLineBreaks')) ------END $tag----- -"@ - } - - if ($Type -eq 'Certificate') { - ConvertTo-Pem 'CERTIFICATE' $Certificate.RawData - } elseif ($Type -eq 'CertificateBase64') { - [Convert]::ToBase64String($Certificate.RawData, 'InsertLineBreaks') - } elseif ($Type -eq 'Pkcs1') { - ConvertTo-Pem 'RSA PRIVATE KEY' $Certificate.PrivateKey.ExportRSAPrivateKey() - } elseif ($Type -eq 'Pkcs12Base64') { - [Convert]::ToBase64String($Certificate.Export([X509ContentType]::Pfx), 'InsertLineBreaks') - } elseif ($Type -in 'Pkcs8', 'PrivateKey') { - ConvertTo-Pem 'PRIVATE KEY' $Certificate.PrivateKey.ExportPkcs8PrivateKey() - } -} diff --git a/sdk/confidentialledger/test-resources-pre.ps1 b/sdk/confidentialledger/test-resources-pre.ps1 index cd76c0e522b1b..4dde43c51a2b2 100644 --- a/sdk/confidentialledger/test-resources-pre.ps1 +++ b/sdk/confidentialledger/test-resources-pre.ps1 @@ -1,4 +1,4 @@ -Import-Module -Name ./eng/scripts/X509Certificate2 -Verbose +Import-Module -Name $PSScriptRoot/../../eng/common/scripts/X509Certificate2 -Verbose $cert = New-X509Certificate2 -SubjectName 'E=opensource@microsoft.com, CN=Azure SDK, OU=Azure SDK, O=Microsoft, L=Frisco, S=TX, C=US' -ValidDays 365 diff --git a/sdk/keyvault/Azure.Security.KeyVault.Certificates/tests/CertificateClientLiveTests.Constants.cs b/sdk/keyvault/Azure.Security.KeyVault.Certificates/tests/CertificateClientLiveTests.Constants.cs index 1df476ea534cd..308c93d55d972 100644 --- a/sdk/keyvault/Azure.Security.KeyVault.Certificates/tests/CertificateClientLiveTests.Constants.cs +++ b/sdk/keyvault/Azure.Security.KeyVault.Certificates/tests/CertificateClientLiveTests.Constants.cs @@ -6,7 +6,7 @@ namespace Azure.Security.KeyVault.Certificates.Tests public partial class CertificateClientLiveTests { /* pwsh - Import-Module -Name eng/scripts/X509Certificate2.psm1 # assumes $PWD is repo root + Import-Module -Name ./eng/common/scripts/X509Certificate2 # assumes $PWD is repo root $cert1 = New-X509Certificate2 -SubjectName 'E=opensource@microsoft.com, CN=Azure SDK, OU=Azure SDK, O=Microsoft, L=Redmond, S=WA, C=US' -ValidDays 3652 $CaPublicKeyBase64 = $cert1 | Format-X509Certificate2 -Type CertificateBase64 diff --git a/sdk/keyvault/test-resources-post.ps1 b/sdk/keyvault/test-resources-post.ps1 index 459b818526af4..3f4d81c1e320b 100644 --- a/sdk/keyvault/test-resources-post.ps1 +++ b/sdk/keyvault/test-resources-post.ps1 @@ -1,118 +1,83 @@ -# Copyright (c) Microsoft Corporation. All rights reserved. -# Licensed under the MIT License. - -# IMPORTANT: Do not invoke this file directly. Please instead run eng/New-TestResources.ps1 from the repository root. - -#Requires -Version 6.0 -#Requires -PSEdition Core - -using namespace System.Security.Cryptography -using namespace System.Security.Cryptography.X509Certificates - -# Use same parameter names as declared in eng/New-TestResources.ps1 (assume validation therein). -[CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'Medium')] -param ( - [Parameter()] - [hashtable] $DeploymentOutputs, - - # Captures any arguments from eng/New-TestResources.ps1 not declared here (no parameter errors). - [Parameter(ValueFromRemainingArguments = $true)] - $RemainingArguments -) - -# By default stop for any error. -if (!$PSBoundParameters.ContainsKey('ErrorAction')) { - $ErrorActionPreference = 'Stop' -} - -function Log($Message) { - Write-Host ('{0} - {1}' -f [DateTime]::Now.ToLongTimeString(), $Message) -} - -function New-X509Certificate2([string] $SubjectName) { - - $rsa = [RSA]::Create(2048) - try { - $req = [CertificateRequest]::new( - [string] $SubjectName, - $rsa, - [HashAlgorithmName]::SHA256, - [RSASignaturePadding]::Pkcs1 - ) - - # TODO: Add any KUs necessary to $req.CertificateExtensions - - $NotBefore = [DateTimeOffset]::Now.AddDays(-1) - $NotAfter = $NotBefore.AddDays(365) - - $req.CreateSelfSigned($NotBefore, $NotAfter) - } - finally { - $rsa.Dispose() - } -} - -function Export-X509Certificate2([string] $Path, [X509Certificate2] $Certificate) { - - $Certificate.Export([X509ContentType]::Pfx) | Set-Content $Path -AsByteStream -} - -function Export-X509Certificate2PEM([string] $Path, [X509Certificate2] $Certificate) { - -@" ------BEGIN CERTIFICATE----- -$([Convert]::ToBase64String($Certificate.RawData, 'InsertLineBreaks')) ------END CERTIFICATE----- -"@ > $Path - -} - -# Make sure we deployed a Managed HSM. -if (!$DeploymentOutputs['AZURE_MANAGEDHSM_URL']) { - Log "Managed HSM not deployed; skipping activation" - exit -} - -[Uri] $hsmUrl = $DeploymentOutputs['AZURE_MANAGEDHSM_URL'] -$hsmName = $hsmUrl.Host.Substring(0, $hsmUrl.Host.IndexOf('.')) - -Log 'Creating 3 X509 certificates to activate security domain' -$wrappingFiles = foreach ($i in 0..2) { - $certificate = New-X509Certificate2 "CN=$($hsmUrl.Host)" - - $baseName = "$PSScriptRoot\$hsmName-certificate$i" - Export-X509Certificate2 "$baseName.pfx" $certificate - Export-X509Certificate2PEM "$baseName.cer" $certificate - - Resolve-Path "$baseName.cer" -} - -Log "Downloading security domain from '$hsmUrl'" - -$sdPath = "$PSScriptRoot\$hsmName-security-domain.key" -if (Test-Path $sdpath) { - Log "Deleting old security domain: $sdPath" - Remove-Item $sdPath -Force -} - -Export-AzKeyVaultSecurityDomain -Name $hsmName -Quorum 2 -Certificates $wrappingFiles -OutputPath $sdPath -ErrorAction SilentlyContinue -Verbose -if ( !$? ) { - Write-Host $Error[0].Exception - Write-Error $Error[0] - - exit -} - -Log "Security domain downloaded to '$sdPath'; Managed HSM is now active at '$hsmUrl'" - -# Force a sleep to wait for Managed HSM activation to propagate through Cosmos replication. Issue tracked in Azure DevOps. -Log 'Sleeping for 30 seconds to allow activation to propagate...' -Start-Sleep -Seconds 30 - -$testApplicationOid = $DeploymentOutputs['CLIENT_OBJECTID'] - -Log "Creating additional required role assignments for '$testApplicationOid'" -$null = New-AzKeyVaultRoleAssignment -HsmName $hsmName -RoleDefinitionName 'Managed HSM Crypto Officer' -ObjectID $testApplicationOid -$null = New-AzKeyVaultRoleAssignment -HsmName $hsmName -RoleDefinitionName 'Managed HSM Crypto User' -ObjectID $testApplicationOid - -Log "Role assignments created for '$testApplicationOid'" +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. + +# IMPORTANT: Do not invoke this file directly. Please instead run eng/New-TestResources.ps1 from the repository root. + +#Requires -Version 6.0 +#Requires -PSEdition Core + +using namespace System.Security.Cryptography +using namespace System.Security.Cryptography.X509Certificates + +# Use same parameter names as declared in eng/New-TestResources.ps1 (assume validation therein). +[CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'Medium')] +param ( + [Parameter()] + [hashtable] $DeploymentOutputs, + + # Captures any arguments from eng/New-TestResources.ps1 not declared here (no parameter errors). + [Parameter(ValueFromRemainingArguments = $true)] + $RemainingArguments +) + +Import-Module -Name $PSScriptRoot/../../eng/common/scripts/X509Certificate2 -Verbose + +# By default stop for any error. +if (!$PSBoundParameters.ContainsKey('ErrorAction')) { + $ErrorActionPreference = 'Stop' +} + +function Log($Message) { + Write-Host ('{0} - {1}' -f [DateTime]::Now.ToLongTimeString(), $Message) +} + + +# Make sure we deployed a Managed HSM. +if (!$DeploymentOutputs['AZURE_MANAGEDHSM_URL']) { + Log "Managed HSM not deployed; skipping activation" + exit +} + +[Uri] $hsmUrl = $DeploymentOutputs['AZURE_MANAGEDHSM_URL'] +$hsmName = $hsmUrl.Host.Substring(0, $hsmUrl.Host.IndexOf('.')) + +Log 'Creating 3 X509 certificates to activate security domain' +$wrappingFiles = foreach ($i in 0..2) { + $certificate = New-X509Certificate2 -SubjectName "CN=$($hsmUrl.Host)" + + $baseName = "$PSScriptRoot\$hsmName-certificate$i" + Export-X509Certificate2 -Path "$baseName.pfx" -Type 'Pfx' -Certificate $certificate + Export-X509Certificate2 -Path "$baseName.cer" -Type 'Certificate' -Certificate $certificate + + Resolve-Path "$baseName.cer" +} + +Log "Downloading security domain from '$hsmUrl'" + +$sdPath = "$PSScriptRoot\$hsmName-security-domain.key" +if (Test-Path $sdpath) { + Log "Deleting old security domain: $sdPath" + Remove-Item $sdPath -Force +} + +Export-AzKeyVaultSecurityDomain -Name $hsmName -Quorum 2 -Certificates $wrappingFiles -OutputPath $sdPath -ErrorAction SilentlyContinue -Verbose +if ( !$? ) { + Write-Host $Error[0].Exception + Write-Error $Error[0] + + exit +} + +Log "Security domain downloaded to '$sdPath'; Managed HSM is now active at '$hsmUrl'" + +# Force a sleep to wait for Managed HSM activation to propagate through Cosmos replication. Issue tracked in Azure DevOps. +Log 'Sleeping for 30 seconds to allow activation to propagate...' +Start-Sleep -Seconds 30 + +$testApplicationOid = $DeploymentOutputs['CLIENT_OBJECTID'] + +Log "Creating additional required role assignments for '$testApplicationOid'" +$null = New-AzKeyVaultRoleAssignment -HsmName $hsmName -RoleDefinitionName 'Managed HSM Crypto Officer' -ObjectID $testApplicationOid +$null = New-AzKeyVaultRoleAssignment -HsmName $hsmName -RoleDefinitionName 'Managed HSM Crypto User' -ObjectID $testApplicationOid + +Log "Role assignments created for '$testApplicationOid'"