Обзор удостоверений пакета в приложениях Windows
Удостоверение пакета — это уникальный идентификатор в пространстве и времени. Так же, как днк однозначно идентифицирует вас, удостоверение пакета однозначно идентифицирует пакет.
Пакет содержит связанный набор битов (файлов и т. д.). В двух пакетах нет одного удостоверения, и для всех изменений битов, связанных с пакетом, требуется другое удостоверение.
Что такое удостоверение пакета?
Удостоверение пакета — это логическая конструкция, однозначно определяющая пакет. Удостоверение состоит из 5 частей:
- Имя: это имя, выбранное разработчиком приложения. Microsoft Store обеспечивает уникальность всех имен приложений для всех разработчиков приложений в Магазине, но имена не гарантируют уникальность в общей экосистеме.
- Версия: номер версии пакета. Разработчик приложений может выбрать произвольные номера версий, но должен обеспечить увеличение номеров версий с обновлениями.
- Архитектура: архитектура процессора, предназначенная для пакета. Одно и то же приложение может быть создано для разных архитектур процессора, причем каждая сборка, размещенная в собственном пакете.
- ResourceId: строка, выбранная разработчиком приложения для уникального определения пакетов ресурсов, например различных языков или различных масштабов отображения. Пакеты ресурсов обычно являются нейтральными по архитектуре. Для пакетов всегда используется
~
resourceId. - Издатель: имя субъекта разработчика приложения, которое определяется их сертификатом подписи. Это теоретически уникально для каждого разработчика приложений, так как авторитетные центры сертификации используют уникальные реальные имена и удостоверения для заполнения поля имени субъекта сертификата.
Эта конструкция иногда называется кортежей 5-частей.
Примечание.
Для неподписанных пакетов (1) по-прежнему требуется издатель. (2) Издатель должен содержать маркер Unsigned (OID.2.25.3117293689139841740540773059495699722=1), (3) маркер Unsigned должен быть последним полем в строке издателя , и (4) нет сертификата или подписи для неподписанных пакетов.
Ограничения полей удостоверений пакета
Поле | Тип данных | Ограничения | Комментарии |
---|---|---|---|
Имя. | Строка пакета | Мин: 3 Макс: 50 |
Допустимые значения для API проверки (см . строку пакета) |
Версия | DotQuad | Мин: 0.0.0.0 Макс: 65535.65535.65535.65535 |
Строка формы использует нотацию base-10, "Major.Minor.Build". |
Архитектура | Перечисление | Min: N/A Max: N/A |
Допустимые значения: "нейтральные", "x86", "x64", "arm", "arm64", "x86a64" |
ResourceId | Строка пакета | Мин: 0 Максимум: 30 |
Допустимые значения для API проверки (см . строку пакета) |
Publisher | Строка | Мин. значение: 1. Макс: 8192 |
Допустимые значения на X.509 |
PublisherId | Строка | Мин: 13 Макс. 13 |
Base32 закодирован, Crockford variant, т. е. [a-hjkmnp-tv-z0-9] |
Что такое строка пакета?
Строка пакета — это строка, которая позволяет использовать следующие символы:
- Допустимые входные символы (подмножество ASCII)
- Прописные буквы (U+0041 thru U+005A)
- Строчные буквы (U+0061 thru U+007A)
- Номера (U+0030 thru U+0039)
- Dot (U+002E)
- Dash (U+002D)
Следующие значения запрещены использовать в качестве строк пакета:
Condition | Запрещенные значения |
---|---|
Не удается равенства | ".", ".", "con", "prn", "aux", "nul", "com1", "com2", "com3", "com4", "com5", "com6", "com7", "com8", "com9", "lpt1", "lpt2", "lpt3", "lpt4", "lpt5", "lpt6", "lpt7", "lpt8", "lpt9" |
Не удается начать с | "con.", "prn.", "aux.", "nul.", "com1.", "com2.", "com3.", "com4.", "com5.", "com6.", "com7.", "com8.", "com9.", "lpt1.", "lpt2.", "lpt3.", "lpt4.", "lpt5.", "lpt6.", "lpt7.", "lpt8.", "lpt9.", "xn--" |
Не удается завершить | "." |
Не удается содержать | .xn-" |
Строка пакета должна быть сравниваема с помощью интерфейсов API сравнения строк с порядковым регистром (например, _wcsicmp).
Идентификатор пакета name
и resourceid
поля являются строками пакетов.
Объект PackageId
PackageId — это объект, содержащий кортеж 5-частей в виде отдельных полей (Name
, , Version
, ResourceId
Architecture
). Publisher
Полное имя пакета
Полное имя пакета — это непрозрачная строка, производная от всех 5 частей удостоверения пакета (имя, версия, архитектура, resourceid, publisher)
<Name>_<Version>_<Architecture>_<ResourceId>_<PublisherId>
Например, одно полное имя пакета для приложения Фотографии Windows — "Microsoft.Windows.Photos_2020.20090.1002.0_x64__8wekyb3d8bbwe", Где "Microsoft.Windows.Photos" — это имя: "2020.20090.1002.0" — номер версии, "x64" — это целевая архитектура процессора, идентификатор ресурса пуст (содержимое между последними двумя подчеркиваниями) и "8wekyb3d8bbwe" — это идентификатор издателя для Майкрософт.
Полное имя пакета однозначно идентифицирует пакет ИЛИ пакет MSIX . Ошибка в наличии двух пакетов или пакетов с разным содержимым, но с одинаковым полным именем пакета.
Примечание.
MSIX — это новое имя для предыдущего термина APPX. Дополнительные сведения см. в разделе "Что такое MSIX"?
Имя семейства пакетов
Имя семейства пакетов — это непрозрачная строка, производная только от двух частей удостоверения пакета — имени и издателя:
<Name>_<PublisherId>
Например, имя семейства пакетов приложения Фотографии Windows — "Microsoft.Windows.Photos_8wekyb3d8bbwe", где "Microsoft.Windows.Photos" — это имя, а "8wekyb3d8bbwe" — это идентификатор издателя для Майкрософт.
Имя семейства пакетов часто называется полным именем пакета без версии.
Примечание.
Это не так, так как имя семейства пакетов также не имеет архитектуры и идентификатора ресурса.
Примечание.
Данные и безопасность обычно относятся к семейству пакетов. Например, если вы настроили приложение Блокнота, установленное из пакета Блокнота версии 1.0.0.0.0,0, для включения Wordwrap. Затем блокнот был обновлен до версии 1.0.0.1, а данные конфигурации не были перенесены в более новую версию пакета.
Идентификатор издателя
Имя семейства пакетов — это строка с форматом:
<name>_<publisherid>
Где идентификатор издателя имеет некоторые очень конкретные свойства:
- Производный от издателя
- MinLength = MaxLength = 13 chars [фиксированный размер]
- Допустимые символы (как regex) = a-hj-km-np-tv-z0-9
- Base-32, Crockford Variant, т. е. буквенно-цифровой (A-Z0-9), кроме I (глаз), L (ell), O (oh) или U (you)
- Порядковый регистр не учитывается для сравнения --- ABCDEFABCDEFG == abcdefabcdefg
Так что вы никогда не увидите % : \ / " ? или другие символы в идентификаторе издателя.
Дополнительные сведения см. в статье PackageFamilyNameFromId и PackageNameAndPublisherIdFromFromFamilyName .
Идентификатор издателя часто называется PublisherId.
Почему существует идентификатор издателя?
Идентификатор издателя существует, так как издатель должен соответствовать имени или подписи cert X.509, таким образом:
- Это может быть очень большой (длина <= 8192 chars)
- Он может включать символы, которые являются неловкими или ограниченными (обратная косая черта и т. д.)
Эти проблемы могут сделать некоторые строки X.509 неловкими или невозможными для использования в файловой системе, реестре, URL-адресах и других контекстах.
Разделы справки создать PublisherId?
Используйте PackageNameAndPublisherIdFromFamilyName для извлечения PublisherId
из объекта PackageFamilyName
.
Используйте PackageIdFromFullName для извлечения PublisherId
из объекта PackageFullName
.
Это редко необходимо создать из PublisherId
Publisher
, но это можно сделать с помощью доступных API:
#include <appmodel.h>
HRESULT PublisherIdFromPublisher(
_In_ PCWSTR publisher,
_Out_writes_(PACKAGE_PUBLISHERID_MAX_LENGTH + 1) PWSTR publisherId)
{
PCWSTR name{ L"xyz" };
const size_t nameLength{ ARRAYSIZE(L"xyz") - 1 };
const size_t offsetToPublisherId{ name + 1 }; // xyz_...publisherid...
PACKAGE_ID id{};
id.name = name;
id.publisher = publisher;
WCHAR familyName[PACKAGE_PUBLISHERID_MAX_LENGTH + 1]{};
UINT32 n{ ARRAYSIZE(familyName) };
RETURN_IF_WIN32_ERROR(PackageFamilyNameFromId(&id, &n, familyName);
RETURN_IF_FAILED(StringCchCopyW(publisherId, PACKAGE_PUBLISHERID_MAX_LENGTH + 1, familyName + offsetToPublisherId));
return S_OK;
}
Ниже приведена классическая реализация Windows C для той же операции:
#include <appmodel.h>
HRESULT PublisherIdFromPublisher(
_In_ PCWSTR publisher,
_Out_writes_(PACKAGE_PUBLISHERID_MAX_LENGTH + 1) PWSTR publisherId)
{
const WCHAR c_name[]{ L"xyz" };
const UINT32 c_nameLength{ ARRAYSIZE(c_nameForPublisherToPublisherId) - 1 };
PACKAGE_ID id{};
id.name = c_name;
id.publisher = publisher;
WCHAR familyName[PACKAGE_PUBLISHERID_MAX_LENGTH + 1]{};
UINT32 n{ ARRAYSIZE(familyName) };
RETURN_IF_WIN32_ERROR(PackageFamilyNameFromId(&id, &n, familyName));
RETURN_IF_FAILED(StringCchCopyW(publisherId, PACKAGE_PUBLISHERID_MAX_LENGTH + 1, familyName + c_nameLength + 1);
return S_OK;
}
Это создает PublisherId, преобразовав идентификатор пакета в имя семейства пакетов с результирующий формат xyz_<publisherid>
. Этот рецепт является стабильным и надежным.
Это требуется только для компиляции с appmodel.h из пакета SDK и связывания с ядром32.lib (или kernelbase.lib, onecore.lib или api-ms-win-appmodel-runtime-l1.lib при использовании APIIsets).
Общие сведения об архитектуре процессора в удостоверении пакета
Распространенное недоразумение заключается в том, что Architecture=x64
пакет может содержать только код x64. Это не так. Это означает, что пакет работает в системах, поддерживающих код x64, и может использоваться приложениями x64. Вы можете сделать пакет, содержащий только PDF-файлы, но объявить его <Identity Architecture=x64...>
, так как он предназначен только для установки в системах, совместимых с x64 (например, пакеты x64 могут быть установлены только в системах x64 и (с Windows 11) Arm64, так как системы x86, Arm и Windows 10 Arm64 не поддерживают x64.
Еще более неправильно понимается, не означает, Architecture=neutral
что пакет не содержит исполняемый код. Это означает, что пакет работает на всех архитектурах. Например, можно сделать пакет, содержащий API шифрования AES, написанный на JavaScript, Python, C#, и т. д., но производительность не допускается в системах Arm64. Таким образом, вы включаете оптимизированный двоичный файл Arm64 и реализуете API для его обработки:
void Encrypt(...)
{
HANDLE h{};
if (GetCpu() == arm64)
{
h = LoadLibrary(GetCurrentPackagePath() + "\bin\encrypt-arm64.dll")
p = GetProcAddress(h, "Encrypt")
return (*p)(...)
}
else
{
// ...call other implementation...
}
}
Кроме того, можно сделать нейтральный пакет несколькими вариантами:
\
bin\
encrypt-x86.dll
encrypt-x64.dll
encrypt-arm.dll
encrypt-arm64.dll
Затем разработчики могут LoadLibrary("bin\encrypt-" + cpu + ".dll")
получить соответствующий двоичный файл для своего процесса во время выполнения.
Как правило, нейтральные пакеты не имеют содержимого на архитектуру, но они могут. Существуют ограничения на то, что можно сделать (например, можно сделать пакет Блокнота, содержащий notepad.exe скомпилированный для x86 + x64 + arm + arm64, но appxmanifest.xml может объявлять <Application Executable=...>
только одно из них). Учитывая, что есть пакеты, которые позволяют устанавливать только необходимые биты, это очень редкое дело. Это не незаконно, просто расширенные и экзотические.
Кроме того, (или x64|arm|arm64) не означает, Architecture=x86
что пакет содержит только исполняемый код для указанной архитектуры. Это просто подавляющее частое дело.
Примечание.
При обсуждении "кода" или "исполняемого кода" в этом контексте мы ссылаемся на переносимые исполняемые файлы (PE).
Учитывается ли регистр удостоверения пакета?
В основном нет, но Publisher
учитывает регистр.
Остальные поля (Name
, ResourceId
, PublisherId
PackageFullName
иPackageFamilyName
) не являются. Эти поля сохраняют регистр, но сравнивают регистр без учета регистра.
См. также
Windows developer