Поделиться через


Класс System.Security.Cryptography.Xml.SignedXml

В этой статье приводятся дополнительные замечания к справочной документации по этому API.

Класс SignedXml представляет собой реализацию .NET спецификации синтаксиса и обработки XML-подписи консорциума Всемирной паутины (W3C), также известную как XMLDSIG (Цифровая подпись XML). XMLDSIG — это стандартный, совместимый способ подписывать и проверять все или часть XML-документа или другие данные, которые можно адресовать из универсального идентификатора ресурса (URI).

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

Класс SignedXml позволяет создавать следующие три типа цифровых подписей XML:

Тип подписи Описание
Обернутая подпись Подпись содержится в подписанном XML-элементе.
Обрамляющая подпись Подписанный XML содержится в элементе <Signature> .
Внутренняя отдельная подпись Подпись и подписанный XML находятся в одном документе, но ни один элемент не содержит другого.

Существует также четвертый тип подписи, называемый внешней отсоединенной подписью, которая заключается в том, что данные и подпись находятся в отдельных XML-документах. Внешние отсоединяемые подписи не поддерживаются классом SignedXml .

Структура XML-подписи

XMLDSIG создает <Signature> элемент, содержащий цифровую подпись XML-документа или других данных, которые можно адресовать из URI. Элемент <Signature> может дополнительно содержать сведения о том, где найти ключ, который проверяет подпись и какой алгоритм шифрования использовался для подписи. Базовая структура выглядит следующим образом:

<Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
    <SignedInfo>
      <CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>
      <SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
      <Reference URI="">
        <Transforms>
          <Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
        </Transforms>
        <DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
        <DigestValue>Base64EncodedValue==</DigestValue>
      </Reference>
    </SignedInfo>
    <SignatureValue>AnotherBase64EncodedValue===</SignatureValue>
</Signature>

Основными частями этой структуры являются:

  • элемент <CanonicalizationMethod>;

    Задает правила перезаписи Signature элемента из XML/text в байты для проверки подписи. Значением по умолчанию в .NET является http://www.w3.org/TR/2001/REC-xml-c14n-20010315, которое определяет надежный алгоритм. Этот элемент представлен свойством SignedInfo.CanonicalizationMethod .

  • элемент <SignatureMethod>;

    Задает алгоритм, используемый для создания и проверки подписей, который был применен к <Signature> элементу для создания значения в <SignatureValue>. В предыдущем примере значение http://www.w3.org/2000/09/xmldsig#rsa-sha1 определяет подпись RSA PKCS1 SHA-1. Из-за проблем с столкновением с SHA-1 корпорация Майкрософт рекомендует модель безопасности на основе SHA-256 или более поздней версии. Этот элемент представлен свойством SignatureMethod .

  • элемент <SignatureValue>;

    Указывает криптографическую подпись для <Signature> элемента. Если эта подпись не проверяется, некоторые части <Signature> блока были изменены, и документ считается недопустимым. Если <CanonicalizationMethod> значение является надежным, это значение очень устойчиво к изменению. Этот элемент представлен свойством SignatureValue .

  • URI Атрибут <Reference> элемента

    Указывает объект данных с помощью ссылки на URI. Этот атрибут представлен свойством Reference.Uri .

    • Не указывая атрибут URI, то есть установив свойство Reference.Uri в значение null, ожидается, что принимающее приложение должно знать идентификацию объекта. В большинстве случаев URI null может привести к возникновению исключения. Не используйте универсальный null код ресурса (URI), если приложение не взаимодействует с протоколом, который требует его.

    • Установка атрибута URI на пустую строку указывает, что подписывается корневой элемент документа, что является одной из форм закрытой подписи.

    • Если значение атрибута URI начинается с #, то значение должно соответствовать элементу в текущем документе. Эта форма может использоваться с любым из поддерживаемых типов подписей (охваченной подписи, вложенной подписи или внутренней отсоединённой подписи).

    • Что-либо другое считается внешней отсоединенной подписью ресурса и не поддерживается классом SignedXml.

  • элемент <Transforms>;

    Содержит упорядоченный список <Transform> элементов, описывающих, как подписант обработал объект данных. Алгоритм преобразования аналогичен методу канонизации, но вместо перезаписи <Signature> элемента он перезаписывает содержимое, определяемое атрибутом URI<Reference> элемента. Элемент <Transforms> представлен классом TransformChain .

    • Каждый алгоритм преобразования определяется как прием XML (набор узлов XPath) или байтов в качестве входных данных. Если формат текущих данных отличается от требований к входным данным преобразования, применяются правила преобразования.

    • Каждый алгоритм преобразования определяется как производящий либо XML, либо байты в качестве выходных данных.

    • Если выходные данные последнего алгоритма преобразования не определены в байтах (или не были указаны преобразования), метод канонизации используется в качестве неявного преобразования (даже если в элементе был указан <CanonicalizationMethod> другой алгоритм).

    • Значение http://www.w3.org/2000/09/xmldsig#enveloped-signature алгоритма преобразования кодирует правило, интерпретируемое как удаление <Signature> элемента из документа. В противном случае средство проверки конвертной сигнатуры будет дайджестировать документ, включая подпись, но подписыватель бы дайджестировал документ до применения подписи, что привело к разным ответам.

  • элемент <DigestMethod>;

    Определяет метод дайджеста (криптографического хэша), который будет применяться к преобразованном содержимому, определяемом атрибутом URI<Reference> элемента. Это свойство представлено свойством Reference.DigestMethod .

