Пятница, 12 октября 2012 22:33

Совместно используемые сборки и сборки со строгим именем

Оцените материал
(22 голосов)

В главе 2 рассказано о компоновке, упаковке и развертывании сборок. При этом основное внимание уделено закрытому развертыванию (private deployment), при котором сборки, предназначенные исключительно для одного приложения, помещают в базовый каталог приложения или в его подкаталог.

Закрытое развертывание сборок дает компаниям большие возможности в плане управления именованием, версиями и особенностями работы сборок.

В этой главе внимание сосредоточено на создании сборок, которые могут совместно использоваться несколькими приложениями. Замечательный при­ мер глобально развертываемых сборок - это сборки, поставляемые вместе с Microsoft .NET Framework, поскольку почти все управляемые приложения используют типы, определенные Microsoft в библиотеке классов .NET Framework Class Library (FCL).

Как уже было отмечено в главе 2, операционная система Windows получила репутацию нестабильной главным образом из-за того, что для создания и тестирования приложений приходится использовать чужой код. В конце концов, любое приложение для Windows, которое вы пишете, вызывает код, созданный разработчиками Microsoft. Более того, самые разные компании производят элементы управления, которые разработчики затем встраивают в свои приложения. Фактически такой подход стимулирует сама платформа .NET Framework, а со временем, вероятно, число производителей элементов управления воз­ растет.

Время не стоит на месте, как и разработчики из Microsoft, как и сторонние производители элементов управления: они устраняют ошибки, добавляют в свой код новые возможности и т. п. В конечном счете, на жесткий диск пользовательского компьютера попадает новый код. В результате давно установленное и прекрасно работавшее пользовательское приложение начинает задействовать уже не тот код, с которым оно создавалось и тестировалось. И поведение такого приложения становится непредсказуемым, что, в свою очередь, негативно влияет на стабильность Windows.

Решить проблему управления версиями файлов чрезвычайно трудно. На самом деле, я готов спорить, что если взять любой файл и изменить в нем значение одного-единственного бита с 0 на 1 или наоборот, то никто не сможет гарантировать, что программы, использовавшие исходную версию этого файла, будут работать с новой версией файла, как ни в чем не бывало. Это утверждение верно хотя бы потому, что множество программ случайно или преднамеренно учитывает ошибки других программ. Если в более поздней версии кода исправляется какая-либо ошибка, то использующее его приложение начинает работать непредсказуемо.

Итак, вопрос в следующем: как, устраняя ошибки и добавляя к программам новые функции, гарантировать, что эти изменения не нарушат работу других приложений? Я долго думал над этим и пришел к выводу - это просто не­ возможно. Но, очевидно, такой ответ не устроит никого, поскольку в поставляемых файлах всегда будут ошибки, а разработчики всегда будут одержимы желанием добавлять новые функции. Должен все же быть способ распространения новых файлов, позволяющий надеяться, что любое приложение после обновления продолжит замечательно работать, а если нет, то позволяющий легко вернуть приложение в последнее состояние, в котором оно прекрасно работало.

В этой главе рассказано об инфраструктуре .NET Framework, призванной решить проблемы управления версиями. Позвольте сразу предупредить: речь идет о сложных материях. Нам придется рассмотреть массу алгоритмов, правил и политик, встроенных в общеязыковую исполняющую среду (CLR). Помимо этого, упомянуты многие инструменты и утилиты, которыми приходится пользоваться разработчику. Все это достаточно сложно, поскольку, как я уже сказал, проблема управления версиями непроста сама по себе и то же можно сказать о подходах к ее решению.

Два вида сборок - два вида развертывания

Среда CLR поддерживает два вида сборок: с нестрогими именами (weakly named assemblies) и со строгими именами (strongly named assemblies).

