diff --git a/AppxPackage/ManifestReader.cs b/AppxPackage/ManifestReader.cs
index 2b14865..2861935 100644
--- a/AppxPackage/ManifestReader.cs
+++ b/AppxPackage/ManifestReader.cs
@@ -178,6 +178,7 @@ namespace AppxPackage
}
public string Publisher { get { return StringValue (1); } }
public string ResourceId { get { return StringValue (4); } }
+ public string PublisherId => PublisherIdHelper.GetPublisherId (Publisher);
public DataUtils.Version Version
{
get
@@ -197,7 +198,9 @@ namespace AppxPackage
publisher = Publisher,
resource_id = ResourceId,
architecture = ProcessArchitecture.Select (e => (int)e).ToList (),
- version = Version.BuildJSON ()
+ version = Version.BuildJSON (),
+ publisher_id = PublisherId,
+ _publisher_id = "Note: The publisher id obtained may be inaccurate."
};
}
}
diff --git a/AppxPackage/PackageReader.cs b/AppxPackage/PackageReader.cs
index 84b4770..a13927f 100644
--- a/AppxPackage/PackageReader.cs
+++ b/AppxPackage/PackageReader.cs
@@ -13,6 +13,7 @@ using ICSharpCode.SharpZipLib;
using System.IO;
using System.Xml;
using ICSharpCode.SharpZipLib.Zip;
+using System.Security.Cryptography;
//using PriFormat;
namespace AppxPackage
{
@@ -108,6 +109,47 @@ namespace AppxPackage
return ret;
}
}
+ [ComVisible (true)]
+ internal static class PublisherIdHelper
+ {
+ // Base32 编码表 (Crockford 变体,去掉 I L O U 避免混淆)
+ private const string Base32Chars = "0123456789ABCDEFGHJKMNPQRSTVWXYZ";
+ ///
+ /// 从证书的可分辨名称 (Distinguished Name) 计算出对应的 Publisher ID。
+ ///
+ ///
+ /// 证书 DN 字符串,例如 "CN=Microsoft Corporation, O=Microsoft Corporation, L=Redmond, S=Washington, C=US"
+ ///
+ /// 13 位小写字母数字组成的 Publisher ID
+ /// distinguishedName 为 null 时抛出
+ public static string GetPublisherId (string distinguishedName)
+ {
+ if (distinguishedName == null)
+ throw new ArgumentNullException (nameof (distinguishedName));
+ byte [] dnBytes = Encoding.Unicode.GetBytes (distinguishedName);
+ byte [] hash;
+ using (var sha256 = SHA256.Create ())
+ {
+ hash = sha256.ComputeHash (dnBytes);
+ }
+ byte [] first8Bytes = new byte [8];
+ Array.Copy (hash, 0, first8Bytes, 0, 8);
+ var binaryBuilder = new StringBuilder (64);
+ for (int i = 0; i < first8Bytes.Length; i++)
+ {
+ binaryBuilder.Append (Convert.ToString (first8Bytes [i], 2).PadLeft (8, '0'));
+ }
+ string binaryString = binaryBuilder.ToString ().PadRight (65, '0');
+ var resultBuilder = new StringBuilder (13);
+ for (int i = 0; i < 65; i += 5)
+ {
+ string fiveBits = binaryString.Substring (i, 5);
+ int index = Convert.ToInt32 (fiveBits, 2);
+ resultBuilder.Append (Base32Chars [index]);
+ }
+ return resultBuilder.ToString ().ToLowerInvariant ();
+ }
+ }
internal class PriAllValuesReader: IDisposable
{
public PackageReader pr = null;
@@ -491,6 +533,7 @@ namespace AppxPackage
}
public string Publisher { get { return StringValue (1); } }
public string ResourceId { get { return StringValue (4); } }
+ public string PublisherId => PublisherIdHelper.GetPublisherId (Publisher);
public DataUtils.Version Version
{
get
@@ -519,7 +562,9 @@ namespace AppxPackage
resource_id = ResourceId,
architecture = ProcessArchitecture.Select (e => (int)e).ToList (),
version = Version.BuildJSON (),
- realver = RealVersion.BuildJSON ()
+ realver = RealVersion.BuildJSON (),
+ publisher_id = PublisherId,
+ _publisher_id = "Note: The publisher id obtained may be inaccurate."
};
}
}
@@ -1873,7 +1918,9 @@ namespace AppxPackage
}).ToList (),
familyName = id.FamilyName,
fullName = id.FullName,
- resourceId = id.ResourceId
+ resourceId = id.ResourceId,
+ publisherId = id.PublisherId,
+ _publisherId = "Note: The publisher id obtained may be inaccurate."
};
#endregion
#region prerequistes
@@ -2194,6 +2241,10 @@ namespace AppxPackage
nodeid.AppendChild (nodeidfamily);
nodeid.AppendChild (nodeidfamily);
nodeid.AppendChild (nodeidresid);
+ var nodeidpublid = xml.CreateElement ("PublisherId");
+ nodeidpublid.InnerText = id.PublisherId;
+ nodeidpublid.SetAttribute ("Description", "Note: The publisher id obtained may be inaccurate.");
+ nodeid.AppendChild (nodeidpublid);
root.AppendChild (nodeid);
}
#endregion
@@ -2620,7 +2671,9 @@ namespace AppxPackage
}).ToList (),
familyName = id.FamilyName,
fullName = id.FullName,
- resourceId = id.ResourceId
+ resourceId = id.ResourceId,
+ publisherId = id.PublisherId,
+ _publisherId = "Note: The publisher id obtained may be inaccurate."
};
#endregion
#region prerequistes
@@ -2949,6 +3002,10 @@ namespace AppxPackage
nodeid.AppendChild (nodeidfamily);
nodeid.AppendChild (nodeidfamily);
nodeid.AppendChild (nodeidresid);
+ var nodeidpublid = xml.CreateElement ("PublisherId");
+ nodeidpublid.InnerText = id.PublisherId;
+ nodeidpublid.SetAttribute ("Description", "Note: The publisher id obtained may be inaccurate.");
+ nodeid.AppendChild (nodeidpublid);
root.AppendChild (nodeid);
}
#endregion
diff --git a/PkgCLI/Polyfill.cs b/PkgCLI/Polyfill.cs
index 4366b96..e77b47d 100644
--- a/PkgCLI/Polyfill.cs
+++ b/PkgCLI/Polyfill.cs
@@ -137,7 +137,9 @@ namespace PkgCLI
}).ToList (),
familyName = id.FamilyName,
fullName = id.FullName,
- resourceId = id.ResourceId
+ resourceId = id.ResourceId,
+ publisherId = id.PublisherId,
+ _publisherId = "Note: The publisher id obtained may be inaccurate."
},
properties = new {
displayName = prop.DisplayName,
@@ -574,7 +576,9 @@ namespace PkgCLI
}).ToList (),
familyName = id.FamilyName,
fullName = id.FullName,
- resourceId = id.ResourceId
+ resourceId = id.ResourceId,
+ publisherId = id.PublisherId,
+ _publisherId = "Note: The publisher id obtained may be inaccurate."
},
properties = new {
displayName = prop.DisplayName,
diff --git a/shared/html/report.html b/shared/html/report.html
index e87f155..7c07187 100644
--- a/shared/html/report.html
+++ b/shared/html/report.html
@@ -104,6 +104,10 @@
Resource ID |
|
+
+ | Publisher ID |
+ |
+
| Properties |
@@ -319,6 +323,7 @@
t.querySelector("[name='package_family_name']").textContent = pi.identity.package_family_name;
t.querySelector("[name='package_full_name']").textContent = pi.identity.package_full_name;
t.querySelector("[name='resource_id']").textContent = pi.identity.resource_id;
+ t.querySelector("[name='publisher_id']").textContent = pi.identity.publisher_id;
t.querySelector("[name='display_name']").textContent = pi.properties.display_name;
t.querySelector("[name='publisher_display_name']").textContent = pi.properties.publisher_display_name;
t.querySelector("[name='description']").textContent = pi.properties.description;