Выбор метода канонизации

Если не выполняется взаимодействие со спецификацией, которая требует использования другого значения, рекомендуется использовать метод канонизации .NET по умолчанию, который является алгоритмом XML-C14N 1.0, значение которого равно http://www.w3.org/TR/2001/REC-xml-c14n-20010315. Алгоритм XML-C14N 1.0 должен поддерживаться всеми реализациями XMLDSIG, особенно так как это неявное окончательное преобразование для применения.

Существуют версии канонических алгоритмов, которые поддерживают сохранение комментариев. Не рекомендуется использовать методы канонизации с сохранением комментариев, так как они нарушают принцип "подписывать то, что видишь". То есть комментарии в элементе <Signature> не изменят логику обработки подписи, а только повлияют на значение сигнатуры. При использовании слабого алгоритма подписи, разрешение включать комментарии предоставляет злоумышленнику лишнюю свободу для создания коллизии хэша, что делает измененный документ внешне легитимным. В .NET Framework по умолчанию поддерживаются только встроенные канонизаторы. Чтобы поддерживать дополнительные или пользовательские канонизаторы, см. свойство SafeCanonicalizationMethods. Если в документе используется метод канонизации, который не находится в коллекции, представленной SafeCanonicalizationMethods свойством, CheckSignature метод вернет false.

Замечание

Чрезвычайно оборонительное приложение может удалить любые значения из коллекции SafeCanonicalizationMethods, которые, как оно предполагает, подписавшие не будут использовать.

Являются ли ссылочные значения безопасными от изменения?

Да, <Reference> значения безопасны от изменения. .NET проверяет вычисление <SignatureValue> перед обработкой любого из <Reference> значений и связанных с ними преобразований и прекратит выполнение на раннем этапе, чтобы избежать потенциально вредоносных инструкций по обработке.

Выберите элементы для подписывания

Рекомендуется использовать значение "" для URI атрибута (или задать Uri для свойства пустую строку), если это возможно. Это означает, что весь документ рассматривается при вычислении дайджеста, следовательно, весь документ защищен от изменений.

Очень часто встречаются URI значения в виде якорей, таких как #foo, ссылаясь на элемент, атрибут идентификатора которого равен «foo». К сожалению, это легко изменить, так как это включает только содержимое целевого элемента, а не контекста. Злоупотребление этим различием известно как метод оболочки подписи XML (XSW).

Если ваше приложение считает комментарии семантикой (что не распространено при работе с XML), тогда вам следует использовать "#xpointer(/)" вместо "", и "#xpointer(id('foo'))" вместо "#foo". Версии #xpointer интерпретируются с включением комментариев, а формы shortname исключают комментарии.

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

Рекомендации по безопасности элемента KeyInfo

Данные в необязательном <KeyInfo> элементе (то есть свойство KeyInfo), который содержит ключ для проверки подписи, не должны быть доверенными.

