microsoft / navcontainerhelper

Official Microsoft repository for BcContainerHelper, a PowerShell module, which makes it easier to work with Business Central Containers on Docker.
MIT License
379 stars 243 forks source link

Sign APP for AppSource: the use of E-Token #3088

Open ThierryPermentier opened 1 year ago

ThierryPermentier commented 1 year ago

As of June 1st, code signing certificates are delivered as E-Token (on USB stick), not possible anymore to create a PFX from it. How can we sign the apps using functionality of BcContainerHelper? Any suggestions? Or how can we sign the app files in an automated way?

Thank you

CodeThumper commented 1 year ago

How did you get it to work? We are also struggling a bit after June 1st. Has anyone else done a DigiCert certificate and had success?

ThierryPermentier commented 1 year ago

Is your code signing certificate on an usb stick?

Luke-Johnson-CB commented 1 year ago

Hi ThierryPermentier, I work with CodeThumper. No, we got the option to have our private key stored in the Key Locker, but we know that's working because when we run the sign command (with Signtool.exe) without those arguments we get an error that the private key couldn't be accessed. I could go into more detail, but we're curious what you did to get the signing to work for you and where you got your certificate because we're open to doing it the exact same way if it works. We encountered the same problem of not being able to create the complete .pfx file, so what were the steps you took to sign your app given the new regulations? Thanks.

ThierryPermentier commented 1 year ago

Our certificate is from Sectigo (code signing for 1 year). We have a personal token on an USB stick. We followed the instructions from this post. Because the signtool is used with different parameters than the ones from navcontainerhelper, we wrote our own powershell (call to signtool.exe) to sign our extensions. So we sign our code on the host, not in the container. As we have to use a USB stick, we only can sign on 1 server. (There are solutions to share the USB across the network, but we do not use them)

Luke-Johnson-CB commented 1 year ago

Ok, thanks for the info. We did get it working with the DigiCert connection to their Key Locker using signtool manually in the command line after referencing their Authenticode setup and fixing the terminal environment variables.

RalleSP86 commented 1 year ago

Hi,

We have also get a new Codesigning Certificate from Digicert on a HSM USB-Stick. Is there already a solution to use this E-Token with Containerhelper? We can't use them in our Azure Pipelines. Th only way to sign the App is on a local host, where the USB-Token is connected.

ThierryPermentier commented 1 year ago

Is "Code signing with Software Trust Manager" from Digicert not an option for you?

mishof commented 10 months ago

Our codesigning certificate that we have been using for the past 3 years has expired. We ordered a new one at ssl.com. But we got a Codesigning Certificate on a yubikey. We can not create a pfx file anymore, and according to SSL.com this is as expected (see below).

I'll expect this problem to grow. Others will be experiencing the new codesigning limitations soon. Will containerhelper provide a solution for this?

"Starting June 1, 2023, SSL.com's Organization Validation (OV) and Individual Validation (IV) Code Signing Certificates have been issued either on Federal Information Processing Standard 140-2 (FIPS 140-2) USB tokens, Cloud HSMs, or through our eSigner cloud code signing service, and are no longer available as downloadable pfx files. This change is in compliance with the Certificate Authority/Browser (CA/B) Forum's new key storage requirements to increase security for code signing keys. The previous rule allowed OV and IV code signing certificates to be issued as downloadable files from the internet. Since the new requirements only allow the use of encrypted USB tokens or cloud-based FIPS compliant hardware appliances to store the certificate and private key, it is expected that instances of code signing keys being stolen and misused by malicious actors will be greatly reduced."

mishof commented 9 months ago

