【问题标题】:Resign Clickonce manifest using mage.exe使用 mage.exe 退出 Clickonce 清单
【发布时间】:2014-07-28 20:46:19
【问题描述】:

简单的场景。 Visual Studio 会在发布过程中生成和签名。

然后我转到MyApp.dll.config.deploy 文件并更新一些设置。

现在,当我尝试安装应用程序时,clickonce 安装程序说 MyApp.dll.config 的计算哈希值与清单中指定的不同。

所以我尝试了mage.exe -sign MyApplicationManifest -certfile ... -password ...,结果是MyApp.vsto successfully signed

但我仍然收到计算的哈希错误。我错过了一步吗?

我已阅读以下链接: http://msdn.microsoft.com/en-us/library/acz3y3te(v=vs.110).aspx http://blogs.msdn.com/b/msiclickonce/archive/2009/07/17/clickonce-application-fails-after-changing-configuration-file-and-resigning-the-manifest.aspx

【问题讨论】:

    标签: clickonce mage


    【解决方案1】:

    需要执行几个步骤才能退出清单。我使用 powershell 自动完成。

    1. 如果发布步骤已将文件重命名为 .deploy 扩展名,您需要将它们重命名为原始文件。
    2. 在您的应用程序清单上运行 mage.exe -update。如果您有部署清单,则需要执行相同的操作,除了在 mage.exe 的命令行参数中您需要指定应用程序清单的相对位置。
    3. 将所有文件重命名为 .deploy 扩展名(除清单之外的所有文件。
    4. 如果您有 setup.exe 引导程序并且您更改了它(例如更新 url),您必须使用 delcert.exe 删除签名,然后使用 signtool.exe 对其进行签名

    注意:一旦 exe 被签名,它就不能用 signtool 重新签名,除非你用 delcert.exe 删除签名。

    如果您的 clickonce 是 VSTO clickonce,您的清单名称将不同(我认为 MyApp.dll.manifest 与 MyApp.exe.manifest)。

    更新我已经包含了 powershell 脚本,其中敏感信息已被编辑。

    $root = "$PSScriptRoot"
    $ToolsPath = "C:\Tools"
    $CertFile = $ToolsPath + "\REMOVED"
    $CertPassword = "REMOVED"
    
    #Update the setup.exe bootstrappers update url
    Start-Process "$PSScriptRoot\setup.exe" -ArgumentList "-url=`"$ClickOnceUpdateUrl`"" -Wait
    
    #The bootstrappers signature is now invalid since we updated the url
    #We need to remove the old signature
    Start-Process 'C:\Tools\delcert.exe' -ArgumentList "`"$root\setup.exe`"" -Wait
    
    Write-Host "$root [writeline]"
    #Resign with signtool
    Invoke-Expression 'C:\Tools\signtool.exe sign /d "PUBLISHER NAME" /f "$CertFile" /p "$CertPassword" "$root\setup.exe"'
    
    #update config properties
    $CodeBasePath = Convert-Path "$PSScriptRoot\Application Files\MYAPP_*"
    $ConfigPath = $CodeBasePath + "\MYAPP.dll.config.deploy"
    [xml] $xml = Get-Content $ConfigPath
    
    $ApiEndpoint = $xml.SelectSingleNode('/configuration/appSettings/add[@key="MYAPP:ApiBaseUrl"]')
    $ApiEndpoint.value = $MYAPPApiEndpoint
    $xml.Save($ConfigPath)  
    
    #Begin Resigning various Manifests
    $AppManifestPath = Convert-Path "Application Files\MYAPP_*\MYAPP.dll.manifest"
    
    #Need to resign the application manifest, but before we do we need to rename all the files back to their original names (remove .deploy)
    Get-ChildItem "$CodeBasePath\*.deploy" -Recurse | Rename-Item -NewName { $_.Name -replace '\.deploy','' }
    
    #Resign application manifest
    Invoke-Expression 'C:\Tools\mage.exe -update "$CodeBasePath\MYAPP.dll.manifest" -certFile "$CertFile" -password "$CertPassword" -if "ID.ico"'
    
    #Regisn deployment manifests in root and versioned folder
    Invoke-Expression 'C:\Tools\mage.exe -update "$CodeBasePath\MYAPP.vsto" -certFile "$CertFile" -password "$CertPassword" -appManifest "$AppManifestPath" -pub "PUBLISHER NAME" -ti "http://timestamp.globalsign.com/scripts/timstamp.dll"'
    Invoke-Expression 'C:\Tools\mage.exe -update "$root\MYAPP.vsto" -certFile "$CertFile" -password "$CertPassword" -appManifest "$AppManifestPath" -pub "PUBLISHER NAME" -ti "http://timestamp.globalsign.com/scripts/timstamp.dll"'
    
    #Rename files back to the .deploy extension, skipping the files that shouldn't be renamed
    Get-ChildItem -Path "Application Files\*"  -Recurse | Where-Object {!$_.PSIsContainer -and $_.Name -notlike "*.manifest" -and $_.Name -notlike "*.vsto"} | Rename-Item -NewName {$_.Name + ".deploy"}
    

    【讨论】:

    • 我很想看看你为此编写的脚本。
    • @RichardEverett 我包含了脚本。
    【解决方案2】:

    这篇文章更详细地解释了同样的案例: https://blogs.msdn.microsoft.com/vsto/2009/04/29/signing-and-re-signing-manifests-in-clickonce-saurabh-bhatia/

    总结的解决方案是:

    • 修改配置文件
    • 如果需要,删除文件的 .deploy 扩展名
    • 更新应用程序清单

      mage.exe –update Myapplication.exe.manifest –certfile mycert.pfx

    • 更新 .application 文件

      mage.exe –update Myapplication.application –appmanifest “Application Files\MyApplication_%Version%\Myapplication.exe.manifest” –certfile mycert.pfx

    • 如果需要,恢复 .deploy 扩展

    【讨论】:

    • 感谢您发布该链接。我实际上遇到了这个问题,并使用我在那里学到的东西来编写我的脚本。
    • 您的链接现已损坏,仅供参考
    • 现在应该修复了。感谢微软通过修改链接重定向不断改善我们的生活。
    • 它又坏了,它现在重定向到一个不相关的帖子
    • @CristianT 太真实了它让我发笑
    【解决方案3】:

    您可能对我编写的 PowerShell 脚本感兴趣,它除了签署清单还修复了 ClickOnce 不支持 SHA256 证书和 SmartScreen 不再支持 SHA1 的新问题,导致其中一个或另一个显示警告。

    SignClickOnceApp.ps1

    发布时的代码

    <#
    .SYNOPSIS 
        A PowerShell Script to correctly sign a ClickOnce Application.
    .DESCRIPTION 
        Microsoft ClickOnce Applications Signed with a SHA256 Certificate show as Unknown Publisher during installation, ClickOnce Applications signed with a SHA1 Certificate show an Unknown Publisher SmartScreen Warning once installed, this happens because:
        1) The ClickOnce installer only supports SHA1 certificates (not SHA256), but,
        2) Microsoft has depreciated SHA1 for Authenticode Signing.
    
        This script uses two code signing certificates (one SHA1 and one SHA256) to sign the various parts of the ClickOnce Application so that both the ClickOnce Installer and SmartScreen are happy.
    .PARAMETER VSRoot
        The Visual Studio Projects folder, if not provided .\Documents\Visual Studio 2015\Projects will be assumed
    .PARAMETER SolutionName
        The Name of the Visual Studio Solution (Folder), if not provided the user is prompted.
    .PARAMETER ProjectName
        The Name of the Visual Studio Project (Folder), if not provided the user is prompted.
    .PARAMETER SHA1CertThumbprint
        The Thumbprint of the SHA1 Code Signing Certificate, if not provided the user is prompted.
    .PARAMETER SHA256CertThumbprint
        The Thumbprint of the SHA256 Code Signing Certificate, if not provided the user is prompted.
    .PARAMETER TimeStampingServer
        The Time Stamping Server to be used while signing, if not provided the user is prompted.
    .PARAMETER PublisherName
        The Publisher to be set on the ClickOnce files, if not provided the user is prompted.
    .PARAMETER Verbose
        Writes verbose output.
    .EXAMPLE
        SignClickOnceApp.ps1 -VSRoot "C:\Users\Username\Documents\Visual Studio 2015\Projects" -SolutionName "MySolution" -ProjectName "MyProject" -SHA1CertThumbprint "f3f33ccc36ffffe5baba632d76e73177206143eb" -SHA256CertThumbprint "5d81f6a4e1fb468a3b97aeb3601a467cdd5e3266" -TimeStampingServer "http://time.certum.pl/" -PublisherName "Awesome Software Inc."
        Signs MyProject in MySolution which is in C:\Users\Username\Documents\Visual Studio 2015\Projects using the specified certificates, with a publisher of "Awesome Software Inc." and the Certum Timestamping Server.
    .NOTES 
        Author  : Joe Pitt
        License : SignClickOnceApp by Joe Pitt is licensed under the Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License. To view a copy of this license, visit http://creativecommons.org/licenses/by-nc-sa/4.0/.
    .LINK 
        https://www.joepitt.co.uk/Project/SignClickOnceApp/
    #>
    param (
        [string]$VSRoot, 
        [string]$SolutionName, 
        [string]$ProjectName, 
        [string]$SHA1CertThumbprint, 
        [string]$SHA256CertThumbprint, 
        [string]$TimeStampingServer,
        [string]$PublisherName,
        [switch]$Verbose
    )
    
    $oldverbose = $VerbosePreference
    if($Verbose) 
    {
        $VerbosePreference = "continue" 
    }
    
    # Visual Studio Root Path
    if(!$PSBoundParameters.ContainsKey('VSRoot'))
    {
        $VSRoot = '.\Documents\Visual Studio 2015\Projects\'
    }
    if (Test-Path "$VSRoot")
    {
        Write-Verbose "Using '$VSRoot' for Visual Studio Root"
    }
    else
    {
        Write-Error -Message "VSRoot does not exist." -RecommendedAction "Check path and try again" -ErrorId "1" `
            -Category ObjectNotFound -CategoryActivity "Testing VSRoot Path" -CategoryReason "The VSRoot path was not found" `
            -CategoryTargetName "$VSRoot" -CategoryTargetType "Directory"
        exit 1
    }
    
    # Solution Path
    if(!$PSBoundParameters.ContainsKey('SolutionName'))
    {
        $SolutionName = Read-Host "Solution Name"
    }
    if (Test-Path "$VSRoot\$SolutionName")
    {
        Write-Verbose "Using '$VSRoot\$SolutionName' for Solution Path"
        $SolutionPath = "$VSRoot\$SolutionName"
    }
    else
    {
        Write-Error -Message "Solution does not exist." -RecommendedAction "Check Solution Name and try again" -ErrorId "2" `
            -Category ObjectNotFound -CategoryActivity "Testing Solution Path" -CategoryReason "The Solution path was not found" `
            -CategoryTargetName "$VSRoot\$SolutionName" -CategoryTargetType "Directory"
        exit 2
    }
    
    # Project Path
    if(!$PSBoundParameters.ContainsKey('ProjectName'))
    {
        $ProjectName = Read-Host "Project Name"
    }
    if (Test-Path "$SolutionPath\$ProjectName")
    {
        Write-Verbose "Using '$SolutionPath\$ProjectName' for Project Path"
        $ProjectPath = "$SolutionPath\$ProjectName"
    }
    else
    {
        Write-Error -Message "Project does not exist." -RecommendedAction "Check Project Name and try again" -ErrorId "3" `
            -Category ObjectNotFound -CategoryActivity "Testing Project Path" -CategoryReason "The Project path was not found" `
            -CategoryTargetName "$SolutionPath\$ProjectName" -CategoryTargetType "Directory"
        exit 3
    }
    
    # Publish Path
    if (Test-Path "$ProjectPath\publish")
    {
        Write-Verbose "Using '$ProjectPath\publish' for Publish Path"
        $PublishPath = "$ProjectPath\publish"
    }
    else
    {
        Write-Error -Message "Publish path does not exist." -RecommendedAction "Check Project has been published to \publish and try again" -ErrorId "4" `
            -Category ObjectNotFound -CategoryActivity "Testing Publish Path" -CategoryReason "The publish path was not found" `
            -CategoryTargetName "$ProjectPath\publish" -CategoryTargetType "Directory"
        exit 4
    }
    
    # Application Files Path
    if (Test-Path "$PublishPath\Application Files")
    {
        Write-Verbose "Using '$PublishPath\Application Files' for Application Files Path"
        $AppFilesPath = "$PublishPath\Application Files"
    }
    else
    {
        Write-Error -Message "Application Files path does not exist." -RecommendedAction "Check Project has been published to \publish and try again" -ErrorId "5" `
            -Category ObjectNotFound -CategoryActivity "Testing Application Files Path" -CategoryReason "The Application Files path was not found" `
            -CategoryTargetName "$PublishPath\Application Files" -CategoryTargetType "Directory"
        exit 5
    }
    
    # Target Path
    $TargetPath = Convert-Path "$AppFilesPath\${ProjectName}_*"
    if ($($TargetPath.Length) -ne 0)
    {
        Write-Verbose "Using $TargetPath for Target Path"
    }
    else
    {
        Write-Error -Message "No versions." -RecommendedAction "Check Project has been published to \publish and try again" -ErrorId "6" `
            -Category ObjectNotFound -CategoryActivity "Searching for published version path" -CategoryReason "No Application has been published using ClickOnce" `
            -CategoryTargetName "$AppFilesPath\${ProjectName}_*" -CategoryTargetType "Directory"
        exit 6
    }
    
    # SHA1 Certificate
    if(!$PSBoundParameters.ContainsKey('SHA1CertThumbprint'))
    {
        $SHA1CertThumbprint = Read-Host "SHA1 Certificate Thumbprint"
    }
    if ("$SHA1CertThumbprint" -notmatch "^[0-9A-Fa-f]{40}$")
    {
        Write-Error -Message "SHA1 Thumbprint Malformed" -RecommendedAction "Check the thumbprint and try again" -ErrorId "7" `
            -Category InvalidArgument -CategoryActivity "Verifying Thumbprint Format" -CategoryReason "Thumbprint is not a 40 character Base64 string" `
            -CategoryTargetName "$SHA1CertThumbprint" -CategoryTargetType "Base64String"
        exit 7
    }
    $SHA1Found = Get-ChildItem -Path Cert:\CurrentUser\My | where {$_.Thumbprint -eq "$SHA1CertThumbprint"} | Measure-Object
    if ($SHA1Found.Count -eq 0)
    {
        Write-Error -Message "SHA1 Certificate Not Found" -RecommendedAction "Check the thumbprint and try again" -ErrorId "8" `
            -Category ObjectNotFound -CategoryActivity "Searching for certificate" -CategoryReason "Certificate with Thumbprint not found" `
            -CategoryTargetName "$SHA1CertThumbprint" -CategoryTargetType "Base64String"
        exit 8
    }
    
    # SHA256 Certificate
    if(!$PSBoundParameters.ContainsKey('SHA256CertThumbprint'))
    {
        $SHA256CertThumbprint = Read-Host "SHA256 Certificate Thumbprint"
    }
    if ("$SHA256CertThumbprint" -notmatch "^[0-9A-Fa-f]{40}$")
    {
        Write-Error -Message "SHA256 Thumbprint Malformed" -RecommendedAction "Check the thumbprint and try again" -ErrorId "9" `
            -Category InvalidArgument -CategoryActivity "Verifying Thumbprint Format" -CategoryReason "Thumbprint is not a 40 character Base64 string" `
            -CategoryTargetName "$SHA256CertThumbprint" -CategoryTargetType "Base64String"
        exit 9
    }
    $SHA256Found = Get-ChildItem -Path Cert:\CurrentUser\My | where {$_.Thumbprint -eq "$SHA256CertThumbprint"} | Measure-Object
    if ($SHA256Found.Count -eq 0)
    {
        Write-Error -Message "SHA256 Certificate Not Found" -RecommendedAction "Check the thumbprint and try again" -ErrorId "10" `
            -Category ObjectNotFound -CategoryActivity "Searching for certificate" -CategoryReason "Certificate with Thumbprint not found" `
            -CategoryTargetName "$SHA256CertThumbprint" -CategoryTargetType "Base64String"
        exit 10
    }
    
    # TimeStamping Server
    if(!$PSBoundParameters.ContainsKey('TimeStampingServer'))
    {
        $TimeStampingServer = Read-Host "TimeStamping Server URL"
    }
    if ("$TimeStampingServer" -notmatch "^http(s)?:\/\/[A-Za-z0-9-._~:/?#[\]@!$&'()*+,;=]+$")
    {
        Write-Error -Message "SHA256 Thumbprint Malformed" -RecommendedAction "Check the TimeStamp URL and try again" -ErrorId "11" `
            -Category InvalidArgument -CategoryActivity "Verifying TimeStamping URL" -CategoryReason "TimeStamping URL is not a RFC Compliant URL" `
            -CategoryTargetName "$TimeStampingServer" -CategoryTargetType "URL"
        exit 11
    }
    
    # Publisher Name
    # Project Path
    if(!$PSBoundParameters.ContainsKey('PublisherName'))
    {
        $PublisherName = Read-Host "Publisher Name"
    }
    
    # Sign setup.exe and application.exe with SHA256 Cert
    Write-Verbose "Signing '$PublishPath\Setup.exe' (SHA256)"
    Start-Process "$PSScriptRoot\signtool.exe" -ArgumentList "sign /fd SHA256 /td SHA256 /tr $TimeStampingServer /sha1 $SHA256CertThumbprint `"$PublishPath\Setup.exe`"" -Wait -NoNewWindow
    Write-Verbose "Signing '$TargetPath\$ProjectName.exe.deploy' (SHA256)"
    Start-Process "$PSScriptRoot\signtool.exe" -ArgumentList "sign /fd SHA256 /td SHA256 /tr $TimeStampingServer /sha1 $SHA256CertThumbprint `"$TargetPath\$ProjectName.exe.deploy`"" -Wait -NoNewWindow
    
    # Remove .deploy extensions
    Write-Verbose "Removing .deploy extensions"
    Get-ChildItem "$TargetPath\*.deploy" -Recurse | Rename-Item -NewName { $_.Name -replace '\.deploy','' } 
    
    # Sign Manifest with SHA256 Cert
    Write-Verbose "Signing '$TargetPath\$ProjectName.exe.manifest' (SHA256)"
    Start-Process "$PSScriptRoot\mage.exe" -ArgumentList "-update `"$TargetPath\$ProjectName.exe.manifest`" -ch $SHA256CertThumbprint -if `"Logo.ico`" -ti `"$TimeStampingServer`"" -Wait -NoNewWindow
    
    # Sign ClickOnces with SHA1 Cert
    Write-Verbose "Signing '$TargetPath\$ProjectName.application' (SHA1)"
    Start-Process "$PSScriptRoot\mage.exe" -ArgumentList "-update `"$TargetPath\$ProjectName.application`"  -ch $SHA1CertThumbprint -appManifest `"$TargetPath\$ProjectName.exe.manifest`" -pub `"$PublisherName`" -ti `"$TimeStampingServer`"" -Wait -NoNewWindow
    Write-Verbose "Signing '$PublishPath\$ProjectName.application' (SHA1)"
    Start-Process "$PSScriptRoot\mage.exe" -ArgumentList "-update `"$PublishPath\$ProjectName.application`" -ch $SHA1CertThumbprint -appManifest `"$TargetPath\$ProjectName.exe.manifest`" -pub `"$PublisherName`" -ti `"$TimeStampingServer`"" -Wait -NoNewWindow
    
    # Readd .deply extensions
    Write-Verbose "Re-adding .deploy extensions"
    Get-ChildItem -Path "$TargetPath\*"  -Recurse | Where-Object {!$_.PSIsContainer -and $_.Name -notlike "*.manifest" -and $_.Name -notlike "*.application"} | Rename-Item -NewName {$_.Name + ".deploy"}
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-07-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多