Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sign the DAC and DBI during the build process instead of in separate steps #111416

Merged
merged 4 commits into from
Jan 28, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions eng/native/functions.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -678,3 +678,15 @@ function(adhoc_sign_with_entitlements targetName entitlementsFile)
POST_BUILD
COMMAND codesign -s - -f --entitlements ${entitlementsFile} $<TARGET_FILE:${targetName}>)
endfunction()

function(esrp_sign targetName)
if ("${CLR_CMAKE_ESRP_CLIENT}" STREQUAL "")
return()
endif()

add_custom_command(
TARGET ${targetName}
POST_BUILD
COMMAND powershell -ExecutionPolicy ByPass -NoProfile "${CMAKE_CURRENT_FUNCTION_LIST_DIR}/sign-with-dac-certificate.ps1" -esrpClient ${CLR_CMAKE_ESRP_CLIENT} $<TARGET_FILE:${targetName}>
)
endfunction()
50 changes: 50 additions & 0 deletions eng/native/sign-with-dac-certificate.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
[CmdletBinding()]
param(
[string]
[Parameter(Mandatory)]
$esrpClient,
jkoritzinsky marked this conversation as resolved.
Show resolved Hide resolved
[Parameter(ValueFromRemainingArguments=$true)][string[]]$filesToSign
)

$inputFile = Get-Content -Raw $PSScriptRoot/signing/input.template.json | ConvertFrom-Json
$inputFile.SignBatches.SignRequestFiles = $filesToSign | ForEach-Object {
@{
SourceLocation = $_
}
}

$inputJson = [System.IO.Path]::GetTempFileName()
# Our JSON goes up to 6 levels deep, so we need to set the depth to 6
# to successfully round-trip our JSON through ConvertTo-Json
$inputFile | ConvertTo-Json -Depth 6 | Out-File -FilePath $inputJson -Encoding utf8

$outputJson = Resolve-Path "$PSScriptRoot/../../artifacts/log/Release/signing-$(New-Guid).json.log"

Write-Host "Signing files with DAC certificate"
Write-Host "Logging output to $outputJson"

& $esrpClient sign -a $PSScriptRoot/signing/auth.json -c $PSScriptRoot/signing/config.json -i $inputJson -o $outputJson -p $PSScriptRoot/signing/policy.json
jkoritzinsky marked this conversation as resolved.
Show resolved Hide resolved

