From 371ad803b744617f2fc706ad68cdcc92f7caf09e Mon Sep 17 00:00:00 2001 From: Andrea Bianchi Date: Mon, 3 Nov 2025 16:56:33 +0000 Subject: [PATCH 01/26] Add HSM signing with certificate thumbprint and dispatch to test from local --- .github/workflows/release.yml | 61 ++++++++++++++++++++++++++--------- 1 file changed, 45 insertions(+), 16 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c344447..582d64a 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -3,6 +3,17 @@ name: Publish Nuget on: release: types: [published] + workflow_dispatch: + inputs: + version: + description: 'Version to build (e.g., 1.0.0)' + required: false + default: '0.0.1-test' + skip_publish: + description: 'Skip publishing to NuGet (test signing only)' + required: false + type: boolean + default: true jobs: build: @@ -10,12 +21,16 @@ jobs: BUILD_CONFIG: 'Release' PROJECT: 'Dojo.Net.csproj' + runs-on: windows-latest + + permissions: + contents: 'read' + id-token: 'write' + defaults: run: working-directory: ./src - runs-on: ubuntu-latest - steps: - uses: actions/checkout@v4 @@ -28,23 +43,37 @@ jobs: run: dotnet restore - name: Build - run: dotnet build --configuration $BUILD_CONFIG -p:Version=${{ github.event.release.tag_name }} --no-restore + run: dotnet build --configuration $env:BUILD_CONFIG -p:Version=${{ github.event.release.tag_name || inputs.version }} --no-restore - name: Run tests - run: dotnet test /p:Configuration=$BUILD_CONFIG --no-restore --no-build --verbosity normal + run: dotnet test /p:Configuration=$env:BUILD_CONFIG --no-restore --no-build --verbosity normal + + - name: Authenticate to Google Cloud + uses: google-github-actions/auth@v2 + with: + project_id: ${{ secrets.GCP_PROJECT_ID }} + workload_identity_provider: ${{ secrets.GCP_WIF_PROVIDER }} - - # RELEASE UNSIGNED FOR NOW, new signing cert in a gcp wallet is being issued, - # waiting to get workload identity federation for this repo to be set up - # - name: Export certificate - # id: write_file - # uses: timheuer/base64-to-file@v1.1 - # with: - # fileName: 'sign.cer' - # encodedString: ${{ secrets.NUGET_SIGNING_CERT }} + - name: Setup Google Cloud SDK + uses: google-github-actions/setup-gcloud@v2 - # - name: Sign - # run: dotnet nuget sign **\*.nupkg --certificate-path ${{ steps.write_file.outputs.filePath }} --certificate-password ${{ secrets.NUGET_SIGNING_CERT_PASSWORD }} --timestamper http://timestamp.digicert.com + - name: Install Google Cloud HSM SignTool + run: | + gcloud components install kms-windows-cng --quiet + + - name: Sign NuGet packages + shell: pwsh + run: | + $packages = Get-ChildItem -Recurse -Filter "*.nupkg" | Where-Object { $_.Name -notlike "*.symbols.nupkg" } + foreach ($package in $packages) { + Write-Host "Signing $($package.FullName)" + & signtool sign /v /fd SHA256 /tr http://timestamp.digicert.com /td SHA256 ` + /sha1 90C591BF5D43D241871569B3A222A72DD76DFA8A ` + /csp "Google Cloud KMS Provider" ` + /kc "projects/${{ secrets.GCP_PROJECT_ID }}/locations/europe-west2/keyRings/EVCodeSigningKeyRing/cryptoKeys/EVCodeSignDojo/cryptoKeyVersions/1" ` + $package.FullName + } - name: Publish - run: dotnet nuget push **/*.nupkg --source 'https://api.nuget.org/v3/index.json' --api-key ${{secrets.NUGET_API_KEY}} + if: ${{ github.event_name == 'release' || inputs.skip_publish == false }} + run: dotnet nuget push **/*.nupkg --source 'https://api.nuget.org/v3/index.json' --api-key ${{secrets.NUGET_API_KEY}} --skip-duplicate From 23ec67b863cc2c7538a552d7b6816984ba6a2dc4 Mon Sep 17 00:00:00 2001 From: Andrea Bianchi Date: Mon, 3 Nov 2025 17:03:38 +0000 Subject: [PATCH 02/26] run the signing on PR --- .github/workflows/release.yml | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 582d64a..01735f1 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -3,17 +3,9 @@ name: Publish Nuget on: release: types: [published] - workflow_dispatch: - inputs: - version: - description: 'Version to build (e.g., 1.0.0)' - required: false - default: '0.0.1-test' - skip_publish: - description: 'Skip publishing to NuGet (test signing only)' - required: false - type: boolean - default: true + pull_request: + branches: + - main jobs: build: @@ -43,7 +35,7 @@ jobs: run: dotnet restore - name: Build - run: dotnet build --configuration $env:BUILD_CONFIG -p:Version=${{ github.event.release.tag_name || inputs.version }} --no-restore + run: dotnet build --configuration $env:BUILD_CONFIG -p:Version=${{ github.event.release.tag_name || '0.0.1-test' }} --no-restore - name: Run tests run: dotnet test /p:Configuration=$env:BUILD_CONFIG --no-restore --no-build --verbosity normal @@ -75,5 +67,5 @@ jobs: } - name: Publish - if: ${{ github.event_name == 'release' || inputs.skip_publish == false }} + if: ${{ github.event_name == 'release' }} run: dotnet nuget push **/*.nupkg --source 'https://api.nuget.org/v3/index.json' --api-key ${{secrets.NUGET_API_KEY}} --skip-duplicate From 92e3feac0192f144b018059578e7a09a22ad0a37 Mon Sep 17 00:00:00 2001 From: Andrea Bianchi Date: Mon, 3 Nov 2025 17:25:15 +0000 Subject: [PATCH 03/26] add maxcpucount for windows builder race condition --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 01735f1..3cc10ac 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -35,7 +35,7 @@ jobs: run: dotnet restore - name: Build - run: dotnet build --configuration $env:BUILD_CONFIG -p:Version=${{ github.event.release.tag_name || '0.0.1-test' }} --no-restore + run: dotnet build --configuration $env:BUILD_CONFIG -p:Version=${{ github.event.release.tag_name || '0.0.1-test' }} --no-restore /maxcpucount:1 - name: Run tests run: dotnet test /p:Configuration=$env:BUILD_CONFIG --no-restore --no-build --verbosity normal From 21a4fdf3fb08836df748ed00c4bd77a9d2c76cd1 Mon Sep 17 00:00:00 2001 From: Andrea Bianchi Date: Mon, 3 Nov 2025 17:31:23 +0000 Subject: [PATCH 04/26] auth first, use v3 action --- .github/workflows/release.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 3cc10ac..2cecdfe 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -34,14 +34,8 @@ jobs: - name: Restore dependencies run: dotnet restore - - name: Build - run: dotnet build --configuration $env:BUILD_CONFIG -p:Version=${{ github.event.release.tag_name || '0.0.1-test' }} --no-restore /maxcpucount:1 - - - name: Run tests - run: dotnet test /p:Configuration=$env:BUILD_CONFIG --no-restore --no-build --verbosity normal - - name: Authenticate to Google Cloud - uses: google-github-actions/auth@v2 + uses: google-github-actions/auth@v3 with: project_id: ${{ secrets.GCP_PROJECT_ID }} workload_identity_provider: ${{ secrets.GCP_WIF_PROVIDER }} @@ -52,6 +46,12 @@ jobs: - name: Install Google Cloud HSM SignTool run: | gcloud components install kms-windows-cng --quiet + + - name: Build + run: dotnet build --configuration $env:BUILD_CONFIG -p:Version=${{ github.event.release.tag_name || '0.0.1-test' }} --no-restore /maxcpucount:1 + + - name: Run tests + run: dotnet test /p:Configuration=$env:BUILD_CONFIG --no-restore --no-build --verbosity normal - name: Sign NuGet packages shell: pwsh From f6a78be9fc976e4b7894b9442961f2e71ae48e4b Mon Sep 17 00:00:00 2001 From: Andrea Bianchi Date: Tue, 4 Nov 2025 10:19:59 +0000 Subject: [PATCH 05/26] download and install kms cng provider --- .github/workflows/release.yml | 42 ++++++++++++++++++++++++++++++----- 1 file changed, 37 insertions(+), 5 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 2cecdfe..c5925dc 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -35,17 +35,49 @@ jobs: run: dotnet restore - name: Authenticate to Google Cloud - uses: google-github-actions/auth@v3 + uses: google-github-actions/auth@v2 with: project_id: ${{ secrets.GCP_PROJECT_ID }} workload_identity_provider: ${{ secrets.GCP_WIF_PROVIDER }} - - name: Setup Google Cloud SDK - uses: google-github-actions/setup-gcloud@v2 + - name: Download and install Google Cloud KMS CNG Provider + shell: pwsh + run: | + # Download KMS CNG Provider v1.3 + $version = "1.3" + $downloadUrl = "https://github.com/GoogleCloudPlatform/kms-integrations/releases/download/cng-v$version/kmscng-$version-windows-amd64.zip" + + Write-Host "Downloading KMS CNG Provider v$version from $downloadUrl" + Invoke-WebRequest -Uri $downloadUrl -OutFile "kmscng.zip" + + # Extract the ZIP + Write-Host "Extracting KMS CNG Provider" + Expand-Archive -Path "kmscng.zip" -DestinationPath "kmscng" -Force + + # Install the MSI + $msiFile = "kmscng\kmscng.msi" + Write-Host "Installing KMS CNG Provider from $msiFile" + Start-Process msiexec.exe -ArgumentList '/i', $msiFile, '/quiet', '/norestart' -Wait -NoNewWindow + + Write-Host "KMS CNG Provider v$version installed successfully" - - name: Install Google Cloud HSM SignTool + - name: Configure KMS CNG Provider + shell: pwsh run: | - gcloud components install kms-windows-cng --quiet + # Create the config directory + $configDir = "C:\Windows\KMSCNG" + New-Item -ItemType Directory -Force -Path $configDir | Out-Null + + # Create the config file + $configContent = @" + --- + resources: + - crypto_key_version: "projects/${{ secrets.GCP_PROJECT_ID }}/locations/europe-west2/keyRings/EVCodeSigningKeyRing/cryptoKeys/EVCodeSignDojo/cryptoKeyVersions/1" + "@ + + Set-Content -Path "$configDir\config.yaml" -Value $configContent + Write-Host "Configuration file created at $configDir\config.yaml" + Get-Content "$configDir\config.yaml" - name: Build run: dotnet build --configuration $env:BUILD_CONFIG -p:Version=${{ github.event.release.tag_name || '0.0.1-test' }} --no-restore /maxcpucount:1 From b46a082d76b77cb50231b006bfa958bba3be7028 Mon Sep 17 00:00:00 2001 From: Andrea Bianchi Date: Tue, 4 Nov 2025 10:35:13 +0000 Subject: [PATCH 06/26] add signtool to path --- .github/workflows/release.yml | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c5925dc..fbfd23a 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -88,10 +88,22 @@ jobs: - name: Sign NuGet packages shell: pwsh run: | + # Find SignTool from Windows SDK + $signtoolPath = Get-ChildItem -Path "C:\Program Files (x86)\Windows Kits\" -Recurse -Filter "signtool.exe" -ErrorAction SilentlyContinue | + Where-Object { $_.FullName -like "*\x64\*" } | + Select-Object -First 1 + + if (-not $signtoolPath) { + Write-Error "Could not find signtool.exe" + exit 1 + } + + Write-Host "Using SignTool from: $($signtoolPath.FullName)" + $packages = Get-ChildItem -Recurse -Filter "*.nupkg" | Where-Object { $_.Name -notlike "*.symbols.nupkg" } foreach ($package in $packages) { Write-Host "Signing $($package.FullName)" - & signtool sign /v /fd SHA256 /tr http://timestamp.digicert.com /td SHA256 ` + & $signtoolPath.FullName sign /v /fd SHA256 /tr http://timestamp.digicert.com /td SHA256 ` /sha1 90C591BF5D43D241871569B3A222A72DD76DFA8A ` /csp "Google Cloud KMS Provider" ` /kc "projects/${{ secrets.GCP_PROJECT_ID }}/locations/europe-west2/keyRings/EVCodeSigningKeyRing/cryptoKeys/EVCodeSignDojo/cryptoKeyVersions/1" ` From 906d8c5c57a76fcbabdb02ce511a72f05dba6a1e Mon Sep 17 00:00:00 2001 From: Andrea Bianchi Date: Tue, 4 Nov 2025 10:39:21 +0000 Subject: [PATCH 07/26] debug available keys from the chain --- .github/workflows/release.yml | 36 +++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index fbfd23a..282d2f1 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -79,6 +79,42 @@ jobs: Write-Host "Configuration file created at $configDir\config.yaml" Get-Content "$configDir\config.yaml" + - name: Debug - List available certificates and keys + shell: pwsh + run: | + Write-Host "=== Checking KMS CNG Provider Configuration ===" + if (Test-Path "C:\Windows\KMSCNG\config.yaml") { + Write-Host "Config file exists:" + Get-Content "C:\Windows\KMSCNG\config.yaml" + } else { + Write-Host "WARNING: Config file not found!" + } + + Write-Host "`n=== Listing Certificate Stores ===" + Get-ChildItem Cert:\CurrentUser\My | Format-Table Subject, Thumbprint, NotAfter + Get-ChildItem Cert:\LocalMachine\My | Format-Table Subject, Thumbprint, NotAfter + + Write-Host "`n=== Checking for Google Cloud KMS Provider ===" + # Try to list keys using CNG provider + certutil -csp "Google Cloud KMS Provider" -key + + Write-Host "`n=== Checking if our certificate thumbprint exists ===" + $thumbprint = "90C591BF5D43D241871569B3A222A72DD76DFA8A" + $cert = Get-ChildItem Cert:\CurrentUser\My\$thumbprint -ErrorAction SilentlyContinue + if ($cert) { + Write-Host "Found certificate in CurrentUser\My: $($cert.Subject)" + } else { + $cert = Get-ChildItem Cert:\LocalMachine\My\$thumbprint -ErrorAction SilentlyContinue + if ($cert) { + Write-Host "Found certificate in LocalMachine\My: $($cert.Subject)" + } else { + Write-Host "Certificate with thumbprint $thumbprint NOT FOUND in certificate stores" + } + } + + - name: Restore dependencies + run: dotnet restore + - name: Build run: dotnet build --configuration $env:BUILD_CONFIG -p:Version=${{ github.event.release.tag_name || '0.0.1-test' }} --no-restore /maxcpucount:1 From 02af67d1a1ee4607f391af8b15de23f9c38e4fe3 Mon Sep 17 00:00:00 2001 From: Andrea Bianchi Date: Tue, 4 Nov 2025 10:43:11 +0000 Subject: [PATCH 08/26] use full path when instaling kms provider --- .github/workflows/release.yml | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 282d2f1..0bb92cb 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -54,10 +54,29 @@ jobs: Write-Host "Extracting KMS CNG Provider" Expand-Archive -Path "kmscng.zip" -DestinationPath "kmscng" -Force - # Install the MSI - $msiFile = "kmscng\kmscng.msi" + # Install the MSI with full path + $msiFile = Resolve-Path "kmscng\kmscng.msi" Write-Host "Installing KMS CNG Provider from $msiFile" - Start-Process msiexec.exe -ArgumentList '/i', $msiFile, '/quiet', '/norestart' -Wait -NoNewWindow + + $installArgs = @( + '/i' + $msiFile.Path + '/quiet' + '/norestart' + '/l*v' + 'install.log' + ) + + $process = Start-Process msiexec.exe -ArgumentList $installArgs -Wait -NoNewWindow -PassThru + + if ($process.ExitCode -ne 0) { + Write-Host "Installation failed with exit code: $($process.ExitCode)" + if (Test-Path 'install.log') { + Write-Host "Installation log:" + Get-Content 'install.log' | Select-Object -Last 50 + } + exit 1 + } Write-Host "KMS CNG Provider v$version installed successfully" From 624f4b8a7c881b72b845d47665303e112dd8be60 Mon Sep 17 00:00:00 2001 From: Andrea Bianchi Date: Tue, 4 Nov 2025 10:47:18 +0000 Subject: [PATCH 09/26] try using a different working dir --- .github/workflows/release.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 0bb92cb..ac4221c 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -42,6 +42,7 @@ jobs: - name: Download and install Google Cloud KMS CNG Provider shell: pwsh + working-directory: ${{ github.workspace }} run: | # Download KMS CNG Provider v1.3 $version = "1.3" @@ -54,6 +55,10 @@ jobs: Write-Host "Extracting KMS CNG Provider" Expand-Archive -Path "kmscng.zip" -DestinationPath "kmscng" -Force + # List contents to debug + Write-Host "Contents of kmscng directory:" + Get-ChildItem -Path "kmscng" -Recurse + # Install the MSI with full path $msiFile = Resolve-Path "kmscng\kmscng.msi" Write-Host "Installing KMS CNG Provider from $msiFile" From ab88019105592d465064bad732e73d2217f4d5cb Mon Sep 17 00:00:00 2001 From: Andrea Bianchi Date: Tue, 4 Nov 2025 10:50:58 +0000 Subject: [PATCH 10/26] use correct directory --- .github/workflows/release.yml | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ac4221c..cf1ac70 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -55,12 +55,8 @@ jobs: Write-Host "Extracting KMS CNG Provider" Expand-Archive -Path "kmscng.zip" -DestinationPath "kmscng" -Force - # List contents to debug - Write-Host "Contents of kmscng directory:" - Get-ChildItem -Path "kmscng" -Recurse - # Install the MSI with full path - $msiFile = Resolve-Path "kmscng\kmscng.msi" + $msiFile = Resolve-Path "kmscng\kmscng-$version-windows-amd64\kmscng.msi" Write-Host "Installing KMS CNG Provider from $msiFile" $installArgs = @( From 93eea44ea4f48a33865f04ef8b0f4cfdb6aa42cb Mon Sep 17 00:00:00 2001 From: Andrea Bianchi Date: Tue, 4 Nov 2025 11:02:36 +0000 Subject: [PATCH 11/26] try dumping some gcloud perms data --- .github/workflows/release.yml | 40 ++++++++++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index cf1ac70..57de23d 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -102,7 +102,14 @@ jobs: - name: Debug - List available certificates and keys shell: pwsh run: | - Write-Host "=== Checking KMS CNG Provider Configuration ===" + Write-Host "=== GCP Project Information ===" + Write-Host "GCP_PROJECT_ID secret value: ${{ secrets.GCP_PROJECT_ID }}" + Write-Host "Getting project details..." + $projectInfo = gcloud projects describe ${{ secrets.GCP_PROJECT_ID }} --format=json | ConvertFrom-Json + Write-Host "Project ID (name): $($projectInfo.projectId)" + Write-Host "Project Number: $($projectInfo.projectNumber)" + + Write-Host "`n=== Checking KMS CNG Provider Configuration ===" if (Test-Path "C:\Windows\KMSCNG\config.yaml") { Write-Host "Config file exists:" Get-Content "C:\Windows\KMSCNG\config.yaml" @@ -110,6 +117,37 @@ jobs: Write-Host "WARNING: Config file not found!" } + Write-Host "`n=== Testing GCP Authentication ===" + gcloud auth list + + Write-Host "`n=== Testing KMS Key Access ===" + # Try to get the public key + gcloud kms keys versions get-public-key 1 ` + --key EVCodeSignDojo ` + --keyring EVCodeSigningKeyRing ` + --location europe-west2 ` + --project ${{ secrets.GCP_PROJECT_ID }} ` + --output-file public-key.pem 2>&1 + + if (Test-Path "public-key.pem") { + Write-Host "Successfully retrieved public key!" + Get-Content "public-key.pem" + } else { + Write-Host "Failed to retrieve public key" + } + + Write-Host "`n=== Listing accessible KMS keys ===" + gcloud kms keys list ` + --keyring EVCodeSigningKeyRing ` + --location europe-west2 ` + --project ${{ secrets.GCP_PROJECT_ID }} 2>&1 + + Write-Host "`n=== Checking IAM permissions on the key ===" + gcloud kms keys get-iam-policy EVCodeSignDojo ` + --keyring EVCodeSigningKeyRing ` + --location europe-west2 ` + --project ${{ secrets.GCP_PROJECT_ID }} 2>&1 + Write-Host "`n=== Listing Certificate Stores ===" Get-ChildItem Cert:\CurrentUser\My | Format-Table Subject, Thumbprint, NotAfter Get-ChildItem Cert:\LocalMachine\My | Format-Table Subject, Thumbprint, NotAfter From 541e173c65865c234f423b6db473910ad90e70f9 Mon Sep 17 00:00:00 2001 From: Andrea Bianchi Date: Tue, 4 Nov 2025 11:05:44 +0000 Subject: [PATCH 12/26] set up gcloud sdk --- .github/workflows/release.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 57de23d..28f1f8c 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -40,6 +40,9 @@ jobs: project_id: ${{ secrets.GCP_PROJECT_ID }} workload_identity_provider: ${{ secrets.GCP_WIF_PROVIDER }} + - name: Setup Google Cloud SDK + uses: google-github-actions/setup-gcloud@v3 + - name: Download and install Google Cloud KMS CNG Provider shell: pwsh working-directory: ${{ github.workspace }} From 37c9d06065b43a1a72907cbead30e996686c8a9f Mon Sep 17 00:00:00 2001 From: Andrea Bianchi Date: Tue, 4 Nov 2025 11:18:00 +0000 Subject: [PATCH 13/26] add cert pub key to windows keystore --- .github/workflows/release.yml | 59 ++++++++++++++++++++++++++--------- 1 file changed, 45 insertions(+), 14 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 28f1f8c..8e6eeab 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -150,6 +150,51 @@ jobs: --keyring EVCodeSigningKeyRing ` --location europe-west2 ` --project ${{ secrets.GCP_PROJECT_ID }} 2>&1 + + - name: Import certificate into Windows certificate store + shell: pwsh + working-directory: ${{ github.workspace }} + run: | + Write-Host "=== Importing Public Certificate ===" + + # We should have the public key from the previous step + # Convert PEM to CER format and import + if (Test-Path "public-key.pem") { + # Import the certificate that was uploaded to NuGet + $certPath = "${{ github.workspace }}/publicert.cer" + + if (Test-Path $certPath) { + Write-Host "Importing certificate from $certPath" + + # Import to LocalMachine\My store + $cert = Import-Certificate -FilePath $certPath -CertStoreLocation Cert:\LocalMachine\My + Write-Host "Certificate imported successfully" + Write-Host "Subject: $($cert.Subject)" + Write-Host "Thumbprint: $($cert.Thumbprint)" + Write-Host "NotAfter: $($cert.NotAfter)" + + # Verify it matches our expected thumbprint + if ($cert.Thumbprint -eq "90C591BF5D43D241871569B3A222A72DD76DFA8A") { + Write-Host "✓ Certificate thumbprint matches!" + } else { + Write-Host "✗ WARNING: Thumbprint mismatch! Expected: 90C591BF5D43D241871569B3A222A72DD76DFA8A, Got: $($cert.Thumbprint)" + } + } else { + Write-Host "ERROR: publicert.cer not found in repository" + exit 1 + } + } else { + Write-Host "WARNING: public-key.pem not found from previous step" + } + + Write-Host "`n=== Verifying Certificate Import ===" + $thumbprint = "90C591BF5D43D241871569B3A222A72DD76DFA8A" + $cert = Get-ChildItem Cert:\LocalMachine\My\$thumbprint -ErrorAction SilentlyContinue + if ($cert) { + Write-Host "✓ Certificate found in LocalMachine\My store" + } else { + Write-Host "✗ Certificate NOT found in certificate store" + } Write-Host "`n=== Listing Certificate Stores ===" Get-ChildItem Cert:\CurrentUser\My | Format-Table Subject, Thumbprint, NotAfter @@ -158,20 +203,6 @@ jobs: Write-Host "`n=== Checking for Google Cloud KMS Provider ===" # Try to list keys using CNG provider certutil -csp "Google Cloud KMS Provider" -key - - Write-Host "`n=== Checking if our certificate thumbprint exists ===" - $thumbprint = "90C591BF5D43D241871569B3A222A72DD76DFA8A" - $cert = Get-ChildItem Cert:\CurrentUser\My\$thumbprint -ErrorAction SilentlyContinue - if ($cert) { - Write-Host "Found certificate in CurrentUser\My: $($cert.Subject)" - } else { - $cert = Get-ChildItem Cert:\LocalMachine\My\$thumbprint -ErrorAction SilentlyContinue - if ($cert) { - Write-Host "Found certificate in LocalMachine\My: $($cert.Subject)" - } else { - Write-Host "Certificate with thumbprint $thumbprint NOT FOUND in certificate stores" - } - } - name: Restore dependencies run: dotnet restore From 7f899721f143010faea1f6446a3d95c010019b5b Mon Sep 17 00:00:00 2001 From: Andrea Bianchi Date: Tue, 4 Nov 2025 11:33:27 +0000 Subject: [PATCH 14/26] use the public key out of the kms keychain itself, should make it less work when this rotates --- .github/workflows/release.yml | 55 ++++++++++++++++++----------------- 1 file changed, 28 insertions(+), 27 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 8e6eeab..6d29c7d 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -157,34 +157,35 @@ jobs: run: | Write-Host "=== Importing Public Certificate ===" - # We should have the public key from the previous step - # Convert PEM to CER format and import - if (Test-Path "public-key.pem") { - # Import the certificate that was uploaded to NuGet - $certPath = "${{ github.workspace }}/publicert.cer" - - if (Test-Path $certPath) { - Write-Host "Importing certificate from $certPath" - - # Import to LocalMachine\My store - $cert = Import-Certificate -FilePath $certPath -CertStoreLocation Cert:\LocalMachine\My - Write-Host "Certificate imported successfully" - Write-Host "Subject: $($cert.Subject)" - Write-Host "Thumbprint: $($cert.Thumbprint)" - Write-Host "NotAfter: $($cert.NotAfter)" - - # Verify it matches our expected thumbprint - if ($cert.Thumbprint -eq "90C591BF5D43D241871569B3A222A72DD76DFA8A") { - Write-Host "✓ Certificate thumbprint matches!" - } else { - Write-Host "✗ WARNING: Thumbprint mismatch! Expected: 90C591BF5D43D241871569B3A222A72DD76DFA8A, Got: $($cert.Thumbprint)" - } - } else { - Write-Host "ERROR: publicert.cer not found in repository" - exit 1 - } + # Convert PEM to CER format using OpenSSL (comes with Git for Windows) + if (-not (Test-Path "public-key.pem")) { + Write-Host "ERROR: public-key.pem not found from previous step" + exit 1 + } + + Write-Host "Converting public-key.pem to DER format..." + & "C:\Program Files\Git\usr\bin\openssl.exe" x509 -in public-key.pem -inform PEM -out publicert.cer -outform DER + + if (-not (Test-Path "publicert.cer")) { + Write-Host "ERROR: Failed to convert PEM to CER" + exit 1 + } + + $certPath = "${{ github.workspace }}/publicert.cer" + Write-Host "Importing certificate from $certPath" + + # Import to LocalMachine\My store + $cert = Import-Certificate -FilePath $certPath -CertStoreLocation Cert:\LocalMachine\My + Write-Host "Certificate imported successfully" + Write-Host "Subject: $($cert.Subject)" + Write-Host "Thumbprint: $($cert.Thumbprint)" + Write-Host "NotAfter: $($cert.NotAfter)" + + # Verify it matches our expected thumbprint + if ($cert.Thumbprint -eq "90C591BF5D43D241871569B3A222A72DD76DFA8A") { + Write-Host "✓ Certificate thumbprint matches!" } else { - Write-Host "WARNING: public-key.pem not found from previous step" + Write-Host "✗ WARNING: Thumbprint mismatch! Expected: 90C591BF5D43D241871569B3A222A72DD76DFA8A, Got: $($cert.Thumbprint)" } Write-Host "`n=== Verifying Certificate Import ===" From e21dd5c54ffb6e03c70b8cda4163ddc48271c7c4 Mon Sep 17 00:00:00 2001 From: Andrea Bianchi Date: Tue, 4 Nov 2025 11:36:24 +0000 Subject: [PATCH 15/26] fix up location --- .github/workflows/release.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 6d29c7d..17a61c5 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -104,6 +104,7 @@ jobs: - name: Debug - List available certificates and keys shell: pwsh + working-directory: ${{ github.workspace }} run: | Write-Host "=== GCP Project Information ===" Write-Host "GCP_PROJECT_ID secret value: ${{ secrets.GCP_PROJECT_ID }}" From ace5d499ee9c7a9cc545a5512a6e2a9bf0cdf99b Mon Sep 17 00:00:00 2001 From: Andrea Bianchi Date: Tue, 4 Nov 2025 11:46:55 +0000 Subject: [PATCH 16/26] use committed cert --- .github/signing_cert.cer | 44 +++++++++++++++++++++++++++++++++++ .github/workflows/release.yml | 23 +++++++++--------- 2 files changed, 56 insertions(+), 11 deletions(-) create mode 100644 .github/signing_cert.cer diff --git a/.github/signing_cert.cer b/.github/signing_cert.cer new file mode 100644 index 0000000..41b1ea9 --- /dev/null +++ b/.github/signing_cert.cer @@ -0,0 +1,44 @@ +-----BEGIN CERTIFICATE----- +MIIHWTCCBUGgAwIBAgIQCpHMILHwf3nyTsiLiesqxzANBgkqhkiG9w0BAQsFADBp +MQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xQTA/BgNVBAMT +OERpZ2lDZXJ0IFRydXN0ZWQgRzQgQ29kZSBTaWduaW5nIFJTQTQwOTYgU0hBMzg0 +IDIwMjEgQ0ExMB4XDTI1MTAxNzAwMDAwMFoXDTI4MTAxNjIzNTk1OVowYTELMAkG +A1UEBhMCR0IxDzANBgNVBAcTBkxvbmRvbjEZMBcGA1UEChMQUGF5bWVudHNlbnNl +IEx0ZDELMAkGA1UECxMCSVQxGTAXBgNVBAMTEFBheW1lbnRzZW5zZSBMdGQwggIi +MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCwt8TRu8c3YXVLlNdwEjjyyUGF +moknHdaBVrjqnclx1jioJLvcqkdRZZWn/gMF8wxfIng3guPy84O+R6BNqryB+osP +D2Q6m6Ns1nDjgKRRb9GtnBYH3CnPOgCMbrno52TSWIRbFh30aYyCcyq6nfwtOufG +4a/zBz1xyR+VBabOT2pZailIHnbPhy49Kr1gXhE0szzc0lUIcYb5emTSp9TXVma3 +5PXJ7WEPddFm8qr03JzmcDIE0GBlTQK67UD7wFq9l1iaF7n/ryvauA17QCDwSSwA +ov7LwMuU57iAHjEDw8aLggiR0xZf850QVMHPxnPWbgrmBNGXMhNO1Q/6j3g8j7ly +YSRxguAVL/zrmziJYLKNKeiLPOpP+5IBTQeoFlq9+GIXctxfuOTbahzzHr/aFeHd +BVhIKj7V5Jo98GjJ8FUX0MbmkTJicI7Bl65eLrxKFwODUtEA74FtDCAC2rUhOnXi +YXq8cp3+LLnJxF+bNRjgnOxcdqD3otZl7cGbnBcvbjEdguk4qdCOpRNRErSQmqjr +Cr1JOqeVdIAeexH27UhKkdSgow1LEkb8/eKglL9DERw2b0666ZwAN2kfERmkdCgF +DYxgYTDlPZVm38TYwAWn89nP3U4nnoRyj8l+jClyDFMvpf0nnD4DiuVdrCTeD6l9 +u79sxiJ2RU3sZyYL9QIDAQABo4ICAzCCAf8wHwYDVR0jBBgwFoAUaDfg67Y7+F8R +hvv+YXsIiGX0TkIwHQYDVR0OBBYEFIGihPIgnHqk5D1mCehD4lkvNNbyMD4GA1Ud +IAQ3MDUwMwYGZ4EMAQQBMCkwJwYIKwYBBQUHAgEWG2h0dHA6Ly93d3cuZGlnaWNl +cnQuY29tL0NQUzAOBgNVHQ8BAf8EBAMCB4AwEwYDVR0lBAwwCgYIKwYBBQUHAwMw +gbUGA1UdHwSBrTCBqjBToFGgT4ZNaHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0Rp +Z2lDZXJ0VHJ1c3RlZEc0Q29kZVNpZ25pbmdSU0E0MDk2U0hBMzg0MjAyMUNBMS5j +cmwwU6BRoE+GTWh0dHA6Ly9jcmw0LmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0 +ZWRHNENvZGVTaWduaW5nUlNBNDA5NlNIQTM4NDIwMjFDQTEuY3JsMIGUBggrBgEF +BQcBAQSBhzCBhDAkBggrBgEFBQcwAYYYaHR0cDovL29jc3AuZGlnaWNlcnQuY29t +MFwGCCsGAQUFBzAChlBodHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNl +cnRUcnVzdGVkRzRDb2RlU2lnbmluZ1JTQTQwOTZTSEEzODQyMDIxQ0ExLmNydDAJ +BgNVHRMEAjAAMA0GCSqGSIb3DQEBCwUAA4ICAQAYYvk6BPLFv5E5ZQz3cei56CDt +ZUnNXsoitiSg7oOdIYQiOK99rQZYKpuDTROCGG2aFOtiJhB8oewWxmRoknoMLoL6 +WZIjp1X5E2T6jK9Dy05YVLFGB+UhaINRHzoLhvv9dXpm2iBjkoN2Da3fCVyHNIrF +VMo+AV7o48vvmsKZNQ62zF9k0mrQJka+1OJ9H4xdMzy1zlEMkgkOXqLxvz1rR9PK +JpmMPVrmhaEhNiEwE0Wx+b8eXREd1N3yGEafdMwR7rIcvWT68y7cLgJx9iVS+znh +MnL1n/qTHxmeqcffiiJh7ybg9q+uwCEkThnQaikZMF7sHkJ5Eui8SRd2AK8HmkXp +bBPuLSvp24Upd6AErO5aMAkc8/wsWUNkSEzm0JhH+Ui4vYtztzRkCVHHZrw5dU+K +fJ2gIi3LwXwE+6CU3dA20fZc4RvEE+VwQ8zLs2c1rA3Qv2x/gexaSJdrVZGTEEvW +SLB/XsGYbIj5zyDZo33Iet5o6wfQWMGirDF7vKlqwafpBErLipDj805h7GGMx7pb +qVSKomSbUDZGaqQxNJ6xoyI38FHaZQ3RpkDvqXegbQRQwKcBhjuXfFGi5cdak6f9 +o6ysjcG01v1rJ/krQRw4Wsbfhi5FVWldAuonMrjdTgdJSQZ0exb45WS3/UuDmbDA +NpO1t9/kEbBOBnIrKw== +-----END CERTIFICATE----- + + diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 17a61c5..e8016ae 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -158,21 +158,13 @@ jobs: run: | Write-Host "=== Importing Public Certificate ===" - # Convert PEM to CER format using OpenSSL (comes with Git for Windows) - if (-not (Test-Path "public-key.pem")) { - Write-Host "ERROR: public-key.pem not found from previous step" - exit 1 - } - - Write-Host "Converting public-key.pem to DER format..." - & "C:\Program Files\Git\usr\bin\openssl.exe" x509 -in public-key.pem -inform PEM -out publicert.cer -outform DER + $certPath = ".github/signing_cert.cer" - if (-not (Test-Path "publicert.cer")) { - Write-Host "ERROR: Failed to convert PEM to CER" + if (-not (Test-Path $certPath)) { + Write-Host "ERROR: $certPath not found" exit 1 } - $certPath = "${{ github.workspace }}/publicert.cer" Write-Host "Importing certificate from $certPath" # Import to LocalMachine\My store @@ -198,6 +190,15 @@ jobs: Write-Host "✗ Certificate NOT found in certificate store" } + Write-Host "`n=== Verifying Certificate Import ===" + $thumbprint = "90C591BF5D43D241871569B3A222A72DD76DFA8A" + $cert = Get-ChildItem Cert:\LocalMachine\My\$thumbprint -ErrorAction SilentlyContinue + if ($cert) { + Write-Host "✓ Certificate found in LocalMachine\My store" + } else { + Write-Host "✗ Certificate NOT found in certificate store" + } + Write-Host "`n=== Listing Certificate Stores ===" Get-ChildItem Cert:\CurrentUser\My | Format-Table Subject, Thumbprint, NotAfter Get-ChildItem Cert:\LocalMachine\My | Format-Table Subject, Thumbprint, NotAfter From 7e901301603cf2306cd2d0030bf6d0f47c870ece Mon Sep 17 00:00:00 2001 From: Andrea Bianchi Date: Tue, 4 Nov 2025 12:52:56 +0000 Subject: [PATCH 17/26] try new signtool incantation --- .github/workflows/release.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e8016ae..e624e4f 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -234,8 +234,8 @@ jobs: $packages = Get-ChildItem -Recurse -Filter "*.nupkg" | Where-Object { $_.Name -notlike "*.symbols.nupkg" } foreach ($package in $packages) { Write-Host "Signing $($package.FullName)" - & $signtoolPath.FullName sign /v /fd SHA256 /tr http://timestamp.digicert.com /td SHA256 ` - /sha1 90C591BF5D43D241871569B3A222A72DD76DFA8A ` + & $signtoolPath.FullName sign /v /debug /fd SHA256 /tr http://timestamp.digicert.com /td SHA256 ` + /f .github/signing_cert.cer ` /csp "Google Cloud KMS Provider" ` /kc "projects/${{ secrets.GCP_PROJECT_ID }}/locations/europe-west2/keyRings/EVCodeSigningKeyRing/cryptoKeys/EVCodeSignDojo/cryptoKeyVersions/1" ` $package.FullName From 70abbab277f88388ef12a2299a5a7061045a92e0 Mon Sep 17 00:00:00 2001 From: Andrea Bianchi Date: Tue, 4 Nov 2025 12:56:28 +0000 Subject: [PATCH 18/26] use full patj for cert --- .github/workflows/release.yml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e624e4f..52f99a4 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -231,11 +231,18 @@ jobs: Write-Host "Using SignTool from: $($signtoolPath.FullName)" + $certPath = Join-Path $PWD ".github\signing_cert.cer" + Write-Host "Certificate path: $certPath" + if (-not (Test-Path $certPath)) { + Write-Error "Certificate file not found at $certPath" + exit 1 + } + $packages = Get-ChildItem -Recurse -Filter "*.nupkg" | Where-Object { $_.Name -notlike "*.symbols.nupkg" } foreach ($package in $packages) { Write-Host "Signing $($package.FullName)" & $signtoolPath.FullName sign /v /debug /fd SHA256 /tr http://timestamp.digicert.com /td SHA256 ` - /f .github/signing_cert.cer ` + /f $certPath ` /csp "Google Cloud KMS Provider" ` /kc "projects/${{ secrets.GCP_PROJECT_ID }}/locations/europe-west2/keyRings/EVCodeSigningKeyRing/cryptoKeys/EVCodeSignDojo/cryptoKeyVersions/1" ` $package.FullName From 15ed340c804cf2ebe6f50f1c2ba2b33e7858d892 Mon Sep 17 00:00:00 2001 From: Andrea Bianchi Date: Tue, 4 Nov 2025 13:00:00 +0000 Subject: [PATCH 19/26] fix path again --- .github/workflows/release.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 52f99a4..4feb30f 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -218,6 +218,8 @@ jobs: - name: Sign NuGet packages shell: pwsh + env: + WORKSPACE: ${{ github.workspace }} run: | # Find SignTool from Windows SDK $signtoolPath = Get-ChildItem -Path "C:\Program Files (x86)\Windows Kits\" -Recurse -Filter "signtool.exe" -ErrorAction SilentlyContinue | @@ -231,7 +233,7 @@ jobs: Write-Host "Using SignTool from: $($signtoolPath.FullName)" - $certPath = Join-Path $PWD ".github\signing_cert.cer" + $certPath = Join-Path $env:WORKSPACE ".github\signing_cert.cer" Write-Host "Certificate path: $certPath" if (-not (Test-Path $certPath)) { Write-Error "Certificate file not found at $certPath" From 70076981d67e0643457402e1695395e95da2af4f Mon Sep 17 00:00:00 2001 From: Andrea Bianchi Date: Tue, 4 Nov 2025 13:08:09 +0000 Subject: [PATCH 20/26] try using nuget sign instead as signtool does not seem to work for nuget pkgs --- .github/workflows/release.yml | 109 ++++++++++++++++++---------------- 1 file changed, 57 insertions(+), 52 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 4feb30f..b761506 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -152,60 +152,71 @@ jobs: --location europe-west2 ` --project ${{ secrets.GCP_PROJECT_ID }} 2>&1 - - name: Import certificate into Windows certificate store + - name: Import certificate and link to KMS CNG Provider shell: pwsh working-directory: ${{ github.workspace }} run: | - Write-Host "=== Importing Public Certificate ===" + Write-Host "=== Importing Certificate with KMS CNG Provider Link ===" $certPath = ".github/signing_cert.cer" + $kmsKeyPath = "projects/${{ secrets.GCP_PROJECT_ID }}/locations/europe-west2/keyRings/EVCodeSigningKeyRing/cryptoKeys/EVCodeSignDojo/cryptoKeyVersions/1" if (-not (Test-Path $certPath)) { Write-Host "ERROR: $certPath not found" exit 1 } - Write-Host "Importing certificate from $certPath" + # Import certificate and associate with KMS CNG Provider + # We need to use certutil to properly link the cert with the CNG provider + Write-Host "Importing certificate with CNG provider association..." + + # First, import the certificate + $cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2($certPath) + + # Create a certificate with private key reference to KMS + # The key container name is the KMS key path + $store = New-Object System.Security.Cryptography.X509Certificates.X509Store("My", "LocalMachine") + $store.Open("ReadWrite") + + # Check if cert already exists + $existing = $store.Certificates | Where-Object { $_.Thumbprint -eq $cert.Thumbprint } + if ($existing) { + Write-Host "Removing existing certificate..." + $store.Remove($existing) + } + + # Import the certificate + $store.Add($cert) + $store.Close() - # Import to LocalMachine\My store - $cert = Import-Certificate -FilePath $certPath -CertStoreLocation Cert:\LocalMachine\My Write-Host "Certificate imported successfully" Write-Host "Subject: $($cert.Subject)" Write-Host "Thumbprint: $($cert.Thumbprint)" Write-Host "NotAfter: $($cert.NotAfter)" - # Verify it matches our expected thumbprint - if ($cert.Thumbprint -eq "90C591BF5D43D241871569B3A222A72DD76DFA8A") { - Write-Host "✓ Certificate thumbprint matches!" - } else { - Write-Host "✗ WARNING: Thumbprint mismatch! Expected: 90C591BF5D43D241871569B3A222A72DD76DFA8A, Got: $($cert.Thumbprint)" - } + # Now link the certificate to the KMS CNG provider using certutil + Write-Host "`nLinking certificate to Google Cloud KMS Provider..." + Write-Host "KMS Key Path: $kmsKeyPath" - Write-Host "`n=== Verifying Certificate Import ===" - $thumbprint = "90C591BF5D43D241871569B3A222A72DD76DFA8A" - $cert = Get-ChildItem Cert:\LocalMachine\My\$thumbprint -ErrorAction SilentlyContinue - if ($cert) { - Write-Host "✓ Certificate found in LocalMachine\My store" - } else { - Write-Host "✗ Certificate NOT found in certificate store" + # Use certutil to set the private key property + $thumbprint = $cert.Thumbprint + + # This command links the certificate to the CNG provider + certutil -repairstore -csp "Google Cloud KMS Provider" My $thumbprint + + if ($LASTEXITCODE -ne 0) { + Write-Host "Warning: certutil repairstore returned exit code $LASTEXITCODE" } - Write-Host "`n=== Verifying Certificate Import ===" - $thumbprint = "90C591BF5D43D241871569B3A222A72DD76DFA8A" - $cert = Get-ChildItem Cert:\LocalMachine\My\$thumbprint -ErrorAction SilentlyContinue - if ($cert) { + Write-Host "`n=== Verifying Certificate ===" + $verifiedCert = Get-ChildItem Cert:\LocalMachine\My\$thumbprint -ErrorAction SilentlyContinue + if ($verifiedCert) { Write-Host "✓ Certificate found in LocalMachine\My store" + Write-Host "Has Private Key: $($verifiedCert.HasPrivateKey)" } else { Write-Host "✗ Certificate NOT found in certificate store" + exit 1 } - - Write-Host "`n=== Listing Certificate Stores ===" - Get-ChildItem Cert:\CurrentUser\My | Format-Table Subject, Thumbprint, NotAfter - Get-ChildItem Cert:\LocalMachine\My | Format-Table Subject, Thumbprint, NotAfter - - Write-Host "`n=== Checking for Google Cloud KMS Provider ===" - # Try to list keys using CNG provider - certutil -csp "Google Cloud KMS Provider" -key - name: Restore dependencies run: dotnet restore @@ -221,33 +232,27 @@ jobs: env: WORKSPACE: ${{ github.workspace }} run: | - # Find SignTool from Windows SDK - $signtoolPath = Get-ChildItem -Path "C:\Program Files (x86)\Windows Kits\" -Recurse -Filter "signtool.exe" -ErrorAction SilentlyContinue | - Where-Object { $_.FullName -like "*\x64\*" } | - Select-Object -First 1 - - if (-not $signtoolPath) { - Write-Error "Could not find signtool.exe" - exit 1 - } - - Write-Host "Using SignTool from: $($signtoolPath.FullName)" + # Use the certificate thumbprint to sign from the Windows certificate store + # The certificate has its private key backed by Google Cloud KMS via CNG Provider + $thumbprint = "90C591BF5D43D241871569B3A222A72DD76DFA8A" - $certPath = Join-Path $env:WORKSPACE ".github\signing_cert.cer" - Write-Host "Certificate path: $certPath" - if (-not (Test-Path $certPath)) { - Write-Error "Certificate file not found at $certPath" - exit 1 - } + Write-Host "Signing packages with certificate thumbprint: $thumbprint" $packages = Get-ChildItem -Recurse -Filter "*.nupkg" | Where-Object { $_.Name -notlike "*.symbols.nupkg" } foreach ($package in $packages) { Write-Host "Signing $($package.FullName)" - & $signtoolPath.FullName sign /v /debug /fd SHA256 /tr http://timestamp.digicert.com /td SHA256 ` - /f $certPath ` - /csp "Google Cloud KMS Provider" ` - /kc "projects/${{ secrets.GCP_PROJECT_ID }}/locations/europe-west2/keyRings/EVCodeSigningKeyRing/cryptoKeys/EVCodeSignDojo/cryptoKeyVersions/1" ` - $package.FullName + dotnet nuget sign $package.FullName ` + --certificate-fingerprint $thumbprint ` + --certificate-store-location LocalMachine ` + --certificate-store-name My ` + --timestamper "http://timestamp.digicert.com" ` + --hash-algorithm SHA256 ` + --verbosity detailed + + if ($LASTEXITCODE -ne 0) { + Write-Error "Failed to sign package: $($package.FullName)" + exit 1 + } } - name: Publish From 8585d34f1a2064d4e70791426f7193644dd0f9d3 Mon Sep 17 00:00:00 2001 From: Andrea Bianchi Date: Tue, 4 Nov 2025 14:08:29 +0000 Subject: [PATCH 21/26] try manually loading key from cng provider into store --- .github/workflows/release.yml | 77 ++++++++++++----------------------- 1 file changed, 26 insertions(+), 51 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index b761506..3aa69e4 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -152,71 +152,47 @@ jobs: --location europe-west2 ` --project ${{ secrets.GCP_PROJECT_ID }} 2>&1 - - name: Import certificate and link to KMS CNG Provider + - name: Link certificate to KMS CNG Provider key shell: pwsh working-directory: ${{ github.workspace }} run: | - Write-Host "=== Importing Certificate with KMS CNG Provider Link ===" + Write-Host "=== Linking Certificate to KMS Private Key ===" $certPath = ".github/signing_cert.cer" $kmsKeyPath = "projects/${{ secrets.GCP_PROJECT_ID }}/locations/europe-west2/keyRings/EVCodeSigningKeyRing/cryptoKeys/EVCodeSignDojo/cryptoKeyVersions/1" - if (-not (Test-Path $certPath)) { - Write-Host "ERROR: $certPath not found" - exit 1 - } + # Load the public certificate + $cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2($certPath) - # Import certificate and associate with KMS CNG Provider - # We need to use certutil to properly link the cert with the CNG provider - Write-Host "Importing certificate with CNG provider association..." + # Create a CNG key object that references the KMS key + $cngProvider = [System.Security.Cryptography.CngProvider]::new("Google Cloud KMS Provider") + $cngKey = [System.Security.Cryptography.CngKey]::Open($kmsKeyPath, $cngProvider) - # First, import the certificate - $cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2($certPath) + # Create an RSA object with the CNG key + $rsa = [System.Security.Cryptography.RSACng]::new($cngKey) + + # Create a new certificate that combines the public cert with the CNG private key + $certWithKey = [System.Security.Cryptography.X509Certificates.RSACertificateExtensions]::CopyWithPrivateKey($cert, $rsa) - # Create a certificate with private key reference to KMS - # The key container name is the KMS key path - $store = New-Object System.Security.Cryptography.X509Certificates.X509Store("My", "LocalMachine") + # Import this certificate with private key into the store + $store = [System.Security.Cryptography.X509Certificates.X509Store]::new("My", "LocalMachine") $store.Open("ReadWrite") - # Check if cert already exists + # Remove existing cert if present $existing = $store.Certificates | Where-Object { $_.Thumbprint -eq $cert.Thumbprint } if ($existing) { - Write-Host "Removing existing certificate..." $store.Remove($existing) } - # Import the certificate - $store.Add($cert) + $store.Add($certWithKey) $store.Close() - Write-Host "Certificate imported successfully" - Write-Host "Subject: $($cert.Subject)" - Write-Host "Thumbprint: $($cert.Thumbprint)" - Write-Host "NotAfter: $($cert.NotAfter)" + Write-Host "Certificate with KMS private key added to store" + Write-Host "Thumbprint: $($certWithKey.Thumbprint)" - # Now link the certificate to the KMS CNG provider using certutil - Write-Host "`nLinking certificate to Google Cloud KMS Provider..." - Write-Host "KMS Key Path: $kmsKeyPath" - - # Use certutil to set the private key property - $thumbprint = $cert.Thumbprint - - # This command links the certificate to the CNG provider - certutil -repairstore -csp "Google Cloud KMS Provider" My $thumbprint - - if ($LASTEXITCODE -ne 0) { - Write-Host "Warning: certutil repairstore returned exit code $LASTEXITCODE" - } - - Write-Host "`n=== Verifying Certificate ===" - $verifiedCert = Get-ChildItem Cert:\LocalMachine\My\$thumbprint -ErrorAction SilentlyContinue - if ($verifiedCert) { - Write-Host "✓ Certificate found in LocalMachine\My store" - Write-Host "Has Private Key: $($verifiedCert.HasPrivateKey)" - } else { - Write-Host "✗ Certificate NOT found in certificate store" - exit 1 - } + # Verify + $verifiedCert = Get-ChildItem Cert:\LocalMachine\My\$($cert.Thumbprint) + Write-Host "Has Private Key: $($verifiedCert.HasPrivateKey)" - name: Restore dependencies run: dotnet restore @@ -229,18 +205,15 @@ jobs: - name: Sign NuGet packages shell: pwsh - env: - WORKSPACE: ${{ github.workspace }} run: | - # Use the certificate thumbprint to sign from the Windows certificate store - # The certificate has its private key backed by Google Cloud KMS via CNG Provider $thumbprint = "90C591BF5D43D241871569B3A222A72DD76DFA8A" - Write-Host "Signing packages with certificate thumbprint: $thumbprint" - + # Find all .nupkg files (excluding symbols packages) $packages = Get-ChildItem -Recurse -Filter "*.nupkg" | Where-Object { $_.Name -notlike "*.symbols.nupkg" } + foreach ($package in $packages) { Write-Host "Signing $($package.FullName)" + dotnet nuget sign $package.FullName ` --certificate-fingerprint $thumbprint ` --certificate-store-location LocalMachine ` @@ -254,6 +227,8 @@ jobs: exit 1 } } + + Write-Host "All packages signed successfully" - name: Publish if: ${{ github.event_name == 'release' }} From 7a66013c4ed57dfa1d9ae62f2cb4c9ece517466e Mon Sep 17 00:00:00 2001 From: Andrea Bianchi Date: Tue, 4 Nov 2025 14:43:03 +0000 Subject: [PATCH 22/26] back to the ubuntu runner, try jsign --- .github/workflows/release.yml | 200 ++++++---------------------------- 1 file changed, 31 insertions(+), 169 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 3aa69e4..d59e480 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -13,7 +13,7 @@ jobs: BUILD_CONFIG: 'Release' PROJECT: 'Dojo.Net.csproj' - runs-on: windows-latest + runs-on: ubuntu-latest permissions: contents: 'read' @@ -43,193 +43,55 @@ jobs: - name: Setup Google Cloud SDK uses: google-github-actions/setup-gcloud@v3 - - name: Download and install Google Cloud KMS CNG Provider - shell: pwsh - working-directory: ${{ github.workspace }} + - name: Download Jsign run: | - # Download KMS CNG Provider v1.3 - $version = "1.3" - $downloadUrl = "https://github.com/GoogleCloudPlatform/kms-integrations/releases/download/cng-v$version/kmscng-$version-windows-amd64.zip" + version="7.4" + downloadUrl="https://github.com/ebourg/jsign/releases/download/$version/jsign-$version.jar" - Write-Host "Downloading KMS CNG Provider v$version from $downloadUrl" - Invoke-WebRequest -Uri $downloadUrl -OutFile "kmscng.zip" + echo "Downloading Jsign v$version from $downloadUrl" + wget -q $downloadUrl -O jsign.jar - # Extract the ZIP - Write-Host "Extracting KMS CNG Provider" - Expand-Archive -Path "kmscng.zip" -DestinationPath "kmscng" -Force - - # Install the MSI with full path - $msiFile = Resolve-Path "kmscng\kmscng-$version-windows-amd64\kmscng.msi" - Write-Host "Installing KMS CNG Provider from $msiFile" - - $installArgs = @( - '/i' - $msiFile.Path - '/quiet' - '/norestart' - '/l*v' - 'install.log' - ) - - $process = Start-Process msiexec.exe -ArgumentList $installArgs -Wait -NoNewWindow -PassThru - - if ($process.ExitCode -ne 0) { - Write-Host "Installation failed with exit code: $($process.ExitCode)" - if (Test-Path 'install.log') { - Write-Host "Installation log:" - Get-Content 'install.log' | Select-Object -Last 50 - } - exit 1 - } - - Write-Host "KMS CNG Provider v$version installed successfully" - - - name: Configure KMS CNG Provider - shell: pwsh - run: | - # Create the config directory - $configDir = "C:\Windows\KMSCNG" - New-Item -ItemType Directory -Force -Path $configDir | Out-Null - - # Create the config file - $configContent = @" - --- - resources: - - crypto_key_version: "projects/${{ secrets.GCP_PROJECT_ID }}/locations/europe-west2/keyRings/EVCodeSigningKeyRing/cryptoKeys/EVCodeSignDojo/cryptoKeyVersions/1" - "@ - - Set-Content -Path "$configDir\config.yaml" -Value $configContent - Write-Host "Configuration file created at $configDir\config.yaml" - Get-Content "$configDir\config.yaml" - - - name: Debug - List available certificates and keys - shell: pwsh - working-directory: ${{ github.workspace }} - run: | - Write-Host "=== GCP Project Information ===" - Write-Host "GCP_PROJECT_ID secret value: ${{ secrets.GCP_PROJECT_ID }}" - Write-Host "Getting project details..." - $projectInfo = gcloud projects describe ${{ secrets.GCP_PROJECT_ID }} --format=json | ConvertFrom-Json - Write-Host "Project ID (name): $($projectInfo.projectId)" - Write-Host "Project Number: $($projectInfo.projectNumber)" - - Write-Host "`n=== Checking KMS CNG Provider Configuration ===" - if (Test-Path "C:\Windows\KMSCNG\config.yaml") { - Write-Host "Config file exists:" - Get-Content "C:\Windows\KMSCNG\config.yaml" - } else { - Write-Host "WARNING: Config file not found!" - } - - Write-Host "`n=== Testing GCP Authentication ===" - gcloud auth list - - Write-Host "`n=== Testing KMS Key Access ===" - # Try to get the public key - gcloud kms keys versions get-public-key 1 ` - --key EVCodeSignDojo ` - --keyring EVCodeSigningKeyRing ` - --location europe-west2 ` - --project ${{ secrets.GCP_PROJECT_ID }} ` - --output-file public-key.pem 2>&1 - - if (Test-Path "public-key.pem") { - Write-Host "Successfully retrieved public key!" - Get-Content "public-key.pem" - } else { - Write-Host "Failed to retrieve public key" - } - - Write-Host "`n=== Listing accessible KMS keys ===" - gcloud kms keys list ` - --keyring EVCodeSigningKeyRing ` - --location europe-west2 ` - --project ${{ secrets.GCP_PROJECT_ID }} 2>&1 - - Write-Host "`n=== Checking IAM permissions on the key ===" - gcloud kms keys get-iam-policy EVCodeSignDojo ` - --keyring EVCodeSigningKeyRing ` - --location europe-west2 ` - --project ${{ secrets.GCP_PROJECT_ID }} 2>&1 - - - name: Link certificate to KMS CNG Provider key - shell: pwsh - working-directory: ${{ github.workspace }} - run: | - Write-Host "=== Linking Certificate to KMS Private Key ===" - - $certPath = ".github/signing_cert.cer" - $kmsKeyPath = "projects/${{ secrets.GCP_PROJECT_ID }}/locations/europe-west2/keyRings/EVCodeSigningKeyRing/cryptoKeys/EVCodeSignDojo/cryptoKeyVersions/1" - - # Load the public certificate - $cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2($certPath) - - # Create a CNG key object that references the KMS key - $cngProvider = [System.Security.Cryptography.CngProvider]::new("Google Cloud KMS Provider") - $cngKey = [System.Security.Cryptography.CngKey]::Open($kmsKeyPath, $cngProvider) - - # Create an RSA object with the CNG key - $rsa = [System.Security.Cryptography.RSACng]::new($cngKey) - - # Create a new certificate that combines the public cert with the CNG private key - $certWithKey = [System.Security.Cryptography.X509Certificates.RSACertificateExtensions]::CopyWithPrivateKey($cert, $rsa) - - # Import this certificate with private key into the store - $store = [System.Security.Cryptography.X509Certificates.X509Store]::new("My", "LocalMachine") - $store.Open("ReadWrite") - - # Remove existing cert if present - $existing = $store.Certificates | Where-Object { $_.Thumbprint -eq $cert.Thumbprint } - if ($existing) { - $store.Remove($existing) - } - - $store.Add($certWithKey) - $store.Close() - - Write-Host "Certificate with KMS private key added to store" - Write-Host "Thumbprint: $($certWithKey.Thumbprint)" - - # Verify - $verifiedCert = Get-ChildItem Cert:\LocalMachine\My\$($cert.Thumbprint) - Write-Host "Has Private Key: $($verifiedCert.HasPrivateKey)" + echo "Jsign downloaded successfully" - name: Restore dependencies run: dotnet restore - name: Build - run: dotnet build --configuration $env:BUILD_CONFIG -p:Version=${{ github.event.release.tag_name || '0.0.1-test' }} --no-restore /maxcpucount:1 + run: dotnet build --configuration $BUILD_CONFIG -p:Version=${{ github.event.release.tag_name || '0.0.1-test' }} --no-restore - name: Run tests - run: dotnet test /p:Configuration=$env:BUILD_CONFIG --no-restore --no-build --verbosity normal + run: dotnet test /p:Configuration=$BUILD_CONFIG --no-restore --no-build --verbosity normal - name: Sign NuGet packages - shell: pwsh run: | - $thumbprint = "90C591BF5D43D241871569B3A222A72DD76DFA8A" + certPath="${{ github.workspace }}/.github/signing_cert.cer" + kmsKeyPath="projects/${{ secrets.GCP_PROJECT_ID }}/locations/europe-west2/keyRings/EVCodeSigningKeyRing/cryptoKeys/EVCodeSignDojo/cryptoKeyVersions/1" + accessToken=$(gcloud auth print-access-token) - # Find all .nupkg files (excluding symbols packages) - $packages = Get-ChildItem -Recurse -Filter "*.nupkg" | Where-Object { $_.Name -notlike "*.symbols.nupkg" } + echo "Signing NuGet packages with Jsign + Google Cloud KMS" - foreach ($package in $packages) { - Write-Host "Signing $($package.FullName)" + # Find all .nupkg files (excluding symbols packages) + find . -name "*.nupkg" ! -name "*.symbols.nupkg" | while read package; do + echo "Signing $package" - dotnet nuget sign $package.FullName ` - --certificate-fingerprint $thumbprint ` - --certificate-store-location LocalMachine ` - --certificate-store-name My ` - --timestamper "http://timestamp.digicert.com" ` - --hash-algorithm SHA256 ` - --verbosity detailed + java -jar ${{ github.workspace }}/jsign.jar \ + --storetype GOOGLECLOUD \ + --keystore "projects/${{ secrets.GCP_PROJECT_ID }}/locations/europe-west2/keyRings/EVCodeSigningKeyRing" \ + --storepass "$accessToken" \ + --alias "EVCodeSignDojo/cryptoKeyVersions/1" \ + --certfile "$certPath" \ + --tsaurl "http://timestamp.digicert.com" \ + --tsmode RFC3161 \ + "$package" - if ($LASTEXITCODE -ne 0) { - Write-Error "Failed to sign package: $($package.FullName)" + if [ $? -ne 0 ]; then + echo "Failed to sign package: $package" exit 1 - } - } + fi + done - Write-Host "All packages signed successfully" + echo "All packages signed successfully" - name: Publish if: ${{ github.event_name == 'release' }} - run: dotnet nuget push **/*.nupkg --source 'https://api.nuget.org/v3/index.json' --api-key ${{secrets.NUGET_API_KEY}} --skip-duplicate + run: dotnet nuget push '**/*.nupkg' --source 'https://api.nuget.org/v3/index.json' --api-key ${{secrets.NUGET_API_KEY}} --skip-duplicate From 55b6735cd91e6219847428ac471d9a0f6fbf2bf5 Mon Sep 17 00:00:00 2001 From: Andrea Bianchi Date: Tue, 4 Nov 2025 14:51:28 +0000 Subject: [PATCH 23/26] fix jar path --- .github/workflows/release.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index d59e480..bd2fe18 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -65,6 +65,7 @@ jobs: - name: Sign NuGet packages run: | certPath="${{ github.workspace }}/.github/signing_cert.cer" + jsignJar="${{ github.workspace }}/jsign.jar" kmsKeyPath="projects/${{ secrets.GCP_PROJECT_ID }}/locations/europe-west2/keyRings/EVCodeSigningKeyRing/cryptoKeys/EVCodeSignDojo/cryptoKeyVersions/1" accessToken=$(gcloud auth print-access-token) @@ -74,7 +75,7 @@ jobs: find . -name "*.nupkg" ! -name "*.symbols.nupkg" | while read package; do echo "Signing $package" - java -jar ${{ github.workspace }}/jsign.jar \ + java -jar "$jsignJar" \ --storetype GOOGLECLOUD \ --keystore "projects/${{ secrets.GCP_PROJECT_ID }}/locations/europe-west2/keyRings/EVCodeSigningKeyRing" \ --storepass "$accessToken" \ From 740739abd4aaa300ace2576106d5419bd087e2f8 Mon Sep 17 00:00:00 2001 From: Andrea Bianchi Date: Tue, 4 Nov 2025 14:54:18 +0000 Subject: [PATCH 24/26] fix jar path again --- .github/workflows/release.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index bd2fe18..5e4d026 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -44,6 +44,7 @@ jobs: uses: google-github-actions/setup-gcloud@v3 - name: Download Jsign + working-directory: ${{ github.workspace }} run: | version="7.4" downloadUrl="https://github.com/ebourg/jsign/releases/download/$version/jsign-$version.jar" From a4490d532819090875166cd06e851539e75e3267 Mon Sep 17 00:00:00 2001 From: Andrea Bianchi Date: Tue, 4 Nov 2025 15:01:38 +0000 Subject: [PATCH 25/26] remove on branch --- .github/workflows/release.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 5e4d026..e26692d 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -3,9 +3,6 @@ name: Publish Nuget on: release: types: [published] - pull_request: - branches: - - main jobs: build: From bfa32807def5a21f8b4688b66bfd04aaa450a680 Mon Sep 17 00:00:00 2001 From: Andrea Bianchi Date: Tue, 4 Nov 2025 15:08:57 +0000 Subject: [PATCH 26/26] update codeowners --- CODEOWNERS | 1 + 1 file changed, 1 insertion(+) diff --git a/CODEOWNERS b/CODEOWNERS index 21da9a0..772a8f4 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -1 +1,2 @@ * @Dojo-Engineering/remote-payments +* @Dojo-Engineering/core_payments-integration_platform \ No newline at end of file