function Add-SignatureHBRD { [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingPlainTextForPassword', 'SslComPassWord', Justification = 'Password is stored as secure string in Azure DevOps')] [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingCmdletAliases', '', Justification = 'Echo used in a way that is yet to be translated to powershell')] [CmdletBinding()] param( [Parameter(Mandatory=$true)] [string] $containerName,
[Parameter(Mandatory=$true)] [string] $AppFile, [Parameter(Mandatory=$true)] [string] $SslComUserName, [Parameter(Mandatory=$true)] [string] $SslComPassWord, [Parameter(Mandatory=$true)] [string] $SslComSecret, [Parameter(Mandatory=$true)] [string] $SslComMode ) $ErrorActionPreference = 'Stop' $containerAppFile = Get-BcContainerPath -containerName $containerName -path $AppFile if ("$containerAppFile" -eq "") { Write-Error "The app ($appFile)needs to be in a folder, which is shared with the container $containerName" } Invoke-ScriptInBcContainer -containerName $containerName -useSession:$false -ScriptBlock { Param($AppFile, $SslComUserName, $SslComPassWord, $SslComSecret, $SslComMode)
$BuildBinariesPath = '.\EsignerBuildBinaries' if (Test-Path '.\EsignerBuildBinaries\') { if (!(Test-Path '.\EsignerBuildBinaries\eSigner_CKA_Setup*.exe')) { Remove-Item -LiteralPath '.\EsignerBuildBinaries' -Force -Recurse
} } if (!(Test-Path $BuildBinariesPath)) {

        New-Item -Path $BuildBinariesPath -ItemType Directory 

        if (!(Test-Path "C:\Windows\System32\msvcr120.dll")) {
            Write-Host "Downloading vcredist_x86"
            (New-Object System.Net.WebClient).DownloadFile('https://bcartifacts.azureedge.net/prerequisites/vcredist_x86.exe','c:\run\install\vcredist_x86.exe')
            Write-Host "Installing vcredist_x86"
            start-process -Wait -FilePath c:\run\install\vcredist_x86.exe -ArgumentList /q, /norestart
            Write-Host "Downloading vcredist_x64"
            (New-Object System.Net.WebClient).DownloadFile('https://bcartifacts.azureedge.net/prerequisites/vcredist_x64.exe','c:\run\install\vcredist_x64.exe')
            Write-Host "Installing vcredist_x64"
            start-process -Wait -FilePath c:\run\install\vcredist_x64.exe -ArgumentList /q, /norestart
        }

        if (!(Test-Path "C:\Windows\System32\vcruntime140_1.dll")) {
            Write-Host "Downloading vcredist_x64 (version 140)"
            (New-Object System.Net.WebClient).DownloadFile('https://aka.ms/vs/17/release/vc_redist.x64.exe','c:\run\install\vcredist_x64-140.exe')
            Write-Host "Installing vcredist_x64 (version 140)"
            start-process -Wait -FilePath c:\run\install\vcredist_x64-140.exe -ArgumentList /q, /norestart
        }

        if (Test-Path "C:\Program Files (x86)\Windows Kits\10\bin\*\x64\SignTool.exe") {
            $signToolExe = (get-item "C:\Program Files (x86)\Windows Kits\10\bin\*\x64\SignTool.exe").FullName
        } else {
            Write-Host "Downloading Signing Tools"
            $winSdkSetupExe = "c:\run\install\winsdksetup.exe"
            $winSdkSetupUrl = "https://bcartifacts.azureedge.net/prerequisites/winsdksetup.exe"
            (New-Object System.Net.WebClient).DownloadFile($winSdkSetupUrl,$winSdkSetupExe)
            Write-Host "Installing Signing Tools"
            Start-Process $winSdkSetupExe -ArgumentList "/features OptionId.SigningTools /q" -Wait
            if (!(Test-Path "C:\Program Files (x86)\Windows Kits\10\bin\*\x64\SignTool.exe")) {
                throw "Cannot locate signtool.exe after installation"
            }
            $signToolExe = (get-item "C:\Program Files (x86)\Windows Kits\10\bin\*\x64\SignTool.exe").FullName
        }

        if (!(Test-Path '.\eSigner_CKA_Setup')) {
            Write-Host "Download and Unzip eSignerCKA Setup"
            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
            $eSigner = (Get-ChildItem -Path '.\eSigner_CKA_Setup'  -Filter '*.exe' | Sort-Object | Select-Object -First 1).FullName
            Move-Item -Destination "eSigner_CKA_Installer.exe" -Path $eSigner
        }

    }

    Write-Host "Setup eSignerCKA in Silent Mode"                
    ./eSigner_CKA_Installer.exe /CURRENTUSER /VERYSILENT /SUPPRESSMSGBOXES /DIR="$BuildBinariesPath\Desktop\eSignerCKA" | Out-Null
    Get-ChildItem -Path $BuildBinariesPath\Desktop\eSignerCKA

    write-host "Config Account Information on eSignerCKA"
    & "$BuildBinariesPath\Desktop\eSignerCKA\eSignerCKATool.exe" config -mode $SslComMode -user $SslComUserName -pass $SslComPassWord -totp $SslComSecret -key "$BuildBinariesPath\Desktop\eSignerCKA\master.key" -r

    write-host "Unload and Load Certificate into Windows Store"
    & "$BuildBinariesPath\Desktop\eSignerCKA\eSignerCKATool.exe" unload
    & "$BuildBinariesPath\Desktop\eSignerCKA\eSignerCKATool.exe" load            

    write-host "Sign File with SignTool"
    $signToolExe = (get-item "C:\Program Files (x86)\Windows Kits\10\bin\*\x64\SignTool.exe").FullName
    $CodeSigningCert = Get-ChildItem Cert:\CurrentUser\My -CodeSigningCert | Select-Object -First 1; echo $CodeSigningCert.Thumbprint > .Thumbprint
    Set-Variable -Name Thumbprint -Value (Get-Content .Thumbprint); echo $Thumbprint
    & "$signtoolexe" @("sign", "/debug", "/fd", "sha256", "/tr", "http://ts.ssl.com" ,"/td", "sha256", "/sha1", $Thumbprint, $AppFile) | Write-Host
} -ArgumentList $containerAppFile, $SslComUserName, $SslComPassWord, $SslComSecret, $SslComMode

}