# Validate that the files are signed correctly
foreach ($file in $filesToSign) {
$signingCert = $(Get-AuthenticodeSignature $file).SignerCertificate
if ($null -eq $signingCert)
{
throw "File $file does not contain a signature."
}

if ($signingCert.Subject -ne "CN=.NET DAC, O=Microsoft Corporation, L=Redmond, S=Washington, C=US" `
-or $signingCert.Issuer -ne "CN=Microsoft Code Signing PCA 2010, O=Microsoft Corporation, L=Redmond, S=Washington, C=US")
{
throw "File $file not in expected trust chain."
}

$certEKU = $signingCert.Extensions.Where({ $_.Oid.FriendlyName -eq "Enhanced Key Usage" }) | Select-Object -First 1

if ($certEKU.EnhancedKeyUsages.Where({ $_.Value -eq "1.3.6.1.4.1.311.84.4.1" }).Count -ne 1)
{
throw "Signature for $file does not contain expected EKU."
}

Write-Host "$file is correctly signed."
}
18 changes: 18 additions & 0 deletions eng/native/signing/auth.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"Version" : "1.0.0",
"AuthenticationType" : "AAD_CERT",
"TenantId" : "72f988bf-86f1-41af-91ab-2d7cd011db47",
"ClientId" : "2234cdec-a13f-4bb2-aa63-04c57fd7a1f9",
"AuthCert" :
{
"SubjectName" : "CN=2234cdec-a13f-4bb2-aa63-04c57fd7a1f9.microsoft.com",
"StoreLocation" : "CurrentUser",
"StoreName": "My",
"SendX5c" : "true"
},
"RequestSigningCert" : {
"SubjectName" : "CN=2234cdec-a13f-4bb2-aa63-04c57fd7a1f9",
"StoreLocation" : "CurrentUser",
"StoreName" : "My"
}
}
6 changes: 6 additions & 0 deletions eng/native/signing/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"Version" : "1.0.0",
"MaxDegreeOfParallelism" : "50",
"ExponentialRetryCount" : "5",
"EsrpSessionTimeoutInSec" : "1800"
}
36 changes: 36 additions & 0 deletions eng/native/signing/input.template.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
{
"Version": "1.0.0",
"UseMinimatch" : false,
"SignBatches": [
{
"SigningInfo" : {
"Operations" : [
{
"keyCode": "CP-471322",
"operationCode": "SigntoolSign",
"parameters": {
"OpusName": "Microsoft",
"OpusInfo": "http://www.microsoft.com",
"PageHash": "/NPH",
"FileDigest": "/fd sha256",
"TimeStamp": "/tr \"http://rfc3161.gtm.corp.microsoft.com/TSS/HttpTspServer\" /td sha256"
},
"toolName": "sign",
"toolVersion": "1.0"
},
{
"KeyCode": "CP-471322",
"OperationCode": "SigntoolVerify",
"Parameters": {},
"ToolName": "sign",
"ToolVersion": "1.0"
}
]
},
"SourceLocationType" : "UNC",
"DestinationLocationType": "UNC",
"SignRequestFiles" : [
]
}
]
}
3 changes: 3 additions & 0 deletions eng/native/signing/policy.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"Version": "1.0.0"
}
34 changes: 34 additions & 0 deletions eng/pipelines/coreclr/templates/install-diagnostic-certs.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
parameters:
isOfficialBuild:
type: boolean
displayName: 'Is Official Build'
certNames:
type: array
displayName: 'Certificate Name'
vaultName:
type: string
displayName: 'Key Vault Name'
azureSubscription:
type: string
displayName: 'Azure Subscription'

steps:
- ${{ if and(eq(parameters.isOfficialBuild, true), ne(variables['Build.Reason'], 'PullRequest'), or(startswith(variables['Build.SourceBranch'], 'refs/heads/release/'), startswith(variables['Build.SourceBranch'], 'refs/heads/internal/release/'), startswith(variables['Build.SourceBranch'], 'refs/heads/reltest/')), not(endsWith(variables['Build.SourceBranch'], '-staging'))) }}:
- task: AzureKeyVault@2
inputs:
azureSubscription: ${{ parameters.azureSubscription }}
KeyVaultName: ${{ parameters.vaultName }}
SecretsFilter: ${{ join(',', parameters.certNames) }}
displayName: 'Download secrets: Diagnostic Certificates'

- task: EsrpClientTool@2
displayName: Download ESRPClient

- powershell: |
eng/pipelines/install-diagnostic-certs.ps1 "${{ join(',', parameters.certNames) }}"
$signArgs = '/p:DotNetEsrpToolPath=$(esrpclient.toolpath)\$(esrpclient.toolname)'
echo "##vso[task.setvariable variable=_SignDiagnosticFilesArgs;]$signArgs"
displayName: 'Install diagnostic certificates'
env:
${{ each cert in parameters.certNames }}:
${{ cert }}: $(${{ cert }})
11 changes: 11 additions & 0 deletions eng/pipelines/coreclr/templates/remove-diagnostic-certs.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
parameters:
isOfficialBuild:
type: boolean
displayName: 'Is Official Build'

steps:
- ${{ if and(eq(parameters.isOfficialBuild, true), ne(variables['Build.Reason'], 'PullRequest'), or(startswith(variables['Build.SourceBranch'], 'refs/heads/release/'), startswith(variables['Build.SourceBranch'], 'refs/heads/internal/release/'), startswith(variables['Build.SourceBranch'], 'refs/heads/reltest/')), not(endsWith(variables['Build.SourceBranch'], '-staging'))) }}:
- powershell: |
eng/pipelines/remove-diagnostic-certs.ps1 "$(DacCertificateThumbprints)"
displayName: 'Remove Diagnostic Certificates'
condition: always()
86 changes: 0 additions & 86 deletions eng/pipelines/coreclr/templates/sign-diagnostic-files.yml

This file was deleted.

32 changes: 32 additions & 0 deletions eng/pipelines/install-diagnostic-certs.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
[CmdletBinding()]
param(
[string]
[Parameter(Mandatory)]
$certList
)
# Required for the pipeline logging functions
$ci = $true
. $PSScriptRoot/../common/pipeline-logging-functions.ps1

$certs = $certList -split ','
$thumbprints = @()
$certCollection = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2Collection
foreach ($cert in $certs)
{
$certBytes = [System.Convert]::FromBase64String($(Get-Item "Env:$cert").Value)
$certCollection.Import($certBytes,$null, [System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::PersistKeySet)
}

foreach ($cert in $certCollection)
{
Write-Host "Installed certificate '$($cert.Thumbprint)' with subject: '$($cert.Subject)'"
$thumbprints += $cert.Thumbprint
}

$store = Get-Item -Path Cert:\CurrentUser\My
$store.Open([System.Security.Cryptography.X509Certificates.OpenFlags]::ReadWrite)
$store.AddRange($certCollection)
$store.Close()

Write-PipelineSetVariable -name "DacCertificateThumbprints" -Value "$($thumbprints -join ',')" -IsMultiJobVariable $false
Write-Host "Successfully installed diagnostic certificates"
22 changes: 22 additions & 0 deletions eng/pipelines/remove-diagnostic-certs.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
[CmdletBinding()]
param(
[string]
[Parameter(Mandatory)]
$thumbprintList
)

$thumbprints = $thumbprintList -split ','
$store = Get-Item -Path Cert:\CurrentUser\My
$store.Open([System.Security.Cryptography.X509Certificates.OpenFlags]::ReadWrite)
foreach ($thumbprint in $thumbprints)
{
$cert = $store.Certificates.Find([System.Security.Cryptography.X509Certificates.X509FindType]::FindByThumbprint, $thumbprint, $false)
if ($null -eq $cert)
{
Write-Host "Certificate with thumbprint '$thumbprint' not found in the user store."
}
$store.RemoveRange($cert)
Write-Host "Removed certificate '$thumbprint'"
}
$store.Close()
Write-Host "Successfully removed diagnostic certificates"
42 changes: 27 additions & 15 deletions eng/pipelines/runtime-official.yml
Original file line number Diff line number Diff line change
Expand Up @@ -70,23 +70,29 @@ extends:
- windows_x64
- windows_x86
- windows_arm64
variables:
- name: _SignDiagnosticFilesArgs
value: ''
jobParameters:
templatePath: 'templates-official'
buildArgs: -s clr.runtime+clr.alljits+clr.nativeaotruntime -c $(_BuildConfig) /bl:$(Build.SourcesDirectory)/artifacts/logs/$(_BuildConfig)/CoreClrNativeBuild.binlog
preBuildSteps:
- template: /eng/pipelines/coreclr/templates/install-diagnostic-certs.yml
parameters:
isOfficialBuild: ${{ variables.isOfficialBuild }}
certNames:
- 'dotnetesrp-diagnostics-aad-ssl-cert'
- 'dotnet-diagnostics-esrp-pki-onecert'
vaultName: 'clrdiag-esrp-id'
azureSubscription: 'diagnostics-esrp-kvcertuser'

buildArgs: -c $(_BuildConfig) /p:DotNetBuildAllRuntimePacks=true $(_SignDiagnosticFilesArgs)
nameSuffix: AllRuntimes
isOfficialBuild: ${{ variables.isOfficialBuild }}
timeoutInMinutes: 120
postBuildSteps:
- template: /eng/pipelines/coreclr/templates/sign-diagnostic-files.yml
- template: /eng/pipelines/coreclr/templates/remove-diagnostic-certs.yml
parameters:
basePath: $(Build.SourcesDirectory)/artifacts/bin/coreclr
isOfficialBuild: ${{ variables.isOfficialBuild }}
timeoutInMinutes: 30
# Now that we've signed the diagnostic files, do the rest of the build.
- template: /eng/pipelines/common/templates/global-build-step.yml
parameters:
buildArgs: -s clr.corelib+clr.nativecorelib+clr.nativeaotlibs+clr.tools+clr.packages+mono+libs+host+packs -c $(_BuildConfig) /p:DotNetBuildAllRuntimePacks=true
displayName: Build managed CoreCLR components, Mono, all libraries, hosts, and packs

# Upload the results.
- template: /eng/pipelines/common/upload-intermediate-artifacts-step.yml
Expand Down Expand Up @@ -209,10 +215,21 @@ extends:
artifactName: $(crossDacArtifactsContainer)
downloadPath: $(crossDacArtifactsBasePath)
checkDownloadedFiles: true
- template: /eng/pipelines/coreclr/templates/install-diagnostic-certs.yml
parameters:
isOfficialBuild: ${{ variables.isOfficialBuild }}
certNames:
- 'dotnetesrp-diagnostics-aad-ssl-cert'
- 'dotnet-diagnostics-esrp-pki-onecert'
vaultName: 'clrdiag-esrp-id'
azureSubscription: 'diagnostics-esrp-kvcertuser'
- template: /eng/pipelines/common/templates/global-build-step.yml
parameters:
buildArgs: -s linuxdac+alpinedac -c $(_BuildConfig)
buildArgs: -s linuxdac+alpinedac -c $(_BuildConfig) $(_SignDiagnosticFilesArgs)
archParameter: -arch x64,x86,arm,arm64
- template: /eng/pipelines/coreclr/templates/remove-diagnostic-certs.yml
parameters:
isOfficialBuild: ${{ variables.isOfficialBuild }}
- task: CopyFiles@2
displayName: Gather CrossDacs
inputs:
Expand All @@ -221,11 +238,6 @@ extends:
**
!**\sharedFramework\**
TargetFolder: $(crossDacArtifactsPath)
- template: /eng/pipelines/coreclr/templates/sign-diagnostic-files.yml
parameters:
basePath: $(crossDacArtifactsPath)
isOfficialBuild: ${{ variables.isOfficialBuild }}
timeoutInMinutes: 30
postBuildSteps:
# Save packages using the prepare-signed-artifacts format.
# CrossDac packages are expected to be in the windows_x64 folder.
Expand Down
Loading
Loading