В частности, если KeyInfo значение представляет открытый ключ RSA, DSA или ECDSA, документ может быть изменён, несмотря на CheckSignature подпись является допустимой. Это может произойти, так как сущность, выполняющая изменение, просто должна создать новый ключ и повторно подписать документ с этим новым ключом. Таким образом, если приложение не проверяет, является ли открытый ключ ожидаемым значением, документ должен рассматриваться как будто он был изменен. Для этого приложение проверяет открытый ключ, внедренный в документ, и проверяет его в списке известных значений контекста документа. Например, если документ можно интерпретировать как выданный известным пользователем, нужно проверить ключ в списке известных ключей, используемых этим пользователем.

Вы также можете проверить ключ после обработки документа с помощью CheckSignatureReturningKey метода, а не с помощью CheckSignature метода. Но для оптимальной безопасности необходимо заранее проверить ключ.

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

Вопросы безопасности элемента X509Data

Элемент <X509Data> является необязательным дочерним элементом <KeyInfo> и содержит один или несколько сертификатов X509 или идентификаторов для сертификатов X509. Данные в элементе <X509Data> также не должны быть изначально доверенными.

При проверке документа с внедренным <X509Data> элементом .NET проверяет только разрешение данных на сертификат X509, открытый ключ которого можно успешно использовать для проверки подписи документа. В отличие от вызова метода с параметром CheckSignature установленным в verifySignatureOnly, проверка отзыва не выполняется, доверие цепочки не проверяется и срок действия не проверяется. Даже если приложение извлекает сам сертификат и передает его CheckSignature методу с verifySignatureOnly заданным параметром false, это всё равно недостаточно для проверки, чтобы предотвратить подделку документа. Сертификат по-прежнему должен быть проверен как подходящий для подписанного документа.

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

  • Сертификат был выдан непосредственно или через цепочку центра сертификации (ЦС), открытый сертификат которого внедрен в приложение.

    Использование списка доверия, предоставленного ОС, без дополнительных проверок, таких как известное имя субъекта, недостаточно, чтобы предотвратить изменение SignedXml.

  • Сертификат проверен, чтобы удостовериться, что срок его действия не истек во время подписания документа (или "сейчас" для обработки документов в режиме, близком к реальному времени).

  • Для сертификатов длительного действия, выданных ЦС, поддерживающих процесс отзыва, убедитесь, что сертификат не был отозван.

  • Проверяется, что субъект сертификата подходит для текущего документа.

Выбор алгоритма преобразования

Если вы взаимодействуете со спецификацией, которая диктует определенные значения (например, XrML), необходимо следовать спецификации. Если у вас есть вложенная подпись (например, при подписи всего документа), необходимо использовать http://www.w3.org/2000/09/xmldsig#enveloped-signature (представленную классом XmlDsigEnvelopedSignatureTransform ). Кроме того, можно указать неявное XML-C14N преобразование, но это не обязательно. Для оболоченной или отделённой подписи преобразования не требуются. Неявное преобразование XML-C14N берёт на себя всё.

При обновлении системы безопасности, представленной бюллетенем майкрософт по безопасности MS16-035, .NET ограничивает, какие преобразования можно использовать в проверке документов по умолчанию, при этом ненадежные преобразования вызывают CheckSignature всегда возвращать false. В частности, преобразования, требующие дополнительных входных данных (указанных как дочерние элементы в XML), больше не допускаются из-за их подверженности злоупотреблений злоумышленниками. W3C рекомендует избежать преобразований XPath и XSLT, которые являются двумя основными преобразованиями, затронутыми этими ограничениями.

Проблема с внешними ссылками

Если приложение не проверяет, что внешние ссылки подходят для текущего контекста, они могут быть использованы в злоупотреблениях, приводящих к многим уязвимостям безопасности (включая отказ в обслуживании, распределенный отраженный отказ в обслуживании, раскрытие информации, обход подписей и удаленное выполнение кода). Даже если ваше приложение будет проверять внешний URI ссылки, проблема с повторной загрузкой ресурса останется: один раз, когда ваше приложение считывает его, и один раз при SignedXml чтении. Так как нет никакой гарантии того, что этапы чтения и проверки документов приложением имеют одинаковое содержимое, подпись не обеспечивает достоверность.

Учитывая риски внешних ссылок, SignedXml выбрасывает исключение при обнаружении внешней ссылки. Дополнительные сведения об этой проблеме см. в статье базы знаний 3148821.