diff --git a/.gitea/workflows/release.yaml b/.gitea/workflows/release.yaml index baa09ac..e6b0f48 100644 --- a/.gitea/workflows/release.yaml +++ b/.gitea/workflows/release.yaml @@ -37,25 +37,176 @@ jobs: $issContent = $issContent -replace '#define MyAppVersion ".*"', "#define MyAppVersion `"$version`"" [System.IO.File]::WriteAllText($issPath, $issContent, [System.Text.UTF8Encoding]::new($true)) + - name: Connect SimplySign (automated TOTP) + if: ${{ secrets.CERTUM_OTP_URI != '' }} + shell: pwsh + run: | + # Generate TOTP from otpauth:// URI and authenticate SimplySign Desktop + $otpUri = $env:CERTUM_OTP_URI + $userId = $env:CERTUM_USERID + + # Parse the otpauth URI to extract the secret + if ($otpUri -match 'secret=([A-Z2-7=]+)') { + $base32Secret = $Matches[1] + } else { + Write-Error "Failed to parse OTP secret from URI" + exit 1 + } + + # Base32 decode and generate TOTP + Add-Type -TypeDefinition @" + using System; + using System.Security.Cryptography; + public static class TOTP { + public static string Generate(string base32Secret) { + var alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"; + var bits = ""; + foreach (var c in base32Secret.TrimEnd('=').ToUpper()) { + bits += Convert.ToString(Array.IndexOf(alphabet.ToCharArray(), c), 2).PadLeft(5, '0'); + } + var key = new byte[bits.Length / 8]; + for (int i = 0; i < key.Length; i++) { + key[i] = Convert.ToByte(bits.Substring(i * 8, 8), 2); + } + + long counter = (long)(DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalSeconds / 30; + var counterBytes = BitConverter.GetBytes(counter); + if (BitConverter.IsLittleEndian) Array.Reverse(counterBytes); + + using (var hmac = new HMACSHA1(key)) { + var hash = hmac.ComputeHash(counterBytes); + int offset = hash[hash.Length - 1] & 0x0F; + int code = ((hash[offset] & 0x7F) << 24) + | ((hash[offset + 1] & 0xFF) << 16) + | ((hash[offset + 2] & 0xFF) << 8) + | (hash[offset + 3] & 0xFF); + return (code % 1000000).ToString("D6"); + } + } + } + "@ + + $totp = [TOTP]::Generate($base32Secret) + Write-Host "TOTP generated successfully" + + # Find and launch SimplySign + $exePath = $env:CERTUM_EXE_PATH + if (-not $exePath) { + $exePath = "$env:LOCALAPPDATA\Programs\SimplySign Desktop\SimplySign Desktop.exe" + } + if (-not (Test-Path $exePath)) { + $exePath = "C:\Program Files\SimplySign Desktop\SimplySign Desktop.exe" + } + if (-not (Test-Path $exePath)) { + Write-Error "SimplySign Desktop not found" + exit 1 + } + + # Start SimplySign if not running + $process = Get-Process -Name "SimplySign Desktop" -ErrorAction SilentlyContinue + if (-not $process) { + Start-Process $exePath + Start-Sleep -Seconds 5 + } + + # Send credentials via SendKeys + Add-Type -AssemblyName System.Windows.Forms + $wshell = New-Object -ComObject WScript.Shell + + # Activate SimplySign window + Start-Sleep -Seconds 2 + $wshell.AppActivate("SimplySign") | Out-Null + Start-Sleep -Seconds 1 + + # Enter user ID + [System.Windows.Forms.SendKeys]::SendWait($userId) + [System.Windows.Forms.SendKeys]::SendWait("{TAB}") + Start-Sleep -Milliseconds 500 + + # Enter TOTP + [System.Windows.Forms.SendKeys]::SendWait($totp) + [System.Windows.Forms.SendKeys]::SendWait("{ENTER}") + + # Wait for authentication + Write-Host "Waiting for SimplySign authentication..." + Start-Sleep -Seconds 10 + Write-Host "SimplySign authentication completed" + env: + CERTUM_OTP_URI: ${{ secrets.CERTUM_OTP_URI }} + CERTUM_USERID: ${{ secrets.CERTUM_USERID }} + CERTUM_EXE_PATH: ${{ secrets.CERTUM_EXE_PATH }} + + - name: Verify code signing certificate + shell: pwsh + run: | + $certName = "${{ secrets.CERTUM_CERT_NAME }}" + if (-not $certName) { + Write-Host "CERTUM_CERT_NAME not set, skipping signing verification" + exit 0 + } + + Write-Host "Looking for certificate: $certName" + Write-Host "" + + Write-Host "=== CurrentUser\My code signing certs ===" + Get-ChildItem -Path Cert:\CurrentUser\My -CodeSigningCert -ErrorAction SilentlyContinue | ForEach-Object { + Write-Host " Subject: $($_.Subject)" + Write-Host " Issuer: $($_.Issuer)" + Write-Host " Thumbprint: $($_.Thumbprint)" + Write-Host "" + } + + Write-Host "=== Checking via certutil (includes smart card / CSP certs) ===" + certutil -user -store My 2>&1 | Select-String -Pattern "Subject:|Cert Hash|Serial Number|Provider" | ForEach-Object { Write-Host " $_" } + + Write-Host "" + Write-Host "=== SimplySign process check ===" + $ss = Get-Process -Name "SimplySign*" -ErrorAction SilentlyContinue + if ($ss) { + $ss | ForEach-Object { Write-Host " Running: $($_.ProcessName) (PID: $($_.Id))" } + } else { + Write-Host " WARNING: SimplySign Desktop is not running" + } + + Write-Host "" + Write-Host "Certificate verification complete." + - name: Restore dependencies run: dotnet restore - name: Build Release run: dotnet build --configuration Release --no-restore - - name: List build output - shell: cmd + - name: Sign application exe + if: ${{ secrets.CERTUM_CERT_NAME != '' }} + shell: pwsh run: | - echo "Current directory:" - cd - echo "MonitorControl contents:" - dir MonitorControl - echo "MonitorControl\bin contents:" - dir MonitorControl\bin - echo "MonitorControl\bin\Release contents:" - dir MonitorControl\bin\Release - echo "MonitorControl\bin\Release\net9.0-windows contents:" - dir MonitorControl\bin\Release\net9.0-windows + $certName = "${{ secrets.CERTUM_CERT_NAME }}" + $exe = "MonitorControl\bin\Release\net9.0-windows\MonitorControl.exe" + + if (-not (Test-Path $exe)) { + Write-Error "Build output not found: $exe" + exit 1 + } + + # Find signtool.exe from Windows SDK + $signtool = "signtool" + $sdkBase = "C:\Program Files (x86)\Windows Kits\10\bin" + if (Test-Path $sdkBase) { + $versions = Get-ChildItem $sdkBase -Directory | Where-Object { $_.Name -match '^\d+\.' } | Sort-Object Name -Descending + foreach ($v in $versions) { + $candidate = Join-Path $v.FullName "x64\signtool.exe" + if (Test-Path $candidate) { + $signtool = $candidate + break + } + } + } + + Write-Host "Signing $exe with signtool..." + & $signtool sign /debug /n "$certName" /t http://time.certum.pl /fd SHA256 "$exe" + if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } + Write-Host "Application exe signed successfully" - name: Build Installer shell: cmd @@ -82,6 +233,38 @@ jobs: echo Installer directory contents: dir installer + - name: Sign installer exe + if: ${{ secrets.CERTUM_CERT_NAME != '' }} + shell: pwsh + run: | + $certName = "${{ secrets.CERTUM_CERT_NAME }}" + $version = "${{ gitea.ref_name }}".TrimStart("v") + $installer = "C:\build\app\installer\MonitorControl-Setup-$version.exe" + + if (-not (Test-Path $installer)) { + Write-Error "Installer not found: $installer" + exit 1 + } + + # Find signtool.exe from Windows SDK + $signtool = "signtool" + $sdkBase = "C:\Program Files (x86)\Windows Kits\10\bin" + if (Test-Path $sdkBase) { + $versions = Get-ChildItem $sdkBase -Directory | Where-Object { $_.Name -match '^\d+\.' } | Sort-Object Name -Descending + foreach ($v in $versions) { + $candidate = Join-Path $v.FullName "x64\signtool.exe" + if (Test-Path $candidate) { + $signtool = $candidate + break + } + } + } + + Write-Host "Signing $installer with signtool..." + & $signtool sign /debug /n "$certName" /t http://time.certum.pl /fd SHA256 "$installer" + if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } + Write-Host "Installer signed successfully" + - name: Create Portable Zip shell: powershell run: |