Сборки со строгими и нестрогими именами идентичны по структуре, то есть в них используется файловый формат РЕ (portable executable ), заголовок РЕ32(+), СLR-заголовок, метаданные, таблицы манифеста, а также IL-код, рассмотренный в главах 1 и 2. Оба типа сборок компонуются при помощи одних и тех же инструментов, например компилятора С# или AL.exe. В действительности сборки со строгими и нестрогими именами отличаются тем, что первые подписаны при помощи пары ключей, уникально идентифицирующей издателя сборки. Эта пара ключей позволяет уникально идентифицировать сборку, обеспечивать ее безопасность, управлять ее версиями, а также развертывать в любом месте пользовательского жесткого диска или даже в Интернете. Возможность уникально идентифицировать сборку позволяет CLR при попытке привязки приложения к сборке со строгим именем применять определенные политики, которые гарантируют безопасность. Эта глава посвящена разъяснению сущности сборок со строгим именем и политик, применяемых к ним со стороны CLR.

Развертывание сборки может быть закрытым или глобальным. Сборку первого типа развертывают в базовом каталоге приложения или в одном из его подкаталогов. Для сборки с нестрогим именем возможно лишь закрытое развертывание. О сборках с закрытым развертыванием речь шла в главе 2. Сборку с глобальным развертыванием устанавливают в каком-либо общеизвестном каталоге, который CLR проверяет при поиске сборок. Такие сборки можно развертывать как закрыто, так и глобально. В этой главе объяснено, как создают и развертывают сборки со строгим именем. Сведения о типах сборок и способах их развертывания представлены в табл. 3.1.

Таблица 3.1. Возможные способы развертывания сборок со строгими и нестрогими именами

Тип сборки

Закрытое развертывание

Глобальное развертывание

Сборка с нестрогим именем

Да

Нет

Сборка со строгим именем

Да

Да

 

Примечание. Настоятельно рекомендую назначать сборкам строгие имена. Вполне вероятно, что будущие версии CLR потребуют, чтобы все сборки получали строгие имена, а сборки с нестрогими именами останутся «вне закона». Проблема с нестрогими сборками в том, что можно создать несколько разных сборок с одним нестрогим именем. В то же время присвоение строгого имени позволяет уникально идентифицировать ее. Если среда CLR сможет уникально идентифицировать сборку, она будет в состоянии применить больше политик, связанных с управлением версиями и обратной совместимостью. По большому счету, устранение возможности создавать сборки с нестрогими именами упрощает понимание политик управления версиями в CLR.

Назначение сборке строгого имени

Если планируется предоставить доступ к сборке нескольким приложениям, ее следует поместить в общеизвестный каталог, который среда CLR должна автоматически проверять, обнаружив ссылку на сборку. Однако с этим связана проблема - возможен вариант, когда две (или больше) компании сделают сборки с одинаковыми именами. Тогда, если обе эти сборки будут скопированы в один общеизвестный каталог, «победит» последняя из них, а работа приложений, использовавших первую, нарушится - ведь первая при копировании заменяется второй (это и является причиной «ада DLL» в современных системах Windows - все библиотеки DLL копируются в папку System32).

Очевидно, одного имени файла мало, чтобы различать две сборки. Среда CLR должна поддерживать некий механизм, позволяющий уникально идентифицировать сборку. Именно для этого и служат строгие имена. У сборки со строгим именем четыре атрибута, уникально ее идентифицирующих: имя файла (без расширения), номер версии, идентификатор регионального стандарта и открытый ключ. Поскольку открытые ключи представляют собой очень большие числа, вместо последнего атрибута используется небольшой хэш открытого ключа, который называют маркером открытого ключа (public key token). Следующие четыре строки, которые иногда называют отображаемым именем сборки (assembly display name ), идентифицируют совершенно разные файлы сборки:

{codecitation class="brush: plain; gutter: true;" width="100%" }"MyTypes.Version=1.0.8123.0, Culture=neutral, PublicKeyToken=bl7a5c561934e089"
"MyTypes.Version=1.0.8123.0, Culture="en-US", PublicKeyToken=bl7a5c561934e089"
"MyTypes.Version=2.0.1234.0, Culture=neutral, PublicKeyToken=bl7a5c561934e089"
"MyTypes.Version=1.0.8123.0, Culture=neutral, PublicKeyToken=b03f5f7flld50a3a"{/codecitation}

 

