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;