Open DanielGoehler opened 9 months ago
We have a GlobalSign Standard Code Signing with HSM (Hardware Secure Module). DigiCert Code Signing with HSM is also available. Both are supported by Azure Key Vault [1]. Note:
We order through our current certificate provider for all certificates, PSW Group, and we had to contact support after ordering so they could get us the HSM option from GlobalSign. Verification as usual. You will also get your private key signed as before, the only difference is that you will create your private key directly in the Azure Key Vault [2]. To meet the FIPS 140-2 Level 2 HSM requirement, you will need to create an Azure Key Vault Premium [3]. I had to sign a document confirming this and also that I have the qualifications in information security and risk management, network security and physical security. Currently the costs for Azure Key Vault Standard and Premium are the same as for our location Germany West Central [4], but HSM-protected keys RSA 4096-bit cost €4.503 per key per month + €0.136/10,000 transactions. (The difference to Standard is only the €4.503 per key per month).
I then created an app registration with a secret [5] and enabled access to the Azure Key Vault [6].
I used Freddy's AL Go for Github action to sign our AppSource apps for now (https://github.com/microsoft/AL-Go/tree/main/Actions/Sign).
Our release pipeline looks now like this:
[...]
- task: ALOpsAppCompiler@2
displayName: 'App Compiler'
inputs:
artifactversion: '$(artifactversion)'
artifacttype: '$(artifacttype)'
artifactcountry: '$(artifactcountry)'
alsourcepath: '$(System.DefaultWorkingDirectory)/MainApp'
appversiontemplate: '$(appversionno)'
appfilenametemplate: '%APP_PUBLISHER%_%APP_NAME%_%APP_VERSION%.app' # Template for App filename.
alcodeanalyzer: 'AppSourceCop,CodeCop,UICop'
publishartifact: false
#- task: ALOpsAppSign@1
# displayName: 'App Sign'
# inputs:
# usedocker: false
# artifact_path: '$(ALOPS_COMPILE_ARTIFACT)'
# pfx_path: '\\files\DevOps\Common\CodeSigning\CodeSigning.pfx'
# pfx_password: '$(CodeSigningPasscode)'
# timestamp_uri: 'http://timestamp.digicert.com/'
- template: templates/SignApp.yml
parameters:
artifact_path: '$(ALOPS_COMPILE_ARTIFACT)'
client_id: '$(CodeSigningClientId)'
tenant_id: '$(TenantID)'
client_secret: '$(CodeSigningSecret)'
vault_name: '$(CodeSigningVaultName)'
certificate: '$(CodeSigningCertificate)'
timestamp_uri: 'http://rfc3161timestamp.globalsign.com/advanced'
- task: PublishBuildArtifacts@1
displayName: 'Publish Artifact'
inputs:
PathtoPublish: '$(ALOPS_COMPILE_ARTIFACT)'
ArtifactName: 'Dynamics 365'
[...]
Instead of the ALOpsAppSign task, I use templates/SignApp.yml and PublishPipelineArtifact.
parameters:
artifact_path: ''
client_id: ''
tenant_id: ''
client_secret: ''
vault_name: ''
certificate: ''
file_digest: 'sha256'
timestamp_uri: 'http://timestamp.digicert.com'
steps:
- task: PowerShell@2
displayName: 'Install AzureSignTool'
inputs:
targetType: 'inline'
script: |
dotnet nuget add source -n nuget.org https://api.nuget.org/v3/index.json
dotnet tool install AzureSignTool --global --version 4.0.1
ignoreLASTEXITCODE: true
- task: PowerShell@2
displayName: 'Sign App'
inputs:
targetType: 'inline'
script: |
cd (Join-Path $env:userprofile .dotnet/tools)
./AzureSignTool sign --file-digest ${{parameters.file_digest}} `
--azure-key-vault-url "https://${{parameters.vault_name}}.vault.azure.net/" `
--azure-key-vault-client-id ${{parameters.client_id}} `
--azure-key-vault-tenant-id ${{parameters.tenant_id}} `
--azure-key-vault-client-secret ${{parameters.client_secret}} `
--azure-key-vault-certificate ${{parameters.certificate}} `
--timestamp-rfc3161 "${{parameters.timestamp_uri}}" `
--timestamp-digest ${{parameters.file_digest}} `
"${{parameters.artifact_path}}"
To get AzureSignTool up and running, I installed the .NET 6.0 SDK on each build agent.
$tempFile = Join-Path $env:TEMP "dotnet-win6.exe"
Invoke-WebRequest -Uri "https://download.visualstudio.microsoft.com/download/pr/9b8baa92-04f4-4b1a-8ccd-aa6bf31592bc/3a25c73326e060e04c119264ba58d0d5/dotnet-sdk-6.0.418-win-x64.exe" `
-UseBasicParsing `
-OutFile "$tempFile"
Start-Process -Wait -FilePath "$tempFile" -ArgumentList /quiet
Remove-Item "$tempFile" -Confirm:$false -Force:$true
$Hosts = @("BCBUILD1", "BCBUILD2", "BCBUILD3", "BCBUILD4", "BCBUILD5", "BCBUILD6", "BCBUILD7", "BCBUILD8")
$Hosts | ForEach-Object {;
Write-Host $PSItem
Invoke-Command -ComputerName $PSItem -Authentication NegotiateWithImplicitCredential -ScriptBlock {
Write-Host $PSItem
$tempFile = Join-Path $env:TEMP "dotnet-win6.exe"
Invoke-WebRequest -Uri "https://download.visualstudio.microsoft.com/download/pr/9b8baa92-04f4-4b1a-8ccd-aa6bf31592bc/3a25c73326e060e04c119264ba58d0d5/dotnet-sdk-6.0.418-win-x64.exe" `
-UseBasicParsing `
-OutFile "$tempFile"
Start-Process -Wait -FilePath "$tempFile" -ArgumentList /quiet
Remove-Item "$tempFile" -Confirm:$false -Force:$true
}
}
Optimisations would also be possible. If you check to see if the .NET 6.0 SDK is installed, it could be part of the pipeline.
dotnet nuget add source -n nuget.org https://api.nuget.org/v3/index.json
is only needed once.
dotnet tool install AzureSignTool --global --version 4.0.1
is also only needed if %userprofile%.dotnet\tools\AzureSignTool.exe is missing. I haven't looked into why --global doesn't work.
I haven't had this problem with our existing Build Agents, but if you get the error
The file cannot be signed because it is not a recognized file type for signing or it is corrupt.
the SignTool.exe under the hood needs navsip.dll installed on the system. [7]
You can do this by copying the functions from https://github.com/microsoft/AL-Go/blob/main/Actions/Sign/Sign.psm1 to a PowerShell console and running Register-NavSip.
function GetNavSipFromArtifacts
(
[string] $NavSipDestination
)
{
$artifactTempFolder = Join-Path $([System.IO.Path]::GetTempPath()) ([System.IO.Path]::GetRandomFileName())
try {
Download-Artifacts -artifactUrl (Get-BCArtifactUrl -type Sandbox -country core) -basePath $artifactTempFolder | Out-Null
Write-Host "Downloaded artifacts to $artifactTempFolder"
$navsip = Get-ChildItem -Path $artifactTempFolder -Filter "navsip.dll" -Recurse
Write-Host "Found navsip at $($navsip.FullName)"
Copy-Item -Path $navsip.FullName -Destination $NavSipDestination -Force | Out-Null
Write-Host "Copied navsip to $NavSipDestination"
}
finally {
Remove-Item -Path $artifactTempFolder -Recurse -Force
}
}
function Register-NavSip() {
$navSipDestination = "C:\Windows\System32"
$navSipDllPath = Join-Path $navSipDestination "navsip.dll"
try {
if (-not (Test-Path $navSipDllPath)) {
GetNavSipFromArtifacts -NavSipDestination $navSipDllPath
}
Write-Host "Unregistering dll $navSipDllPath"
RegSvr32 /u /s $navSipDllPath
Write-Host "Registering dll $navSipDllPath"
RegSvr32 /s $navSipDllPath
}
catch {
Write-Host "Failed to copy navsip to $navSipDestination"
}
}
Register-NavSip
Update 2024-01-29: Change PublishPipelineArtifact
to PublishBuildArtifacts
so that it works as before.
thanks so much for this procedure, and taking your time to document it.
Our problem is the lack of certificates to test the procedure.
Though - you very well documented the steps, so we'll try to create the step accordingly. we'll also try to documented the procedure to get to a pfx, since this still seems possible.
@waldo1001 If you want to try Azure Key Vault signing, you can import your current PFX into an Azure Key Vault Standard. It is software encrypted.
To meet your new CA's HSM requirements (e.g. FIPS 140-2 Level 2), you will need Premium.
There is also a new blog from Dmitry Katson about Code Signing with AL Go for GitHub and GlobalSign: https://katson.com/code-signing-in-2024/
Anything new about whether or when this is coming to AL-Ops?
We set this feature to "future". The "problem" is that our provider doesn't give us an HSM, but still a simple PFX... . That process took us 6 months .. . We asked a new one now, so let's see.. .
Question - would you want to test it if we blindly added it?
Absolutely, I’d be happy to test and provide feedback. Currently, we have the working PowerShell script referenced above.
To test, you can import your self-created .pfx file into the Azure Key Vault. Premium is only needed to meet your CA’s HSM requirements, not for general testing.
Is your feature request related to a problem? Please describe. It would be nice if ALOps supported signing via HSM (Azure Key Vault), which is supported by GlobalSign and DigiCert [1]. The hardware token signing option also looks promising #614, but is limited to hardware tokens attached to the VM. We currently have 8 different build agent VMs on a VMware cluster with three host machines. I'm not sure this is possible. If we need 8 tokens and the VMs can't be moved to a different host machine.
Describe the solution you'd like As suggested in #614, I will try to get Freddy's AL Go for Github action to work (https://github.com/microsoft/AL-Go/tree/main/Actions/Sign). Perhaps this could also be integrated into ALOpsAppSign.