diff --git a/RemoteApplicationPublisher.sln b/RemoteApplicationPublisher.sln index 7bb86f1..9fdf7e6 100644 --- a/RemoteApplicationPublisher.sln +++ b/RemoteApplicationPublisher.sln @@ -1,29 +1,49 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -VisualStudioVersion = 17.3.32901.215 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RemoteApplicationPublisher", "RemoteApplicationPublisher\RemoteApplicationPublisher.csproj", "{E23CF4FE-4FA6-4554-8A06-D741D336DB0A}" -EndProject -Project("{930C7802-8A8C-48F9-8165-68863BCCD9DD}") = "RemoteApplicationPublisherSetup", "RemoteApplicationPublisherSetup\RemoteApplicationPublisherSetup.wixproj", "{5EEA7966-CB7D-4FE5-8B6E-7B416A4C4EB0}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|x64 = Debug|x64 - Release|x64 = Release|x64 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {E23CF4FE-4FA6-4554-8A06-D741D336DB0A}.Debug|x64.ActiveCfg = Debug|x64 - {E23CF4FE-4FA6-4554-8A06-D741D336DB0A}.Debug|x64.Build.0 = Debug|x64 - {E23CF4FE-4FA6-4554-8A06-D741D336DB0A}.Release|x64.ActiveCfg = Release|x64 - {E23CF4FE-4FA6-4554-8A06-D741D336DB0A}.Release|x64.Build.0 = Release|x64 - {5EEA7966-CB7D-4FE5-8B6E-7B416A4C4EB0}.Debug|x64.ActiveCfg = Debug|x64 - {5EEA7966-CB7D-4FE5-8B6E-7B416A4C4EB0}.Release|x64.ActiveCfg = Release|x64 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {79F83E7A-73DC-41C1-AE1D-09325ED9C440} - EndGlobalSection -EndGlobal + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.3.32901.215 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RemoteApplicationPublisher", "RemoteApplicationPublisher\RemoteApplicationPublisher.csproj", "{E23CF4FE-4FA6-4554-8A06-D741D336DB0A}" +EndProject +Project("{930C7802-8A8C-48F9-8165-68863BCCD9DD}") = "RemoteApplicationPublisherSetup", "RemoteApplicationPublisherSetup\RemoteApplicationPublisherSetup.wixproj", "{5EEA7966-CB7D-4FE5-8B6E-7B416A4C4EB0}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Images", "Images", "{02EA681E-C7D8-13C7-8484-4AC65E1B71E8}" + ProjectSection(SolutionItems) = preProject + Images\RAPAppConfigurationSPP-SPS.PNG = Images\RAPAppConfigurationSPP-SPS.PNG + Images\RAPAppConfigureScreen.PNG = Images\RAPAppConfigureScreen.PNG + Images\RAPCopyToClipboard.PNG = Images\RAPCopyToClipboard.PNG + Images\RAPHostOptionsDialog.PNG = Images\RAPHostOptionsDialog.PNG + Images\RAPMainScreen.PNG = Images\RAPMainScreen.PNG + Images\SPPAutoLoginConfiguration.PNG = Images\SPPAutoLoginConfiguration.PNG + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SolutionItems", "SolutionItems", "{3817A582-03B8-4262-8E95-ED69C42042CF}" + ProjectSection(SolutionItems) = preProject + azure-pipelines.yml = azure-pipelines.yml + code-signing.yml = code-signing.yml + LICENSE = LICENSE + LICENSE.rtf = LICENSE.rtf + README.md = README.md + versionnumber.ps1 = versionnumber.ps1 + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {E23CF4FE-4FA6-4554-8A06-D741D336DB0A}.Debug|x64.ActiveCfg = Debug|x64 + {E23CF4FE-4FA6-4554-8A06-D741D336DB0A}.Debug|x64.Build.0 = Debug|x64 + {E23CF4FE-4FA6-4554-8A06-D741D336DB0A}.Release|x64.ActiveCfg = Release|x64 + {E23CF4FE-4FA6-4554-8A06-D741D336DB0A}.Release|x64.Build.0 = Release|x64 + {5EEA7966-CB7D-4FE5-8B6E-7B416A4C4EB0}.Debug|x64.ActiveCfg = Debug|x64 + {5EEA7966-CB7D-4FE5-8B6E-7B416A4C4EB0}.Release|x64.ActiveCfg = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {79F83E7A-73DC-41C1-AE1D-09325ED9C440} + EndGlobalSection +EndGlobal diff --git a/RemoteApplicationPublisherSetup/RemoteApplicationPublisherSetup.wixproj b/RemoteApplicationPublisherSetup/RemoteApplicationPublisherSetup.wixproj index 7fba366..9b30a91 100644 --- a/RemoteApplicationPublisherSetup/RemoteApplicationPublisherSetup.wixproj +++ b/RemoteApplicationPublisherSetup/RemoteApplicationPublisherSetup.wixproj @@ -24,9 +24,6 @@ SourceDir=bin\$(Configuration)\publish ICE61;ICE63;ICE64 - - C:\Program Files (x86)\Microsoft SDKs\ClickOnce\SignTool\signtool.exe - @@ -65,8 +62,7 @@ - - + @@ -77,7 +73,7 @@ - + diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 9d8a92e..4003587 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -21,8 +21,7 @@ variables: isReleaseBranch: $[ or( eq(variables['Build.SourceBranch'], 'refs/heads/main'), startsWith(variables['Build.SourceBranch'], 'refs/heads/release-') ) ] setupProjectDir: 'RemoteApplicationPublisherSetup' setupProject: '**/$(setupProjectDir)/*.wixproj' - codeSigningCertFileName: 'OneIdentityCodeSigning.pfx' - signingToolPath: 'C:\Program Files (x86)\Windows Kits\10\bin\10.0.18362.0\x64' + signingToolPath: 'C:\Program Files (x86)\Microsoft SDKs\ClickOnce\SignTool\signtool.exe' steps: - task: Bash@3 @@ -38,61 +37,25 @@ steps: arguments: $(Build.SourcesDirectory) $(semanticVersion) $(Build.BuildId) $$(isPrerelease) displayName: 'Setting build version' -- task: NuGetToolInstaller@1 +- template: code-signing.yml -- task: NuGetCommand@2 +# Explicitly install .NET 6 SDK because that is what the project is targeting. Newer build agents don't have it installed. +- task: UseDotNet@2 inputs: - restoreSolution: '$(solution)' - -- task: AzureKeyVault@1 - inputs: - azureSubscription: 'Azure.Infrastructure.CodeSigning' - KeyVaultName: 'CodeSigningCertificates' - SecretsFilter: '*' - displayName: 'Get code signing certificate from Azure Key Vault' - condition: and(succeeded(), eq(variables.isReleaseBranch, true)) - -- powershell: | - $kvSecretBytes = [System.Convert]::FromBase64String("$(OneIdentity-CodeSigning)") - $certCollection = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2Collection - $certCollection.Import($kvSecretBytes,$null,[System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::Exportable) - $protectedCertificateBytes = $certCollection.Export([System.Security.Cryptography.X509Certificates.X509ContentType]::Pkcs12,"$(OneIdentity-CodeSigningCertPassword)") - $certpath = '$(Build.BinariesDirectory)\$(codeSigningCertFileName)' - Write-Verbose -Verbose $certpath - [System.IO.File]::WriteAllBytes($certpath, $protectedCertificateBytes) - displayName: 'Save code signing certificate to PFX file' - condition: and(succeeded(), eq(variables.isReleaseBranch, true)) - -- task: VSBuild@1 - inputs: - solution: '$(solution)' - platform: '$(buildPlatform)' - configuration: '$(buildConfiguration)' - displayName: 'Build $(solution)' + version: 6.x + displayName: Explicitly Install .NET 6 SDK +# The setup project effectively has a "pre-build" event that will call dotnet publish on the main project +# to compile it before compiling the setup project itself. There is also a call to sign the output assemblies +# of the main project after it builds. Finally, there is an "after build" event that signs the MSI of the +# setup project. - task: VSBuild@1 inputs: solution: '$(setupProject)' - platform: '$(buildPlatform)' - configuration: '$(buildConfiguration)' - displayName: 'Build $(setupProject) no signing' - condition: and(succeeded(), eq(variables.isReleaseBranch, false)) - -- task: VSBuild@1 - inputs: - solution: '$(setupProject)' - msbuildArgs: '/p:SignFiles=true /p:CertificatePassword=$(OneIdentity-CodeSigningCertPassword) /p:CertificatePath="$(Build.BinariesDirectory)\$(codeSigningCertFileName)" ' + msbuildArgs: '/p:SignFiles=true /p:SignToolPath="$(signingToolPath)"' platform: '$(buildPlatform)' configuration: '$(buildConfiguration)' displayName: 'Build $(setupProject) with signing' - condition: and(succeeded(), eq(variables.isReleaseBranch, true)) - -- task: DeleteFiles@1 - inputs: - SourceFolder: '$(Build.BinariesDirectory)' - Contents: '$(codeSigningCertFileName)' - condition: succeededOrFailed() - displayName: 'Delete code signing certificate files' - task: ArchiveFiles@2 inputs: diff --git a/code-signing.yml b/code-signing.yml new file mode 100644 index 0000000..f466ef0 --- /dev/null +++ b/code-signing.yml @@ -0,0 +1,46 @@ +steps: +- task: AzureKeyVault@2 + displayName: 'Get code signing certificate from Azure Key Vault' + inputs: + # The "Common" project in Azure has been setup with a new Connected Service under Project Settings > Service Connections. + # We may not have permissions to view them, but they are there. And this service connection should then have access to + # the SPPCodeSigning Key Vault under the OneIdentity.Ops.SaaS.AzureDevOpsInfrastructure subscription, in the CodeSigningCertificates + # resource group. + ConnectedServiceName: "OneIdentity.Infrastructure.SPPCodeSigning" + KeyVaultName: "SPPCodeSigning" + SecretsFilter: "SPPCodeSigning-Password, SPPCodeSigning-TotpPrivateKey" + +# SSL.com example: +# https://www.ssl.com/how-to/how-to-integrate-esigner-cka-with-ci-cd-tools-for-automated-code-signing/#ftoc-heading-1 +# and click on the Azure Pipeline tab. +- powershell: | + # Download and unzip eSignerCKA setup. This downloads their latest version, which when unzipped has + # a file name that also contains the version number. So we need to move it to a known name. + Invoke-WebRequest -OutFile eSigner_CKA_Setup.zip "https://www.ssl.com/download/ssl-com-esigner-cka" + Expand-Archive -Force eSigner_CKA_Setup.zip + Remove-Item eSigner_CKA_Setup.zip + Move-Item -Path "eSigner_CKA_*\*.exe" -Destination "eSigner_CKA_Installer.exe" + displayName: "Download and Unzip eSignerCKA Setup" + +- powershell: | + .\eSigner_CKA_Installer.exe /CURRENTUSER /VERYSILENT /SUPPRESSMSGBOXES /DIR="$(Build.SourcesDirectory)\eSignerCKA" | Out-Null + dir $(Build.SourcesDirectory)\eSignerCKA + displayName: "Setup eSignerCKA in silent mode and output installation directory" + +- powershell: | + $(Build.SourcesDirectory)\eSignerCKA\eSignerCKATool.exe config -mode "product" -user "ssl.oid.safeguardpp@groups.quest.com" -pass "$(SPPCodeSigning-Password)" -totp "$(SPPCodeSigning-TotpPrivateKey)" -key "$(Build.SourcesDirectory)\eSignerCKA\master.key" -r + displayName: "Configure account information on eSignerCKA using Azure Key Vault values" + +- powershell: | + $(Build.SourcesDirectory)\eSignerCKA\eSignerCKATool.exe unload + $(Build.SourcesDirectory)\eSignerCKA\eSignerCKATool.exe load + displayName: "Unload and load certificate into Windows Certificate Store" + +# We should now be able to access the certificate using the standard Windows signtool.exe from the Windows SDK, +# which should be installed on the build agent images being used. +# +# Typically, you often see examples of signtool.exe and other things accessing the certificate by the thumbprint. +# And in fact, the sample SSL.com code includes a bunch of extra PowerShell script to get the thumbprint. But the +# signtool.exe can take a /n parameter to specify the name of the certificate. So we can use that instead. And +# hopefully that won't change when we renew the certificate. +# "signtool.exe" sign /fd sha256 /tr http://ts.ssl.com /td sha256 /n "One Identity LLC" "C:\path\to\program.exe" \ No newline at end of file