What determines the appended string when we sign a msix application with signtool
Solution 1:
The block appended at the end of the string is a publisher hash, calculated from your subject (publisher). It is a 13-character string, base32-encoded representation of first few bytes of SHA-256 hash of your certificate Distinguished Name.
The algorithm is relatively straightforward:
- Take UTF-16 string containing the publisher name (certificate DN) (as-is, with all spaces and punctuations)
- Calculate SHA-256 hash of byte representation of this string
- Take first 8 bytes (64 bits)
- Pad the binary value by a single zero bit to the right (= left shift all bits)
- Group the bits in groups of 5 (since we had 64 + 1 bits, we should get 13 groups each having 5 bytes)
- For each group, convert the bit representation to an integer, and perform a look-up in a replacement table mapping the numbers to letters and digits.
- Join the letters together and make them lowercase to receive the publisher hash.
This can be done for example via the following PowerShell
function Get-PublisherHash($publisherName)
{
$publisherNameAsUnicode = [System.Text.Encoding]::Unicode.GetBytes($publisherName);
$publisherSha256 = [System.Security.Cryptography.HashAlgorithm]::Create("SHA256").ComputeHash($publisherNameAsUnicode);
$publisherSha256First8Bytes = $publisherSha256 | Select-Object -First 8;
$publisherSha256AsBinary = $publisherSha256First8Bytes | ForEach-Object { [System.Convert]::ToString($_, 2).PadLeft(8, '0') };
$asBinaryStringWithPadding = [System.String]::Concat($publisherSha256AsBinary).PadRight(65, '0');
$encodingTable = "0123456789ABCDEFGHJKMNPQRSTVWXYZ";
$result = "";
for ($i = 0; $i -lt $asBinaryStringWithPadding.Length; $i += 5)
{
$asIndex = [System.Convert]::ToInt32($asBinaryStringWithPadding.Substring($i, 5), 2);
$result += $encodingTable[$asIndex];
}
return $result.ToLower();
}
For example:
Get-PublisherHash "CN=SomeName, DN=Some Domain"
> qwz5zh2hhehvm
Any change of your certificate (where the publisher name changes) means that the hash is completely different. And since the hash is a part of MSIX family name and its full package name, the new app is treated as another application.
In some recent efforts to change it (Insider Builds 22000 and newer) there is a feature called "Persistent identity", which can be used to provide a smooth upgrade experience even if the certificate changes.
Source:
- My blog post about calculating the hash
- Microsoft documentation for feature "Persistent identity"
- Description of base32 algorithm