Первая строка идентифицирует файл сборки MyTypes.exeили MyTypes.dll (на самом деле, по строке идентификации нельзя узнать расширение файла сборки). Компания-производитель назначила сборке номер версии 1.0.8123.0, в ней нет компонентов, зависимых от региональных стандартов, так как атрибут Culture определен как neutral. Но сделать сборку MyTypes.dll (или MyTypes. ехе) с номером версии 1.0.8123.0 и нейтральными региональными стандартами может любая компания.

Должен быть способ отличить сборку, созданную этой компанией, от сборок других компаний, которым случайно были назначены те же атрибуты. В силу ряда причин компания Microsoft предпочла другим способам идентификации (при помощи GUID, URL и URN) стандартные криптографические технологии, основанные на паре из закрытого и открытого ключей. В частности, криптографические технологии позволяют проверять целостность данных сборки при установке ее на жесткий диск, а также ставят права доступа к сборке в зависимость от ее издателя. Все эти методики обсуждаются далее.

Итак, компания, желающая снабдить свои сборки уникальной меткой, должна получить пару ключей - открытый и закрытый, после чего открытый ключ можно будет связать со сборкой. У всех компаний будут разные пары ключей, поэтому они смогут создавать сборки с одинаковыми именами, версиями и региональными стандартами, не опасаясь возникновения конфликтов.

Примечание. Вспомогательный класс System.Reflection.AssemblyName позволяет легко генерировать имя для сборки, а также получать отдельные части имени сборки. Он поддерживает ряд открытых экземплярных свойств: Culturelnfo, FuiiName, KeyPair, Name и Version – и предоставляет открытые экземплярные методы, такие как GetPublicKey, GetPublicKeyToken, SetPublicKey и SetPublicKeyToken.

В главе 2 я продемонстрировал механизм назначения имени файлу сборки и применение номера версии и идентификатора региональных стандартов. У сборки с нестрогим именем атрибуты номера версии и региональных стандартов могут быть включены в метаданные манифеста. Однако в этом случае CLR всегда игнорирует номер версии, а при поиске сопутствующих сборок использует лишь идентификатор региональных стандартов. Поскольку сборки с нестрогими именами всегда развертываются в закрытом режиме, для поиска файла сборки в базовом каталоге приложения или в одном из его подкаталогов, указанном атрибутом privatePath конфигурационного ХМL-файла, CLR просто берет имя сборки (добавляя к нему расширение DLL или ЕХЕ).

Кроме имени файла, у сборки со строгим именем есть номер версии и идентификатор региональных стандартов. Кроме того, она подписана при помощи закрытого ключа издателя.

Первый этап создания такой сборки - получение ключа при помощи утилиты Strong Name (SN.exe), поставляемой в составе .NET Framework SDK и Microsoft Visual Studio. Эта утилита поддерживает множество функций, которыми пользуются, задавая в командной строке соответствующие параметры. Заметьте: все параметры командной строки SN.exe чувствительны к регистру. Чтобы сгенерировать пару ключей, выполните следующую команду:

{codecitation class="brush: plain; gutter: true;" width="100%" }SN -k MyCompany.snk{/codecitation}

Эта команда заставит SN.exe создать файл MyCompany.snk, содержащий открытый и закрытый ключи в двоичном формате. Числа, образующие открытый ключ, очень велики. При необходимости после создания этого файла можно использовать SN.exe, чтобы увидеть открытый ключ. Для этого нужно выполнить SN.exe дважды. Сначала -с параметром -р, чтобы создать файл, содержащий только открытый ключ (MyCompany. РubliсКеу): SN -р MyCompany.keys MyCompany.PublicKey А затем требуется выполнить SN.exe с параметром -tp и указать файл, со­ держащий открытый ключ:

{codecitation class="brush: plain; gutter: true;" width="100%" }SN -tpMyCompany.PublicKey{/codecitation}

Вместе с тем невозможно заставить SN.exe аналогичным образом отобразить закрытый ключ.

Большой размер открытых ключей затрудняет работу с ними. Чтобы облегчить жизнь разработчику (и конечному пользователю), были созданы маркеры открытого ключа. Маркер открытого ключа - это 64-разрядный хэш открытого ключа. Если вызвать утилиту SN.exe с параметром -tp, то после значения ключа она выводит соответствующий маркер открытого ключа.

