How to calculate PublisherID from Publisher?
Asked Answered
P

3

17

I'd like to know something about Windows Store and APPX package internals. The package.appxmanifest has an <Identity> element that has a package name, publisher and version attributes, for example

<Identity
    Name="MyCompany.MyGreatApp"
    Publisher="CN=B408A06D-44F7-4860-A12E-644DD44FA743"
    Version="1.0.0.3" />

Apparently, when I open this manifest in VS2013 and go to Packaging tab, it shows me a read-only "Package Family Name" field, which is a concatenation of package name, underscore and something that looks like a strange hash of the publisher string.

MyCompany.MyGreatApp_f08ys7xx9zb3y

How do I calculate this hash (also known as PublisherId)? See also PackageId class or PACKAGE_ID structure.

Here are some sample values for you eager cryptanalysts. It is 13 lowercase letters and digits, so the approximate "quality" is 67 bits. Thank you!

8wekyb3d8bbwe   CN=Microsoft Corporation, O=Microsoft Corporation, L=Redmond, S=Washington, C=US
amge560j0aq9g   CN=C357A519-CEE3-4675-9EF4-44DE1D99A5D6
a2xxwqz7shah6   CN=07AACB4D-E1D7-4606-AF0F-77713A7C52F6
cw5n1h2txyewy   CN=Microsoft Windows, O=Microsoft Corporation, L=Redmond, S=Washington, C=US
54ggd3ev8bvz6   CN=2180B9A4-DDFD-4BFD-8D7E-EADC9C394EF5
azstdzfk4mfqj   CN=246910D1-A42D-4A04-8CF1-0C2A5CD42D4D
rxzpp8adhbvh8   CN=7882B094-0135-443F-8362-164AA239F2A0
pwh22gvzcj20c   CN=9C2E3884-8027-4E71-97C7-BB7731A649A4
q4d96b2w5wcc2   CN=DCD4AC3C-C7E0-46FF-8387-51FDC8CBC467
r6rtpscs7gwyg   CN=54157592-46DE-47CD-AF04-3B89DE46E29B
8xx8rvfyw5nnt   CN=6E08453F-9BA7-4311-999C-D22FBA2FB1B8
kzf8qxf38zg5c   CN=Skype Software Sarl, O=Microsoft Corporation, L=Luxembourg, S=Luxembourg, C=LU
a76a11dkgb644   CN=40886CD1-D5C5-48D6-B914-AB6E72010FFC
6bhtb546zcxnj   CN=BBC567E9-A52C-43A3-A890-F8B17D68310E
46hhcags7zat8   CN=ABF01D82-FF53-447D-B7E8-61B6F2105F68

pd2za7f9waemw   CN=B408A06D-44F7-4860-A12E-644DD44FA740
h0ed56e8a88dc   CN=B408A06D-44F7-4860-A12E-644DD44FA741
wcvtzcf7freyj   CN=B408A06D-44F7-4860-A12E-644DD44FA742
f08ys7xx9zb3y   CN=B408A06D-44F7-4860-A12E-644DD44FA743
85zvc56jp30ec   CN=C408A06D-44F7-4860-A12E-644DD44FA743
x4nmjqajw9mv6   CN=D408A06D-44F7-4860-A12E-644DD44FA743
qrhphajnj16d4   CN=E408A06D-44F7-4860-A12E-644DD44FA743
Pruinose answered 5/2, 2014 at 4:10 Comment(1)
I might be completely off, but I also noticed that letters I, L, O and U are missing in the sample hashes above. That would get us down to (10 + 22) possible values (5 bits); times 13 is 65 bits total. So we might be looking at strangely encoded 65 bits from an ordinary MD5, SHA1 or something. I am assuming the implementation of this PublisherID hash value is not completely insane.Pruinose
P
15

I was very close in my guess of how the PublisherId value is constructed. I heard some whispering...

The value is a Crockford’s Base32 encoding of the first eight bytes of SHA-256 hash of the publisher string in UTF-16 (little endian). I have a good implementation that I validated using the sample values from my question.

Pruinose answered 14/2, 2014 at 9:16 Comment(3)
Can you post the implementation? I'd love to see it and compare with one I'm writing.Reliant
@Tomas Karban I'd also like to see your implementationPrecondemn
I can't get it to work, the sha256 does not start with base32 encoding any help - e.g., when I hash "CN=Microsoft Corp..." I do not get something that starts with 471D3F2C6D42D7C7. Any tips on hashing the Publisher? spaces, nulls, ??? I'm using Get-FileHash on a UTF16 LE file (powershell)Underpants
S
5