mishof commented 9 months ago

Above my powershell code for ssl.com. It could be a nice feature to have this one and maybe others insicde containerhelper.

KrunoslavPipic commented 6 months ago

Hi @mishof ,

I have a question. Is the code snippet you provided in PowerShell for manual signing or through some automation?

We're facing the same issue, and initially, we ordered the Certum SimplySign, which works for manually signing apps, but the token expires after 3 hours, making it impractical for automated signing through, for example, a pipeline. After encountering this problem, SSLPoint support directed us to purchase a token-based certificate, stating: "For automation, you can consider a token-based certificate, e.g., from GlobalSign: https://www.sslpoint.com/eu/code-signing/"

Alright, we have obtained this certificate now, but I still don't see a solution for using this certificate for automatic app signing, considering that our app is built through a pipeline using the Run-AlPipeline function from bccontainerhelper: image

Okay, we've resolved the token input issue, meaning the token can only be entered once, and then the app can be signed: image

However, this still pertains to manually signing apps on a computer where the certificate is loaded from a USB. Considering we have multiple agents, both self-hosted and Microsoft-hosted, I'm unsure if it's even possible to use this certificate for automated app signing in this manner: image

Before certificate expiring, we signed apps using built-in sigining in AL-Pipeline: image image image

Is there anyone else facing the same issue? And is it even possible to use the new HSM format of certificate for automatic app signing?

freddydk commented 6 months ago

AL-Go for GitHub handles this by getting the certificate delivered in an Azure KeyVault with HSM support and then using the AzureSignTool to sign the app. This is no longer done inside Run-AlPipeline, but is done after the pipeline - you should do the same. The Run-AlPipeline is only capable of signing in the "old" way.

mishof commented 6 months ago

@KrunoslavPipic This code works flawlessly in our ci/cd pipelines in Azure DevOps that build and release to appsource. I wrote it with support of ssl.com. It will probably only work with certicates provided through the ssl.com service. The method Freddy metions is probably more generic, and thus preferred.

KrunoslavPipic commented 6 months ago

thanks,

it works now as Freddy suggested:

AzureSignTool.exe sign --description-url $env:SIGNINGURL --file-digest sha384 --azure-key-vault-url $env:SIGNINGVAULTURL --azure-key-vault-client-id $env:SIGNINGCLIENTID --azure-key-vault-tenant-id $env:SIGNINGTENANTID --azure-key-vault-client-secret $env:SIGNINGCLIENTSECRET --azure-key-vault-certificate $env:SIGNINGCERTNAME --timestamp-rfc3161 'http://timestamp.digicert.com' --timestamp-digest sha384 -v $appFile.FullName