Теперь мы знаем, как создать криптографическую пару ключей, и получение сборки со строгим именем не должно вызывать затруднений. При компиляции сборки необходимо задать компилятору параметр /kеуfilе:имя_файла:

{codecitation class="brush: plain; gutter: true;" width="100%" }csc /keyfile:MyCompany.snk Program.cs{/codecitation}

Обнаружив в исходном тексте этот параметр, компилятор открывает заданный файл (MyCompaпy.sпk), подписывает сборку закрытым ключом и встраивает открытый ключ в манифест сборки. Заметьте: подписывается лишь файл сборки, содержащий манифест, другие файлы сборки нельзя под­ писать явно.

В Visual Studio новая пара ключей создается в окне свойств проекта. Для этого перейдите на вкладку Signing, установите флажок Sign the assembly, а затем в поле со списком Choose а stroпg паmе key file выберите вариант <New ... >. Слова «подписание файла» означают здесь следующее: при компоновке сборки со строгим именем в таблицу метаданных манифеста FileDef заносится список всех файлов, составляющих эту сборку. Каждый раз, когда к манифесту добавляется имя файла, рассчитывается хэш содержимого этого файла, и полученное значение сохраняется вместе с именем файла в таблице FileDef. Можно заменить алгоритм расчета хэша, используемый по умолчанию, вызвав AL.exe с параметром /algid или задав на уровне сборки следующий атрибут, определяемый пользователем, - System.Reflectiоn. AssemblyAlgorithmidAttribute. По умолчанию для расчета хэша используется алгоритм SHA-1, возможностей которого должно хватать практически для любого приложения.

После компоновки РЕ-файла с манифестом рассчитывается хэш всего содержимого этого файла (за исключением подписи Authenticode Signature, строгого имени сборки и контрольной суммы заголовка РЕ), как показано на рис. 3.1. Для этой операции применяется алгоритм SHA-1, здесь его нельзя заменить никаким другим. Значение хэша подписывается закрытым ключом издателя, а полученная в результате цифровая подпись RSA заносится в зарезервированный раздел РЕ-файла (при расчете хэша РЕ-файла этот раздел исключается), и в СLR-заголовок РЕ-файла записывается адрес, по которому встроенная цифровая подпись находится в файле.

Рис. З.1. Подписание сборки

В этот РЕ-файл также встраивается открытый ключ издателя (он записывается в таблицу AssemblyOef метаданных манифеста). Комбинация имени файла, версии сборки, региональных стандартов и значения открытого ключа составляет строгое имя сборки, которое гарантированно является уникальным. Ни одна компания ни при каких обстоятельствах не сможет создать две одинаковые сборки, скажем, с именем Calculus, с той же парой ключей.

Теперь сборка и все ее файлы готовы к упаковке и распространению.

Как отмечено в главе 2, при компиляции исходного текста компилятор обнаруживает все типы и члены, на которые ссылается исходный текст. Также компилятору необходимо указать все сборки, на которые ссылается данная сборка. В случае компилятора С# для этого применяется параметр /reference. В задачу компилятора входит генерация таблицы метаданных AssemblyRef и размещение ее в результирующем управляемом моду л е. Каждая запись таблицы метаданных AssemblyRef описывает файл сборки, на которую ссылается данная сборка, и состоит из имени файла сборки (без расширения), номера версии, регионального стандарта и значения открытого ключа.

Примечание. Поскольку значение открытого ключа велико, в том случае, когда сборка ссылается на множество других сборок, значения открытых ключей могут занять значительную часть результирующего файла. Для экономии места в компании Microsoft рассчитывают хэш открытого ключа и берут последние 8 байт полученного хэша. В таблице AssemblyRef на самом деле хранятся именно такие усеченные значения открытого ключа - маркеры отрытого ключа. В общем случае разработчики и конечные пользователи намного чаще встречаются с маркерами, чем с полными значениями ключа. Вместе с тем нужно иметь в виду, что среда CLR никогда не использует маркеры открытого ключа в процессе принятия решений, касающихся без­ опасности или доверия, потому что одному маркеру может соответствовать несколько открытых ключей.

Прочитано 19612 раз
   

Поиск  

   

Материалы