Or you simply use the Kernel32.dll Method PackageFamilyNameFromId:

private static string GetPackageFamilyName(string name, string publisherId)
{
  string packageFamilyName = null;
  PACKAGE_ID packageId = new PACKAGE_ID
  {
    name = name,
    publisher = publisherId
  };
  uint packageFamilyNameLength = 0;
  //First get the length of the Package Name -> Pass NULL as Output Buffer
  if (PackageFamilyNameFromId(packageId, ref packageFamilyNameLength, null) == 122) //ERROR_INSUFFICIENT_BUFFER
  {
    StringBuilder packageFamilyNameBuilder = new StringBuilder((int)packageFamilyNameLength);
    if (PackageFamilyNameFromId(packageId, ref packageFamilyNameLength, packageFamilyNameBuilder) == 0)
    {
      packageFamilyName = packageFamilyNameBuilder.ToString();
    }
  }
  return packageFamilyName;
}

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode, Pack = 4)]
private class PACKAGE_ID
{
  public uint reserved;
  public uint processorArchitecture;
  public ulong version;
  public string name;
  public string publisher;
  public string resourceId;
  public string publisherId;
}


[DllImport("kernel32.dll", CharSet = CharSet.Unicode, ExactSpelling = true)]
private static extern uint PackageFamilyNameFromId(PACKAGE_ID packageId, ref uint packageFamilyNameLength, StringBuilder packageFamilyName);

public static void TestMethod()
{
  GetPackageFamilyName("MyCompany.TestApp", "CN=YourPublisherId");
}
Scientistic answered 15/2, 2016 at 11:26 Comment(1)
This should be the accepted answer as it uses documented public API rather than using hacks.Teyde
A
2

Contacted Tomas last Friday for some help; because I couldn't get it to work. But finally I managed to get it to work. The answer Tomas provided contains all the needed components, however I got a hard time to implement it. Especially the Crockford's Base32.

I've created a PowerShell function for it. Hopefully it can help more people out. Like what Tomas already wrote:

  • Convert to UTF16LE; in PowerShell this is a convert to Unicode (which automatically creates a UTF16 Little Endian format. (When you ouput the Byte array it will mostly contain the character followed by an '0' byte).
  • Then compute a SHA256 on the whole byte array and pull the first 8 bytes from it. And put the result into a byte array.
  • The hard part for me; Crockford's Base32 encoding. Base32 is a 5bit encoding; so we need to read 5 bits at a time. So I converted the whole byte array to a binary string. Padding it with zeros to 65 characters; because it has to be a multiple of 5. Then read 5 bits, convert it to decimal. And lookup the decimal index in de encoding string. Read the character on this index and add it to a string. Repeat with every 5 bits.

Final string should be the PublisherId.

Tomas; thanks for your help.

Function Get-PublisherIdFromPublisher ($Publisher) {
    $EncUTF16LE = [system.Text.Encoding]::Unicode
    $EncSha256 = [System.Security.Cryptography.HashAlgorithm]::Create("SHA256")

    # Convert to UTF16 Little Endian
    $UTF16LE = $EncUTF16LE.GetBytes($Publisher)

    # Calculate SHA256 hash on UTF16LE Byte array. Store first 8 bytes in new Byte Array
    $Bytes = @()
    (($EncSha256.ComputeHasH($UTF16LE))[0..7]) | % { $Bytes += '{0:x2}' -f $_ }

    # Convert Byte Array to Binary string; Adding padding zeros on end to it has 13*5 bytes
    $BytesAsBinaryString = -join $Bytes.ForEach{ [convert]::tostring([convert]::ToByte($_,16),2).padleft(8,'0') }
    $BytesAsBinaryString = $BytesAsBinaryString.PadRight(65,'0')

    # Crockford Base32 encode. Read each 5 bits; convert to decimal. Lookup position in lookup table
    $Coded = $null
    For ($i=0;$i -lt (($BytesAsBinaryString.Length)); $i+=5) {
        $String = "0123456789ABCDEFGHJKMNPQRSTVWXYZ"
        [int]$Int = [convert]::Toint32($BytesAsBinaryString.Substring($i,5),2)
        $Coded += $String.Substring($Int,1)
    }
    Return $Coded.tolower()
}
Angular answered 5/4, 2020 at 9:30 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.