WWW.BOOK.LIB-I.RU
БЕСПЛАТНАЯ  ИНТЕРНЕТ  БИБЛИОТЕКА - Электронные ресурсы
 
s

Pages:   || 2 | 3 | 4 | 5 |

«том 4 альманах програ ста Тематический сборник материалов MSDN Library и IN/ISDN' Magazine Microsoft Безопасность в.NET Шифрование Защита кода и данных Н.РШШР Е альманах программиста ...»

-- [ Страница 1 ] --

том 4

альманах

програ ста

Тематический сборник материалов

MSDN" Library и IN/ISDN' Magazine

Microsoft"

Безопасность в.NET

Шифрование

Защита кода и данных

Н.РШШР Е

альманах программиста

Microsoft*

Составитель Ю. Е. Купцевич

Москва 2004

И.РШШ Р Е Ц П Ц У

УДК 004.45

ББК 32.973.26-018.2

А57

А57 Альманах программиста, том 4. Безопасность в Microsoft.NET/

Сост. Ю. Е. Купцевич. — М.: Издательско-торговый дом «Русская Редакция», 2004. — 304 с.; ил.

ISBN 5-7502-0184-8

Альманах представляет собой тематическую подборку статей из журнала MSDN Magazine/Русская Редакция- Издание адресовано широкому кругу программистов, интересующихся современными и перспективными информационными технологиями. Четвертый том альманаха, посвященный проблемам безопасности, состоит из двух тематических рубрик, содержащих 16 статей. В этих статьях рассматриваются такие вопросы, как шифрование и защита данных и кода на платформе 2003.

УДК 004.45 ББК 32.973.26-018.2.NET, ActiveX, IntelliSense, JScript, Microsoft, Microsoft Press, MSDN, VBScript, Visual Basic, Visual C++, Visual J++, Visual Studio, Win32, Windows и Windows NT являются либо охраняемыми товарными знаками, либо товарными знаками корпорации Microsoft в США и/или других странах. NT — товарный знак компании Northern Telecom Limited. Все другие товарные знаки являются собственностью соответствующих фирм.

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

© Microsoft Corporation и CMP Media LLC, 2004 ISBN 5-7502-0184-8 © ИТД «Русская Редакция», 2004 Оглавление Шифрование 7 Дэн Фокс Шифрование Пространство имен Cryptography в.NET Framework 9 Джеймс Мак-Каффри Шифрование Защита данных с помощью Advanced Encryption Standard 25 Кит Браун Безопасный код Хэширование паролей, атрибут AllowPartiallyTrustedCallers 52 Стефен Тауб.NET Remoting Защита трафика.NET Remoting с помощью приемников каналов асимметричного шифрования 60 Защита кода и данных...... 81 Дон Бокс Защита в.NET Сервисы признаков, политик, разрешений и применения политик в инфраструктуре CLR 83 Microsoft Принципы безопасного кодирования для.NET Framework 122 Кит

–  –  –

Нирадж Сривастава Безопасность Защита SOAP-пакетов на основе WS-Security и приемников каналов Remoting 195 Майкл Говард Оценка уязвимостей Полезные советы по выявлению уязвимостей в вашем коде 210 Габриэл Торок и Билл Лич Затемнение кода Пресечение попыток обратного проектирования вашего кода Visual Basic.NET или С# 219 Алек Дэвис Защита Закрытие доступа к строкам подключения баз данных и другим секретным параметрам в коде 233 Джейсон Фишер Охота на вирусы Знание типичных механизмов вирусных атак поможет лучше защитить приложения 251 Стэн Зундблат и Пер Зундблат Шаблоны Архитектура автономного приложения 268 Михаил Коготков-Лизин Защищенный вход Средства безопасной аутентификации через Microsoft.NET Passport 280 От составителя Уважаемый читатель!

Альманах программиста представляет собой тематический сборник статей из Microsoft MSDN Library и журнала «MSDN Magazine* по наиболее актуальным и перспективным технологиям разработки программного обеспечения. Он выпускается издательством «Русская Редакция» периодически, по мере накопления материалов. Альманах избавляет вас от поисков нужной информации, опубликованной в отдельных номерах журнала «MSDN Magazine» или документах MSDN Library за 2002-2003 гг. Первый том был посвящен работе с базами данных, второй — технологиям ASP/ASP.NET, третий — основным программным продуктам, образующим платформу 2003 (Windows Server 2003, IIS 6.0, Visual Studio.NET 2003 и Microsoft Office System, в том числе Microsoft InfoPath 2003), а четвертый адресован программистам, желающим освоить методы шифрования и защиты кода и данных на платформе 2003.





Четвертый том альманаха состоит из двух тематических рубрик.

• Шифрование. Пространство имен Cryptography в.NET Framework и его применение для шифрования по различным алгоритмам, шифрование с применением AES (Advanced Encryption Standard), хэширование паролей, атрибут AllowParti ally Trusted Callers, использование приемников каналов асимметричного шифрования для защиты трафика.NET Remoting.

• Защита кода и данных. Модель защиты по нравам доступа кода, обзор расширений S4U Kerberos в Windows Server 2003, новые средства IIS 6.0 для надежной защиты информации и серверных процессов, ролевая защита на основе Authorization Manager в.NET-приложениях, защита SOAP-пакетов на основе WS-Security и приемников каналов Remoting, рекомендации по выявлению уязвимостей в вашем коде, затемнение кода для пресечения попыток обратного проектирования вашего кода, написанного на Visual Basic.NET или С#, закрытие доступа к строкам подключения баз данных и другим секретным параметрам в коде, типичные механизмы вирусных атак, архитектура защищенного автономного приложения и средства безопасной аутентификации через Microsoft.NET Passport.

От составителя Исходный код для статей альманаха можно скачать по ссылке, указанной в конкретной статье или в полном комплекте (для всех статей альманаха) с Web-сайта издательства «Русская Редакция» по ссылке www.rusedit.ru/ downloaoVcode_ap4.zip.

Пятый том альманаха будет посвящен тематике, связанной с базовыми концепциями и функциональностью.NET Framework, в том числе системе типов, механизмам отражения (reflection) и удаленного взаимодействия (reraoting).

В дальнейшем планируется выпуск альманахов по специфике языков программирования, поддерживающих.NET, отладке и тестированию и другим темам. Если вас интересует какая-то специфическая тематика из материалов MSDN Magazine или MSDN Library, обращайтесь на сайт издательства www.rusedit.ru или по адресу almanah@rusedit.ru. Мы постараемся учесть ваши пожелания в будущих выпусках альманаха.

Дэн Фокс Шифрование Пространство имен Cryptography в.NET Framework*.NET Framework включает набор криптографических сервисов, расширяющих аналогичные сервисы Windows через Crypto API. Автор исследует пространство имен System.Securlty.Cryptography и модель программирования, применяемую в криптографических преобразованиях.

В статье обсуждается, почему в.NET использовать шифрование стало проще, показывается, насколько легко разработчики могут программно обращаться к криптографическим API-функциям, и поясняется разница между алгоритмами симметричного и асимметричного шифрования. Кроме того, кратко рассматривается большинство наиболее распространенных алгоритмов, в том числе RSA, DSA, Rljndael, SHA и др.

Взрывное развитие электронной коммерции резко усилило потребность в надежных алгоритмах шифрования для защиты конфиденциальной информации. Для решения этой задачи Microsoft еще в 1996 г. ввела Cryptography API (Crypto API). Продолжающаяся эволюция средств защиты способствует появлению новых криптографических сервисов, таких как пространство имен System.Securlty.Cryptography общеязыковой исполняющей среды (cominon language runtime, CLR) в Microsoft.NET.

Это пространство имен открывает программный доступ к самым разнообразным криптографическим сервисам, с помощью которых приложения могут шифровать и дешифровать данные, обеспечивать их целостность, Публиковалось в MSDN Magazine/Русская Редакция. 2002. № 1 (июль). — Прим. изд.

3,0 Шифрование а также обрабатывать цифровые подписи и сертификаты. Некоторые из таких сервисов применяются непосредственно в самой.NET Framework (например для поддержки аутентификации в ASP.NET). Я изложу базовые сведения о криптографических сервисах, предоставляемых.NET Framework, и приведу несколько примеров кода, демонстрирующих возможное применение соответствующих классов в приложениях.

Пространство имен Cryptography На самом высоком уровне пространство имен Cryptography можно разбить на четыре основные части (табл. 1). Главное предназначение этого пространства — предоставлять классы с алгоритмами таких операций, как шифрование и создание хэшей. Эти алгоритмы реализуются на основе расширяемого шаблона (pattern), включающего два уровня наследования.

На вершине иерархии располагается абстрактный базовый класс (вроде AsymmetricAlgorithm или Hash Algorithm), имя которого соответствует типу алгоритма. От такого класса наследует абстрактный класс второго уровня, предоставляющий открытый интерфейс для использования данного алгоритма.

Например, SHA1 (Secure Hash Algorithm) представляет собой производный от HashAlgorithm класс и содержит методы и свойства, специфичные для алгоритма SHA1. Наконец, сама реализация алгоритма является производной от класса второго уровня; именно ее экземпляр создается и используется клиентским приложением. На этом уровне реализация может быть управляемой, неуправляемой или и той и другой, Табл. 1. Основные элементы пространства имен Cryptography

–  –  –

К именам неуправляемых реализаций обычно добавляется суффикс «CryptoServiceProvider» (скажем, SHAlCryptoServiceProvider), указывающий на то, что данная реализация на самом деле предоставляется провайдером криптографического сервиса (Cryptographic Service Provider, CSP), который установлен на уровне операционной системы и действует как оболочка Crypto API. В имена управляемых реализаций включается суффикс «Managed» (например SHAlManaged). Такие реализации не опираются на Crypto API и содержат исключительно управляемый код.

Еще один важный момент. При создании экземпляра одного из конкретных классов исходные конструкторы всегда записывают в параметры объекта разумные и безопасные значения (если это возможно). Так, алгоритмы асимметричного шифрования, опирающиеся на криптографию с открытым ключом, генерируют случайную пару ключей, а алгоритмы симметричного шифрования — случайный ключ и вектор инициализации (initialization vector, IV); при этом они автоматически настраивают такие свойства, как Mode и Padding. Более того, алгоритмы второго типа по умолчанию стараются использовать «стойкие* значения.

Второй основной набор классов в пространстве имен System.Security.Cryptography включает как классы, реально применяемые в процессе шифрования и расшифровки данных, так и разнообразные вспомогательные классы. Это пространство имен содержит, например, абстрактный класс RandomNumberGenerator, от которого наследуют классы RNGCryptoServiceProvider, ToBase64Transform и FromBase64Transform (используются при соответствующих преобразованиях данных).

Пространство имен Cryptography не только предоставляет алгоритмы шифрования, но и содержит дочернее пространство имен Х509Certificates.

Последнее объединяет всего три класса, предназначенных для операций с сертификатами Authenticode X.509 v.3.

Класс X509Certificate предоставляет статические методы CreateFromCertFile и CreateFromSignedFile для создания экземпляра сертификата:

Dim с As X509Certificate с = X509Certificate.CreateFromCertFile("myCert.cer") Console.WriteLine(c.GetName) Console.WriteLine(c.GetPublicKeyString) Console.WriteLine(c.GetSerialNumberString) Console.WriteLineCc.GetExpirationDateString) После этого сертификат можно использовать в нескольких целях, в том числе для проверки на Web-сервере: вы связываете сертификат с клиентским запросом через свойство ClientCertificates объекта System.Net.HttpWebRequest (оно открывает доступ к объекту X509CertificateCollection).

Шифрование В пространстве имен Cryptography также присутствует дочернее пространство имен XML, используемое системой защиты.NET Framework для цифровой подписи XML-объектов в соответствии с проектом WSC-спецификации по синтаксису и обработке XML-подписей (http://www.w3.org/ TR/2000/WD-xmldsig-core-20000228/). Эта спецификация описывает синтаксис, а также правила создания и представления цифровых подписей, которые могут быть применены к любому информационному наполнению — внутреннему или внешнему по отношению к XML-документу с цифровой подписью. Хотя в этой спецификации ничего не говорится о шифровании XML (пока!), она по-настоящему важна в реализации поддержки целостности данных, аутентификации сообщений и сервисов аутентификации владельцев подписей для XML-документов.

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

Алгоритмы шифрования Пространство имен Cryptography, в частности, открывает доступ к алгоритмам симметричного шифрования (далее — симметричные алгоритмы).

Симметричные алгоритмы называются так потому, что для шифрования и дешифрования используется один секретный ключ. Очевидно, что и отправитель, и получатель должны держать такой ключ в секрете, иначе шифрование станет бессмысленным. Кроме того, в режиме СВС симметричные алгоритмы используют IV — несекретное двоичное значение; оно инициализирует алгоритм и вносит дополнительные вариации в процесс шифрования. SymmetricAlgorithm является абстрактным базовым классом, от которого наследуют другие классы, специфичные для конкретных алгоритмов.

К числу поддерживаемых симметричных алгоритмов относятся Data Encryption Standard (DES), RC2, Rijndael и Triple Data Encryption Standard (TripleDES). Каждый алгоритм включает какой-нибудь производный от SymmetricAlgorithm абстрактный класс вроде DES и производный от базового управляемый класс или класс провайдера сервиса, например DESCryptoServiceProvider.

Иерархия симметричных алгоритмов показана на рис. 1.

Вы можете создавать экземпляры конкретных классов, приведенных на рис. 1 (например класса RijndaelManaged), и обращаться к их свойствам (рис. 2). В частности, на рис. 2 создается новый экземпляр класса алгоритма, который автоматически генерирует ключ и IV как массивы типа Byte, Пространство имен Cryptography в.NET Framework 13 которые потом сериализуются в значения типа String и выводятся на консоль. Заметьте, что свойства KeySize и BlockSize позволяют определять длину ключа и размер блока данных (в битах), который может быть зашифрован или расшифрован за одну операцию.

SymmetricAlgorithm

–  –  –

TripieDESCryptoServiceProvider Рис. 1. Симметричные алгоритмы Рис. 2. Применение класса RijndaelManaged Dim oEnc As New RljndaelManagedO Dift 1 As StMJrt Dift strKey, strlV As String For 1 * 1 To (oEnc.KeySize / 8) strKey &= o€nc.Key(i - t.ToStrlna & Next

–  –  –

Car»sole.«rlt8LlflestrIV) Console. *fiteUne(ooc.KeySlze.T^trina Console. WriteLineCoEnc. BlockSize. Tostring) Второй тип — асимметричное шифрование или шифрование с открытым ключом (далее — асимметричные алгоритмы); соответствующий класс является производным от абстрактного класса AsymmetricAlgorithm.

К общеизвестным асимметричным алгоритмам относятся Digital Signature Шифрование Algorithm (DSA) и RSA [название образовано от первых букв фамилий его создателей: Ron Rivest (Рон Ривест), Adi Shamir (Ади Шамир) и Len Adleman (Лен Эйдлман)]. Б асимметричных алгоритмах применяется пара ключей: один — закрытый, другой — открытый. Как правило, открытый ключ доступен кому угодно и используется отправителем для шифрования данных, тогда как закрытый ключ хранится в защищенном месте и применяется для расшифровки данных, зашифрованных с помощью открытого ключа. Наиболее распространен алгоритм RSA.

Эти алгоритмы в конечном счете являются производными от абстрактных классов DSA и RSA, в свою очередь производных от AsymmetricAlgorithm (рис. 3). Так как эти алгоритмы очень сложны, им нужны вспомогательные классы, производные, например, от AsymmetricKeyExchangeFormatter и AsyrametricSignatureFormatter, Asymmetric Algorithm

–  –  –

RSAOAEPKeyExchangeDeformatter DSASignatureDeformatter RSAPKCS1 KeyExchangeDeformatter RSAPKCSISignatureDeformatter Asymmetric Key Exchange Form alter AsymmetricSignatureFormatter RSAOAEPKeyExchangeFormatter DSASSignatu re Formatter RSAPKCSfKeyExchangeFormatter H5 RSAPKCSlSignatureFormatter Рис. З. Асимметричные алгоритмы Вы можете не только позволить исходному конструктору асимметричных алгоритмов сгенерировать пару ключей, но и извлечь уже существующую пару из хранилища, поддерживаемого CSR Для этого вы создаете экземпляр класса асимметричного алгоритма, указываете в качестве имени контейнера ключа хранилище Crypto API, заполняя нужные свойства объекта СspParameters, а затем предоставляете этот объект конструктору класса асимметричного алгоритма. Пример, иллюстрирующий, как сохранять и извлекать пары ключей, см, по ссылке http://www.gotdotnet.com/team/ cl r/about_security. aspx.

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

Этот алгоритм позволяет проверять, не были ли изменены данные. Когда вы посылаете вместе с данными дайджест, получатель может повторно вычислить его. Если на вход алгоритма хэширования поступают другие данные, на выходе он дает другой результат, и тогда отличие нового дайджеста от старого указывает на то, что данные изменены. Именно на основе таких алгоритмов обычно реализуются цифровые подписи Пространство имен Cryptography содержит базовый класс HashAlgorithm и производные классы, поддерживающие алгоритмы MD5, SHA1, SHA256, SHA384 и SHA512. Алгоритм MD5 дает 128-битный хэш, a SHA1 - 160битный. Числа в названиях других версий SHA-алгоритмов соответствуют длине создаваемых ими хэшеЙ. Чем больше хэш, тем надежнее алгоритм и тем труднее его взломать. Все эти алгоритмы реализованы в двух версиях: на основе управляемого и неуправляемого кода (рис. 4).

HashAlqorithm KeyedHashAlgorithm

–  –  –

Чтобы вычислить дайджест, нужно просто создать экземпляр класса алгоритма хэширования и вызвать его перегруженный метод ComputeHash, наследуемый от HashAlgorithm:

Dim fsData As New FileStream("mydata.txt", _ FileHode.Open, FileAccess.Read) Dim digest() As Byte Dim oSHA As New SHA512Managed( digest - oSHA.ComputeHash(fsData) fsKey.Close() Здесь методу ComputeHash передается объект Stream, но он принимает и байтовый массив.

В пространстве имен Cryptography также имеется абстрактный класс KeyedHashAlgorithm (рис. 4). Алгоритмы, реализованные в классах HMACSHA1 и MACTripleDES, производных от KeyedHashAlgorithm, позволяют генерировать Message Authentication Codes (MAC). С помощью MAC можно определить, были ли модифицированы данные, переданные по незащищенному каналу связи, — при условии, что и отправитель, и получатель используют общий секретный ключ.

Клиент не может напрямую создавать экземпляры абстрактных базовых классов SymmetricAlgorithm, Asymmetric Algorithm, HashAlgorithm и KeyedHashAlgorithm, но все они предоставляют перегруженный статический (общий) метод Create. Вызов этого метода без параметра приводит к созданию экземпляра алгоритма по умолчанию: RijndaelManaged (при симметричном шифровании), RSACryptoServiceProvider (при асимметричном шифровании), SHAlCryptoServiceProvider (при хэшировании) или HMACSHA1 (при хэшировании по ключу). Вторая версия метода принимает строку, указывающую конкретную реализацию (список реализаций см. в справочной системе.NET Framework). Аналогичным образом абстрактный класс каждого алгоритма, например SHA, тоже предоставляет перегруженный статический метод Create, который либо создает экземпляр реализации по умолчанию, либо принимает строковый идентификатор.

Так или иначе, но в конечном счете объект создается вызовом метода CreateFromName класса CryptoConfig с передачей ему строкового идентификатора. Например, если вы вызываете метод Create без параметра, классу SHAlCryptoServiceProvider передается строка по умолчанию «System.Security.Cryptography.HashAlgorithm».

Пространство имен Cryptography в.NET Framework 17

Чтобы продемонстрировать работу этих методов, рассмотрим код, в котором каждый оператор, создающий экземпляр класса RijndaelManaged, эквивалентен остальным операторам:

Dim r, г1, г2, гЗ As RijndaelManaged гЗ = CType(CryptoConfig.CreateFromName("Rijndael"), RijndaelManaged) г2 = CTypeCSymmetricAlgorithm.Create, flijndaelManaged) M = CType(Rijndael. Create, RijndaelManaged) г = New RijndaelManagedO Это несколько сбивает с толку, но классы разработаны таким образом, чтобы выбор конкретного алгоритма можно было отложить до периода выполнения или сразу указать при настройке системы. Фактически в общесистемный конфигурационный файл (machine.config) можно вставить раздел cryptoName Mapping, который позволяет переопределить сопоставления по умолчанию или добавить нужную запись в список сопоставлений, «зашитый» в код Mscorlib.dll. Например, если вы включите в этот файл XML-код, показанный на рис. 5, то реализацией хэширования по умолчанию станет MDSCryptoServiceProvider и, кроме того, будет добавлено новое сопоставление «dan».

Рис. 5. Настройка пространства имен Cryptography

iascorlib с rypt08raphySettlfigs cryptoKaHieMapping cryptQClasses «coryptoClass rayH05= "System. Security, Cryptog ra phy.RBSCryptoServieeProvlder, mscorUb, Ver=1. 0.2411.0, CuUure=neutral, PubliGKeyToken=b77a5c561934e089"/ /c ryptoClasses naraeEntry пагвевМая" class="fliyHD5*V fiante Entry/ na«eis"SyBteieI Security. Cryptography. HashAigorithtn /cryptoNaKeMapping /cryptograpliySettings Как видите, вставка элемента cryptoClass в элемент cryptoClasses позволяет определить новое ссылочное имя (в данном случае — myMDS), полностью описав нужные класс и сборку. Затем с помощью элементов nameEntry создаются новые сопоставления.

После этого можно писать такой клиентский код:

Шифрование Dim m, ml As MDSCryptoServlceProvider m = CType(HashAlgorithm.Create, MDSCryptoServiceProvider) ml = CType(CryptoConfig,CreateFroiriName("dan"), MDSCryptoServlceProvider) В обоих примерах создаются объекты типа MD5CryptoServiceProvider.

Использование криптографических сервисов.NET Framework поддерживает модель программирования на основе потоков (streams). Классы потоков, производные от System.lO.Stream, представляют данные из различных хранилищ (текстовых файлов, XML-документов, MSMQ-сообщений, памяти и сети) и позволяют считывать данные из соответствующих хранилищ или записывать в них. Не удивительно, что пространство имен Cryptography построено на такой концепции, ведь она дает элегантный и эффективный способ шифрования и дешифрования данных с применением уже рассмотренных алгоритмов. В основе этой функциональности лежит класс CryptoStream. производный от System.IO.Stream и служащий в качестве модели потоков при криптографических преобразованиях, Чтобы показать вам, как пользоваться базовой криптографической функциональностью в этом пространстве имен, я написал простой класс TextFileCrypto (рис. (). Он позволяет зашифровать и расшифровывать любой текстовый файл по алгоритму DES с ключом, который генерируется этим классом и сохраняется в файле.

Рис. 6. Базовая криптографическая функциональность с применением алгоритм^ DES Option Strict Or*

–  –  –

Рис. 6. Базовая криптографическая функциональность с применением алгоритма DES (продолжение) Public Sub New() mDES = Hew CESCryptoServiceProviderO End Sub

–  –  –

Private Sub OpenKeyFileO Oin fsKey As New FileStreasiCmstrKey, FileMode.Open, FtleAccess.Ftead) ' Открываем файл ключа и считываем из него ключ fsKey.ReadCmKey, 0, 8) fsKey,Read(ieIV, 0, 8) fsKey.Close()

–  –  –

Рис. 6. Базовая криптографическая функциональность с применением алгоритма DES (продолжение) ' Генерируем новый ключ и IV случайным образом и сохраняем в файле. Заметьте, что они будут сгенерированы ' автоматически, если вы сами не сделаете этого.

mOES.GerterateKeyO «DES.GeneratfilVO

–  –  –

Diai fslnput As Ne» FileStreaFiKeistrFile, „ FileHode.Qpen, FileAceess.Read) Oil» fsQutput As H&M FileStrearaC'tenJp.dat", _ FileHode. Create, FileAccessi. Write) f sQutput. Settengtft(O)

–  –  –

' Создав* DES Encryptor из этого зкаемпляра Bi» desEncr$t As ICryptoTrarisforni = mOES.

CreateEneryptor( ' Создаем CryptoStream, преобразующий ф&Йлоаый поток ' с применением DES-иифровамия Diffl sCrypfto As New CryptoStreani{fsGutpyt, desEncrypt, _ CryptoStreamMode.Write) arInput(Convert.ToInt32CfsInput.Lefigtti - t) fsInput,Bead(arlnputr Q, Convert. Tolnt32(fslnpjat. Length)) fsInput.CloseO * Записываем зашифрованный файл sCrypto.WritetarlnptFt, 0, arlnput. length) sCrypto.Close)

–  –  –

Рис.6. Базовая криптографическая функциональность с применением алгоритма DES (продолжение) ' Удаляем и переименовываем File. GopyC'temp. dat", nistrFile, True) File. Delete( " teiip. dat " ) End Function

–  –  –

' Создаем файловый поток для считывания зашифрованного файла Dim fsfiead As New FileStreaBBstrFlle, FileHode.Qpefl, _ Fileftocess.Read) Dim fsQtitput As New FileStreairt("terap.dat", _ FileHoele. Create, FileAeeess. Write)

• создаем DES Decryptor из нашего экземпляра des 81m desPecrypt As ICryptoTransforB =* siDES,CreateOecryptor( " Создаем кри?гго-поток, настроенный на чтение потока байтов * и их расшифровку по DES Die sCrypto As Hew CryptoStreajn(fsRead, desDecrypt, „ С ryptoSt reamHode. flead ) Oirai swWriter As Hew StreaBWriter(fsQutpyt) Dim srReatier As Hew StreaBfleader(sCrypto) ' Записываем расшифрованный файл swWriter. Write(srReader.FteadToEnd}

–  –  –

'End Class Как видите, конструктор класса создает экземпляр DESCryptoServiceProvider, который предоставляет методы CreateEncryptor и CreateDecryptor. Впоследствии эти методы используются для возврата объектов, выполняющих шифрование и дешифрование. Свойства KeyFile и FileName позволяют соответственно открыть существующий файл, содержащий ключ и IV, и указать имя файла, подлежащего шифрованию или дешифрованию. Если такой файл действительно есть, он открывается закрытым методом OpenKeyFile, после чего ключ и IV считываются в массивы типа Byte. Метод SaveKeyFile вызывает методы GenerateKey и GeneratelV нижележащего класса DES для генерации случайного ключа и IV. Потом эти значения сохраняются в файле, имя которого было передано как аргумент.

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

Заметьте, что алгоритм DES использует 56-битный (7-байтовый) ключ. DES поддерживает ключи лишь этой длины, но узнать допустимые размеры ключей для других алгоритмов можно через свойство LegalKeySizes, которое возвращает массив объектов KeySizo; их свойства MinSize и MaxSize сообщают минимальную и максимальную длину ключа. Например, алгоритм Rijndael поддерживает ключи размером 128, 192 и 256 битов. Для более стойкого шифрования можно указать больший размер (через свойство KeySize).

Сердцевина класса TextFileCrypto - методы EncryptFile и DecryptFile.

Первое, что делает метод EncryptFile, — открывает шифруемый файл и временный файл для хранения зашифрованных данных. Затем он создает шифрующий объект (encryptor object), вызывая метод CreateEncryptor провайдера сервиса (в нашем случае — DESCryptoServiceProvider). Этот объект реализует интерфейс ICryptoTransform и поэтому может быть передан конструктору CryptoStream, который преобразует данные по мере их записи в файл, управляемый FileScream (здесь — fsOutput). Исходный файл перезаписывается временным. В результате создается зашифрованПространство имей Cryptography в,NET Framework 23 ный файл, который можно безопасно передавать по Интернету через FTP или как почтовое вложение.

Метод DeeryptFile создает дешифрующий объект (decryptor) методом CreateDecryptor. Этот объект аналогичным образом передается конструктору CryptoStream для расшифровки данных из файла, считываемого FileStream-объектом fsRead. Наконец, расшифрованные данные сохраняются в текстовом файле классом Stream Writer, который использует при этом метод ReadToEnd класса Stream Reader.

Для шифрования данных с помощью класса TextFileCrypto в клиентской программе достаточно написать всего четыре строки кода:

Dim objCrypt As New TextFileCryptoO objCrypt.FileName = "students.txt" Генерируем и сохраняем ключ ob]Crypt.SaveKeyFile("mykey.dat") Шифруем файл objCrypt.EncryptFile()

Когда клиент получает зашифрованный файл, он может расшифровать его так:

Dim objCrypt As New TextFileCryptoO objCrypt.FileName = "students.txt" ' Считываем ключ objCrypt.KeyFile = "mykey.dat" ' Расшифровываем файл objCrypt. OecryptFileO Стойкое шифрование в Windows Хотя ограничения на экспорт средств стойкого шифрования сняты,.NET Framework не будет использовать стойкое шифрование для неуправляемых реализаций, если вы приобрели операционную систему в экспортной версии.

Чтобы обойти это препятствие, установите High Encryption Pack (он входит в Service Pack 2 для Windows 2000, который можно скачать по адресу http:// www.microsoft. com/ windows2000/downloads/recommended/encryption).

Для Windows NT 4.0 сервисные пакеты распространяются как в стандартной версии, так и в версии со стойким шифрованием. Если у вас еще не установлен такой сервисный пакет, скачайте Service Pack ба в версии со стойким шифрованием по а/фесу http://www.microsoft.com/ntserver/nts/ downloads/recommended/SP6/allSP6.asp).

24 Шифрование В случае Windows Me, Windows 98 и Windows 95 пакет High Encryption Pack устанавливается вместе с Microsoft Internet Explorer 5.5. Если ваша версия Internet Explorer ниже 5.5, скачайте соответствующий High Encryption Pack по адресу http://www.microsoft.com/windows/ie/download/128bit/ default.asp).

Дэн Фокс (Dan Fox) — директор компании Quilogy по технологиям (Оверленд-Парк, штат Канзас). Автор книг «Pure Visual Basic» (Sams, 1999) и «Building Distributed Application with Visual Basic.NET» (Sams, 2002), а также соавтор «Exam Cram: Visual Basic 6» (The Coriolis Group, 1999).

Вы можете связаться с ним по адресу dfox@quilogy.com.

Джеймс Мак-Каффри Шифрование Защита данных с помощью Advanced Encryption Standard* Advanced Encryption Standard (AES) — спецификация шифрования электронных данных, предложенная Национальным институтом стандартов и технологий (National Institute of Standards and Technology). Ожидается, что она найдет широкое применение в шифровании цифровых данных, в частности финансовой, телекоммуникационной и правительственной информации. В статье дан обзор AES и применяемых алгоритмов, а также приведены полная реализация AES на С# и примеры шифрования.NETданных. Прочитав эту статью, вы научитесь шифровать данные с помощью AES, тестировать программное обеспечение с поддержкой AES и использовать такое шифрование в своих системах.

Национальный институт стандартов и технологий (National Institute of Standards and Technology, NIST) выпустил 26 мая 2002 г. новую спецификацию AES (Advanced Encryption Standard). В этой статье я продемонстрирую работающую реализацию AES на СП и подробно объясню, что такое AES и как работает код ее реализации. Я покажу, как шифровать данные с помощью AES и как расширить приведенный в статье код, чтобы создать AES-класс, пригодный для коммерческого применения. Кроме того, мы поговорим о том, как добавить шифрование AES в программные системы и зачем это нужно, а также как тестировать программное обеспечение, использующее AES.

Публиковалось в MSDN Magazine/Русская Редакция. 2003. № И (ноябрь). — Прим. изд.

Шифрование Заметьте, что исходный код, приведенный в этой статье, и любые реализации, основанные на материалах данной статьи, подпадают под законодательство, регулирующее экспорт криптографических модулей (подробную нормативную информацию см. по ссылке http://www.bxa.doc.gov/Encryption).

AES — новый криптографический алгоритм для защиты электронных данных. Точнее, AES — это итеративный блочный шифр с симметричным ключом, который использует 128-, 192- и 256-битные ключи и шифрует/дешифрует данные блоками по 128 битов (16 байтов). В отличие от шифров с открытым ключом, использующих пару ключей, шифры с симметричным ключом применяют для шифрования и дешифрования данных один и тот же ключ. Зашифрованные данные, возвращаемые блочным шифром, содержат ровно столько же битов, сколько и входные данные. При итеративном шифровании выполняется цикл, на каждой итерации которого над входными данными выполняются операции перестановки (пермутаиии) и замены.

На рис. 1 показан пример использования AES: 1б-байтный блок данных шифруется 192-битным ключом, а затем дешифруется.

С: '.AESiAe5DemoCQn$a!e\bini,Deb«e\

–  –  –

Рис. 1. Шифрование/дешифрование данных Предшественником AES является старый стандарт DES (Data Encryption Standard). DES утвердили в качестве федерального стандарта в 1977 г. Он считался пригодным для использования до 1998 г., когда оказалось, что с помощью новейших достижений аппаратно-программного обеспечения и криптографического анализа можно расшифровать сообщение, зашифрованное по алгоритму DES, за 50 часов. С тех пор начались многочисленные успешные атаки на данные, зашифрованные по алгоритму DES. Поэтому в настоящее время DES считается устаревшим, Защита данных с помощью Advanced Encryption Standard В конце 1999 г. NTST предложил использовать в новом стандарте алгоритм Rijndael (произносится как «rain doll»), разработанный исследователями Джоан Демен (Joan Daemen) и Винсентом Риджменом (Vincent Rijmen), как в наибольшей степени отвечающий критериям безопасности, эффективности реализации, гибкости и простоты. Термины AES и Rijndael иногда употребляются как синонимы, но это разные понятия. Ожидается, что AES станет стандартом де-факто в шифровании всех видов электронных данных, в том числе используемых коммерческими приложениями (например для банковских и финансовых транзакций), в телекоммуникациях, при передаче частной и правительственной информации.

Обзор алгоритма AES Алгоритм AES основан на перестановках (permutations) и заменах (substitutions). Перестановка — это изменение порядка данных, а замена замещение одного блока данных другим. В AES используется несколько видов перестановок и замен. Чтобы в них разобраться, рассмотрим конкретный пример — шифрование по алгоритму AES данных, показанное на рис. 1.

Здесь шифруется следующее 128-битное значение (во второй строке показаны индексы массива):

–  –  –

При вызове конструктора AES-класса инициализируются две таблицы, используемые методом шифрования. Первая таблица — матрица замен Sbox размером 16x16. Первые пять строк и столбцов Sbox показаны на рис. 2.

Кроме того, в процедуре шифрования по массиву байтов ключа формируется «таблица ключей» («key schedule») w[], показанная на рис. 3.

–  –  –

Рис. 4. State Рис. З. Таблица ключей Первые Nk (в данном случае Nk = 6) строк w[] заполняются значением исходного ключа (от 0x00 до 0x17), а остальные строки генерируются по исходному ключу (seed key). Переменная Nk — это размер исходного ключа в 32-битных словах. В дальнейшем, когда я буду рассказывать о реализации AES, вы увидите, как именно генерируются элементы w[]. Дело в том, что в AES применяется несколько ключей вместо одного. Эти новые ключи называются итеративными (round keys), чтобы подчеркнуть их отличие от исходного ключа, задаваемого при вызове процедуры шифрования.

Сначала подпрограмма шифрования по алгоритму AES копирует 16-байтный входной массив в матрицу State размером 4x4 (рис. 4). Процедура шифрования называется Cipher. Она работает с матрицей State[] и описывается псевдокодом на рис. 5.

Рис. 5, Псевдокод Cipher.Clpe&rbyte[3 iniMit, byte[} output) byte[4,4| State;

Копирование inputH s 8tate[] AtHFtoundKey for (round = 1; round Hr-1; ++round) { SubBytes Shiftfiows MixColumrts AddRoundKey } SubBytes Shift flows Копирование Stated в output[] Защита данных с помощью Advanced Encryption Standard 29 Алгоритм шифрования выполняет операцию предварительной обработки, которая в спецификации называется AddRoundKey. AddRoundKey выполняет над каждым байтом входной матрицы State операцию XOR с первыми четырьмя строками таблицы ключей. Байту State[r,c] присваивается результат операции XOR с элементом таблицы ключей w[c,r], т. е. State[r,c] = State[r,c] XOR w[c,r].

Например, если первая строка матрицы State содержит байты { 00, 44, 88, ее }, а первый столбец таблицы ключей имеет вид { 00,04, 08, Ос }, то новым значением State[0,2] будет 0x80 — результат операции XOR над State[0,2] (0x88) и w[2,0] (0x08):

0 0 0 0 1 0 0 0 XOR В основном цикле алгоритма шифрования AES выполняются четыре операции над матрицей State, которые в спецификации называются SubBytes, ShiftRows, MixColumns и AddRoundKey. Операция AddRoundKey — то же, что и предварительная операция AddRoundKey с тем исключением, что при каждом вызове AddRoundKey используются следующие четыре строки таблицы ключей. Подпрограмма SubBytes выполняет операцию замены: замещает каждый байт матрицы State новым байтом, определяемым по таблице Sbox. Например, пусть значением State[0,l] является 0x40 и требуется найти его замену. Берется значение State[0,l] (0x40), и переменной х присваивается его левая цифра (4), а переменной у — правая (0). Затем по индексам х и у из таблицы Sbox берется значение замены (рис. 2).

ShiftRows — это операция перестановки, при которой байты матрицы State циклически сдвигаются влево. На рис. 6 показано, как ShiftRows обрабатывает State[]. Строка 0 матрицы State циклически сдвигается на 0 позиций влево, строка 1 — на одну позицию влево, строка 2 — на две позиции влево, а строка 3 — на три позиции влево.

Рис. 6. Обработка State операцией ShiftRows

–  –  –

multiplications), применяемых к элементам столбца, который содержит этот байт.

Я расскажу о сложении и умножении элементов полей в следующем разделе, Допустим, значением State[0,l] является 0x09, а остальные элементы столбца 1 — 0x60, Oxel и 0x04; тогда новым значением State[0,l] будет результат следующего выражения:

–  –  –

Здесь сложение и умножение — это специальные математические операции над элементами поля, а не обычное целочисленное сложение и умножение.

Четыре подпрограммы SubBytes, ShiftRows, MixColumns и AddRoundKey вызываются в цикле, выполняемом Nr - 1 раз, где Mr — число итераций для ключа данного размера, Количество итераций алгоритма шифрования равно 10, 12 или 14 в зависимости от размера исходного ключа (128, 192 или 256 бит). В нашем примере Nr равно 12, поэтому четыре операции вызываются 11 раз. По завершении цикла алгоритм шифрования еще раз вызывает SubBytes, ShiftRows и AddRoundKey, а затем копирует матрицу State в выходной параметр.

Подведем итог: ядро алгоритма AES-шифрования образуют четыре операции. AddRoundKey заменяет группы из 4 байтов, комбинируя их с итеративными ключами, которые генерируются по значению исходного ключа.

SubBytes замещает отдельные байты в соответствии с таблицей замен.

ShiftRows переставляет группы из 4 байтов, циклически сдвигая 4-байтовые строки. MixColumns заменяет байты результатами операций сложения и умножения элементов поля.

Сложение и умножение в поле GF(28) Как видите, алгоритм шифрования AES использует достаточно простые операции перестановки и замены, если не считать процедуры MixColumns.

В MixColumns применяются специальные операции сложения и умножения. Сложение и умножение в AES основаны на математической теории полей. А конкретнее, в AES используется поле GF(2 ).

Защита данных с помощью Advanced Encryption Standard 31 Поле GF(2 ) состоит из 256 значений от 0x00 до Oxff, над которыми опреН делены операции сложения и умножения. Отсюда (2 ) в названии. Поле GF (Galois Field) названо в честь Галуа, математика, основавшего теорию полей. Одной из характеристик GF(2*) является то, что результат сложения или умножения всегда принадлежит множеству {0x00... Oxff). Теория полей достаточно сложна, но применительно к сложению в GF(2 ) полуH чается простой конечный результат: сложение в GF(2 ) — это обыкновенная операция XOR.

W

Однако умножение в GF(2 ) — более сложная операция. Как вы в дальнейшем увидите в реализации на С#, в процедурах шифрования и дешифрования алгоритма AES используется умножение только на семь констант:

0x01, 0x02, 0x03, 0x09, OxOb, OxOd и ОхОе. Поэтому вместо объяснения общей теории умножения в GF(2 ) я ограничусь рассмотрением этих семи частных случаев.

Умножение на 0x01 в GF(2 ) занимает особое место; оно соответствует умножению на 1 в обычной арифметике и выполняется точно так же: при умножении любого значения на 0x01 получается то же самое значение.

Теперь рассмотрим умножение на 0x02. Как и в случае сложения, теория трудна, зато результат сравнительно прост. Если умножаемое значение меньше 0x80, оно сдвигается влево на 1 бит. Если же умножаемое значение больше или равно 0x80, оно сначала сдвигается влево на 1 бит, а затем к результату сдвига применяется операция XOR со значением Oxlb. Тем самым предотвращается «переполнение поля», и результат умножения остается в допустимом диапазоне.

Освоив операции сложения и умножения на 0x02 в поле GF(28), вы можете выразить через них умножение на любую константу. При умножение на 0x03 в GF(2H) значение 0x03 можно представить как сумму степеней числа 2. Чтобы умножить произвольный байт b на 0x03, представьте 0x03 как 0x02 + 0x01.

Следовательно:

–  –  –

Эту операцию можно выполнить, зная, как умножать на 0x02 и 0x01 и как складывать.

Аналогично умножение произвольного байта на OxOd сводится к следующим операциям:

–  –  –

Другие операции умножения, необходимые процедуре MixColumns при шифровании и дешифровании по алгоритму AES, выполняются по тому же универсальному шаблону:

b * 0x09 = Ь * (0x08 + 0x01) = (Ь * 0x02 * 0x02 * 0x02) + (Ь * 0x01)

–  –  –

Ь * ОхОе = Ь * (0x08 + 0x04 + 0x02) = (Ь * 0x02 * 0x02 * 0x02) + {Ь * 0x02 * 0x02) + (Ь • 0x02) Подведем итог. Сложение в GF(2 ) — это операция XOR. Умножение в GF(2 ) сводится к операциям сложения и умножения на 0x02, а умножение на 0x02 — это сдвиг на 1 бит влево, выполняемый в зависимости от условия. Дополнительную информацию об операциях в GF(2 ) см. в спецификации AES.

Расширение ключа В алгоритме шифрования и дешифрования AES используется таблица ключей, генерируемая по массиву байтов исходного ключа. В спецификации AES это называется процедурой KeyExpansion. Генерация фактически нескольких ключей по начальному ключу обеспечивает лучшее перемешивание битов по сравнению с использованием одного ключа. Нельзя сказать, что разобраться в KeyExpansion так уж трудно, но тем не менее это одна из самых сложных частей алгоритма AES.

На высокоуровневом псевдокоде процедура KeyExpansion выглядит так:

KeyExpansion(byte[] key, byte[][4] w) Копирование исходного ключа в первые строки w

–  –  –

Процедура «создание новой строки по двум предыдущим» использует две подпрограммы, Rot Word и Sub Word, а также таблицу констант Rcon (аббревиатура от round constants — итеративные константы). Рассмотрим каждый из этих трех элементов, а потом вернемся к подпрограмме KeyExpansion в целом.

Защита данных с помощью Advanced Encryption Standard Подпрограмма RotWord весьма проста. Она принимает массив из 4 байтов и циклически сдвигает их на 1 позицию влево. В таблице ключей w[] четыре столбца, и RotWord сдвигает заданную строку w[] влево. Заметьте: функция RotWord, вызываемая KeyExpansion, очень похожа на подпрограмму ShiftRows, используемую алгоритмом шифрования, с тем исключением, что применяется к одной строке таблицы ключей w[], а не ко всей таблице состояния State[].

Подпрограмма Sub Word выполняет побайтовую замену заданной строки таблицы ключей w[] в соответствии с Sbox. Замена в KeyExpansion выполняется так же, как и в алгоритме шифрования. Входной байт, который замещается, разбивается на пару (х,у), задающую индексы в таблице замен Sbox. Например, замена 0x27 дает х = 2 и у = 7, a Sbox[2,7] возвращает Охсс.

Подпрограмма KeyExpansion использует массив Rcon[], называемый таблицей итеративных констант. Каждая из этих констант содержит 4 байта, соответствующие строке таблицы ключей. В AES-подпрограмме KeyExpansion используется 11 итеративных констант (рис. 7).

Рис. 7. Инициализация Rcon private void BuildRconO

–  –  –

Левый байт каждой итеративной константы — это степень числа 2 в поле GF(28). Еще один способ вычисления значений этого байта — умножать каждое предыдущее значение на 0x02 в соответствии с правилами умножения в поле GF(28), приведенным в предыдущем разделе.

Заметьте:

0x80 х 0x02 = 0x36, так как 0x80 сдвигается влево на 1, а затем над результатом сдвига выполняется XOR с Oxlb.

–  –  –

Теперь более пристально рассмотрим цикл подпрограммы KeyExpansion.

Если детализировать показанный выше псевдокод, этот цикл приобретет следующий вид:

for (row = Nk; row (4 * Nr+1); f+row) { temp = w[row-1]

–  –  –

w[row] = w[row-Uk] xor temp } Если пока отвлечься от операторов if, то видно, что каждая строка таблицы ключей vv[] — результат XOR предыдущей строки со строкой, находящейся на Nk (4, б или 8 в зависимости от размера ключа) строк выше. Первое условие if означает, что к каждой четвертой, шестой или восьмой строке (в зависимости от размера ключа — 128,192 или 256 битов) применяются подпрограммы SubWbrd, Rot Word, а затем — операция XOR с итеративной константой. Второе условие означает, что в случае 256-битного ключа изменяются строки 12, 20, 28 и так далее через восемь строк. Это делается для того, чтобы в таблице ключей содержались более разнообразные значения.

Посмотрим, как KeyExpansion приступает к обработке ключа, показанного в примере в начале статьи.

Исходный ключ — 192-битное значение (6 слов):

–  –  –

В таблице ключей w[] 4 столбца и Nb x (Nr + 1) = 4 х (12 + 1) = 52 строки.

Подпрограмма KeyExpansion копирует значения исходного ключа в первые строки таблицы ключей w[], содержащей байты. Поскольку исходный ключ содержит 192 бита (24 байта), а таблица w[] всегда содержит 4 столбца, в данном случае KeyExpansion копирует исходный ключ в первые 6 строк w[].

Как KeyExpansion заполняет остальные строки таблицы ключей? В данном примере первой вычисляемой строкой является строка 6, поскольку строки от 0 до 5 заполнены значениями исходного ключа:

temp = w[row-1] = 14 15 16 17

Условие (row % Nk == 0) истинно, поэтому к строке применяется подпрограмма Rot Word:

temp = 15 16 17 14 Защита данных с помощью Advanced Encryption Standard 35

Затем применяется подпрограмма Sub Word:

temp = 59 47 fO fa

Далее выполняется XOR с Rcon[row / Nk] - Rcon[6 / 6] = 01 00 00 00:

temp = 58 47 fO fa

Наконец, выполняется XOR с w[row-Nk] = w[6-6] = 00 01 02 03, и в результате получается строка:

w[6] = 58 46 f2 f9 Этот процесс повторяется для остальных строк таблицы ключей \v[].

Подведем итог. В шифровании и дешифровании AES важное место занимает генерация нескольких итеративных ключей по исходному ключу. Алгоритм KeyExpansion генерирует таблицу ключей, используя операции замены и перестановки, во многих отношениях аналогичные операциям, выполняемым алгоритмами шифрования и дешифрования. 1 Конструктор AES-класса на С# Итак, мы рассмотрели все компоненты алгоритма шифрования AES, и теперь реализуем этот алгоритм на С#. Официальная спецификация алгоритма AES содержится в документе Federal Information Processing Standards Publication 197. Я решил, что моя реализация должна соответствовать ей максимально точно, но вскоре обнаружил, что спецификация скорее теоретический документ, чем руководство по реализации. Для удобства я предпочел использовать те же имена переменных, что и в опубликованном стандарте (даже такие загадочные, как «Nr и «w»).

В моей реализации девять полей данных и один перечислимый тип:

public enum KeySize { Bits128, Bits192, Bits256 };

private int Nb;

private int Nk;

private int Nr;

private byte[] key;

private byte[,3 Sbox;

private byte[,] iSbox;

private byte[,] w;

private byte[,] Rcon;

private byte[,] State;

–  –  –

Как правило, в спецификации в качестве единицы хранения данных исполвзуются байты, но имеется два важных поля, задающих размер в 4байтовых словах. Члены Nb и Nk содержат соответственно размер блока в словах и размер ключа в словах. Кг задает количество циклов. Размер блока — всегда 16 байтов (или 128 битов, т. е. 4 слова в терминологии AES), так что можно было бы объявить его константой. Размеру ключа присваивается значение 4, 6 или 8 в зависимости от значения перечислимого параметра KeySize. Чтобы усложнить зашифрованные данные, алгоритм AES содержит цикл, выполняемый заданное число раз — 10, 12 или 14. Эти значения выбраны в соответствии с теорией криптографического анализа. Количество циклов напрямую зависит от размера ключа.

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

Aes a = new Аез(размер ключа, исходный ключ)

Подпрограммы шифрования и дешифрования вызываются так:

а.Clpher(plainText, cipherText);

a.InvCipher(cipherText, decipheredText);

Я выбрал слегка неуклюжие имена методов Cipher и InvCipher, поскольку они используются в спецификации AES. Вот как выглядит код конструктора AES-класса:

public Aes(KeySize keySize, byte[] keyBytes) { SetNbNkNr(keySize);

this.key = new byte[this.Nk * 4];

keyBytes.CopyTo(this.key, 0);

BuildSboxO;

BuildlnvSboxO;

BuildRconO;

KeyExpansion();

Сначала конструктор присваивает значения полям Nb, Nk и Nr, вызывая вспомогательный метод SetNbNkNr, показанный на рис. 8. Если вас очень волнует эффективность, поместите этот код прямо в конструктор, чтобы избавиться от издержек, связанных с вызовом метода.

Защита данных с помощью Advanced Encryption Standard Рис. 8. Метод SetNbNkNr private void SetNbNkNr(KeySize keySize)

–  –  –

Затем байты, передаваемые конструктору, копируются в поле класса.

Ключ объявлен как поле класса и инициализируется операторами:

this,key = new byte[this.Nk - 4];

keyBytes.CopyTo(this. key, 0);

Для инициализации таблиц замен Sbox[] и iSbox[] я решил вызывать в конструкторе закрытые вспомогательные методы BuildSbox и BuildlnvSbox. Sbox[] и iSbox[] необходимы подпрограмме расширения ключа и методам Cipher и InvCipher соответственно, поэтому можно было бы поместить инициализацию Sbox[| и вызов метода Key Expansion в методы Cipher и InvCipher. Но код, который выполняет эти операции в конструкторе, выглядит понятнее. Как заполняется sBox[], показано на рис. 9. Таблица iSbox[] заполняется аналогично. Для удобства чтения код структурирован.

В дальнейшем вы увидите весьма интересный альтернативный способ заполнения таблиц Sbox и iSbox.

–  –  –

Вспомните: левый байт каждой строки Rcon[] — это степень 2 в поле

GF(2H), следовательно, эту таблицу можно заполнить, выполнив вычисления вида:

newVal = prevVal * 0x02;

Защита данных с помощью Advanced Encryption Standard В конце конструктора AES-класса вызывается метод Key Expansion, формирующий таблицу ключей w[] (рис. 10). Код метода довольно прост.

В спецификации используется гипотетический тип данных — 4-байтное слово. В С# такого типа нет, поэтому я смоделировал его как массив из 4 байтов. Сначала таблице \v[] выделяется память оператором new.

Затем первые Nk (4, 6 или 8) строк w[] заполняются значениями массива key[], передаваемого конструктору:

this.w[row,0] = this.key[4*row];

this.w[row,1] = this.key[4*row+1];

this.w[row,2] = this.key[4«row+2];

this.w[row,3] = this.key[4*row+3];

Рис. 10. Метод KeyExpansion private void KeyExpansionO this.и = new byt;e[Nb * (Nr+1), 43;

–  –  –

this. v*t row, 0] = tftis.keyE4*row};

this.w[row,1] ж this.key4*rowH3;

tftls.w[row,23 - this,key(4»row+2];

this.w[row,3] = thls.key{4*row+31;

Syte[] teiap = new byte[4];

–  –  –

tenptO] * (byte К (int)tespE03 ** Cint)this.BconErow/Nk, 0] );

teraptl3 * (byte)( (int)te«tpC13 " Cint)this,Rcontrow/Nk,l3 );

terap[2] * (byteK (int)temp[2] ~ (int)this.Rqon[row/Nk,2] );

teapfS] * (byteX (int)teffifjE33 " (lRt)tfiis.ReonErow/Nk,3] );

–  –  –

Рис. 10. Метод KeyExpansion = w[row-Nk] xor this.w[row,03 = (byte) ( (int)this.w[row-Nk,OJ " CinttespEO] );

this,w[row,1} = (byte) ( (iirt}this.w[rQw-Nk,13 " (tnt)teinp[13 );

this.w[row,2] = (bytej ( (irrt)this.wErow-Nk, 2] " (int)tefflp[2] );

this.w[row,S] - (byte) С (ItttJthis.wErow-Nk.Sj * (int)temp[33 };

–  –  –

В этом коде часто встречается операция XOR над парами байтов. Так как в языке С# не определен оператор А (XOR) для типа byte, приходится приводить byte к int, а результат — обратно к byte.

Например, приходится использовать:

temp[0] = (byte)( (int)tempEQ] " (lnt)this.Rcon[row/Nk,0] );

вместо:

temp[0] = temp[0] " tfiis.Rcon[row/Nk,0];

При выполнении условия метод Key Expansion вызывает закрытые методы SubWord и RotWord. Выбор имен методов объясняется тем, что такие имена используются в спецификации. И в этом случае, поскольку в С# нет типа word, я моделирую его массивом из четырех байтов. Код методов SubWord и RotWord весьма прост, и вы с легкостью разберетесь в нем. Он содержится в исходном коде AesLib, который можно скачать по ссылке http://msdn.microsoft.com/msdrimag/code03.aspx в разделе за ноябрь.

Более сложной операцией является поиск значения замены в процедуре SubWord. Вспомните: чтобы найти замену, входной байт разбивается на левые 4 бита и правые 4 бита. То есть индекс х получается сдвигом байта на 4 бита вправо оператором ». а индекс у — выполнением логического оператора AND со значением 00001111.

Если записать код в менее лаконичном, но более удобном для чтения виде, чем в моем примере, он будет выглядеть так:

int х = word[0] » 4;

int у = word[0] & OxOf;

byte substitute = this.Sbox[x,y];

result[0] = substitute;

а я использовал следующий код:

result[0] = this.Sbox[ word[0] » 4, word[0] & OxOf ];

Защита данных с помощью Advanced Encryption Standard Подведем итог. Конструктор AES-класса принимает размер ключа (128, 192 или 256 бит) и массив байтов, содержащий исходный ключ. Затем присваивает значения размеру входного блока, размеру исходного ключа и количеству итераций алгоритма шифрования, а потом копирует исходный ключ в поле key. Кроме того, конструктор формирует четыре таблицы: две таблицы замен, используемые методами шифрования и дешифрования, таблицу итеративных констант и таблицу ключей, содержащую итеративные ключи.

AES-метод Cipher на С# Код метода Cipher показан на рис. 11. Он очень прост, поскольку за него работают закрытые методы AddRoundKey, SubBytes, ShiftRows и MixColumns.

Рис, 11. Метод Cipher public void Cipher(uyteH input, byte[] output} { // state s input this. State = new bytet+.Hfr};

for (int 1 = 0; 1 (4 * Nb); ++1) { this. Stated * 4, i / 43 = input ti];

AddfloundKey(O);

–  –  –

Сначала метод Cipher копирует входной массив текста в матрицу состояния State[]. Далее метод Cipher первый раз вызывает метод AddRoundKey, после чего выполняет цикл, количество итераций которого на 1 меньше общего числа итераций. Потом выполняется еще одна итерация, на которой, согласно спецификации, не вызывается метод MixColumns.

Код закрытых методов AddRoundKey и SubBytes показан на рис. 12. Методу AddRoundKey нужен номер выполняемой итерации, чтобы определить, какие четыре строки таблицы ключей wf] следует использовать. Заметьте, что к State[r,c] применяется операция XOR с w[c,r], а не с w[r,c]. Метод SubBytes извлекает индексы из входного байта с помощью сдвига вправо на 4 бита и операции AND с OxOf, т. е. так же, как и метод KeyExpansion.

Рис. 12. Методы AddRoundKey id SubBytes

–  –  –

Код метода SlnftRows приведен на рис. 13. Вспомните: ShiftRows (который правильнее было бы назвать RotateRows) циклически сдвигает row[0] на 0 позиций влево, row[l] — на 1 позицию влево и т. д.

Защита данных с помощью Advanced Encryption Standard Рис. 13, Метод ShiftRows private void ShiftRo«s()

–  –  –

Чтобы перейти через конец строки к началу, используется оператор %.

Метод MixColumns (рис. 14) заменяет каждый байт линейной комбинацией всех других значений столбца, используя сложение и умножение в

GF(28). Из теории полей следует, что при умножении должны использоваться постоянные коэффициенты, равные 0x01, 0x02 или 0x03. Замена заданного столбца определяется следующим образом:

State[0,c] = 0x02 " State[0,c] + 0x03 * State[1,c] + 0x01 * State[2,c] + 0x01 * State[3,c] State[1,c] = 0x01 * State[0,c] + 0x02 * Stated.c] + 0x03 * State[2,c] + 0x01 * State[3,c] State[2,c] = 0x01 * State[0,c] + 0x01 * State[1,c] + 0x02 * State[2,c] + 0x03 * State[3,c] State[3,c] = 0x03 * State[0,c] + 0x01 * Stated,c] + 0x01 * State[2,c] + 0x02 * State[3.c] Эти выражения довольно громоздки, поэтому я решил написать закрытые вспомогательные функции, вычисляющие произведения на 0x01, 0x02 и 0x03 в поле GF(28). Вспомогательные функции очень короткие.

Например, код, умножающий в поле байт b на 0x03, выглядит так:

–  –  –

Как я уже говорил, любое умножение в поле GF(28) можно свести к умножению на константу 0x02 и сложению. Я назвал метод, умножающий на 0x02, gfmultby02, отступив от принципа использовать те же имена методов, что и в спецификации, где эта подпрограмма называлась xtime.

Метод Cipher выполняет цикл, где к входным данным применяются четыре операции. В результате формируются зашифрованные выходные данные. AddRoundKey заменяет байты, используя несколько итеративных ключей, получаемых из исходного ключа. SubBytes заменяет байты в соответствии со значениями в таблице замен. ShifcRows переставляет байты, сдвигая строки матрицы, a MixColumns заменяет байты, применяя операции сложения и умножения в поле к элементам столбцов.

Защита данных с помощью Advanced Encryption Standard 45 AES-метод InvCipher на С# Основной принцип алгоритма дешифрования AES несложен. Чтобы расшифровать зашифрованный блок, нужно просто выполнить в обратном порядке операции, противоположные соответствующим операциям шифрования. Таков общий принцип, но имеются и некоторые частности.

В спецификации AES подпрограмма дешифрования называется InvCipher, а не Decipher или Decrypt. Это связано с тем, что в математическом описании AES используются обратные операции.

Сравнив код InvCipher с кодом Cipher, вы увидите, что многое в нем соответствует вашим ожиданиям, но есть два исключения. Во-первых, порядок вызовов обратных методов (таких как InvSubBytes) в InvCipher не совсем обратен порядку вызовов соответствующих методов (вроде SubBytes) в Cipher. Во-вторых, InvCipher вызывает AddRoundKey, а не InvAddRoundКеу. Заметьте: алгоритм InvCipher также использует таблицу ключей, но начинает обработку с самого большого индекса и в цикле уменьшает индекс до нулевого значения.

Код методов InvSubBytes, InvShiftRows и InvMixColumns почти обратен коду методов SubBytes, ShiftRows и MixColumns. Код метода InvSubBytes почти совпадает с кодом метода SubBytes за исключением того, что используется таблица обратных замен iSbox[] вместо таблицы Sbox[].

Легко догадаться, что с помощью iSbox[] просто отменяется преобразование, выполняемое на основе Sbox[]. Например, если байт b равен 0x20, его заменой в таблице Sbox[] является ОхЬ7. А заменой значения ОхЬ7 в iSbox[] является 0x20.

Аналогично метод InvShiftRows отменяет операцию метода ShiftRows:

строка [0] сдвигается на 0 позиций вправо, строка [1] — на 1 позицию вправо, строка [2] — на 2 позиции вправо и строка [3] — на 3 позиции вправо.

Метод InvMixColumns отменяет операцию метода MixColumns, но не настолько очевидным способом. Вспомните: MixColumns заменяет каждый байт матрицы состояния линейной комбинацией байтов исходного столбца и коэффициентов 0x01. 0x02 и 0x03. Здесь снова применяется теория полей.

Оказывается, что обратная операция выглядит аналогично, но выполняет умножение на ОхОЭ, OxOb, OxOd и ОхОе:

\ State[0,c] = ОхОе * State[0,c] + OxOb * State[1,c] + OxOd * State[2,c] + 0x09 * State[3.c] + Stated,c] = 0x09 * State[0,c] + OxOe * Stated,c] OxOb * State[2,c] + OxOd * State[3,c] Шифрование State[2,c] = OxOd * State[0,c] + 0x09 * State[1,c] + OxOe * State[2,c] + OxOb * State[3,c] State[3,c] = OxOb * State[0,c] + OxOd - State[1,c] + 0x09 - State[2,c] + OxOe • State[3,c] Как и в случае метода MixColumns, я решил написать вспомогательные функции, а не развертывать выражения прямо в коде или создавать универсальную вспомогательную функцию умножения. Давайте я покажу, как написать функцию, умножающую произвольный байт b на константу ОхОе (14 в десятичной системе). Число 14, как и любое другое, можно разложить по степеням 2. 14 = 2 + 4 + 8. Поскольку 4 — это 2 в квадрате, а 8 — 2 в кубе, 14 можно представить как 2 + 22 + 23. С учетом того, что сложение в GF(2H) — просто XOR ( А ), а функция gfmuliby02 уже написана, я могу получить результат с помощью следующего оператора:

return (byte) (int)gfmultby02(gfmultby02(gf(nultby02Cb))) ~ /* 23 + */ /* 2г + */ (Int)gfmultby02(gfnnjltby02(b)) " (int)gfmultby02(b) ); /• 2 */ Все операции, используемые алгоритмом шифрования AES, являются обратимыми, поэтому алгоритм дешифрования фактически выполняет операции, обратные тем, которые выполняются при шифровании.

Использование AES-класса Одно из преимуществ реализации AES на языке С# — простота. Рассмотрим код на рис. 15, который формирует вывод, показанный на рис. 1. Сначала объявляются «зашитые» в код значения 16-байтного входного текста и 24-байтного (192-битного) исходного ключа, затем инициализируется AES-объект, далее метод Cipher формирует по входному тексту зашифрованный текст, а метод InvCipher дешифрует зашифрованный текст. Все очень просто и понятно.

Рис. 15. Применение AES static void Main(stringU args) { bytetl plaintext * new byte[j {0x00, 0x11, 0x22, 0x33, flx44, 0x56, 0x66, 0x77,0x88, 0x99, Qxaa, Gxbb, Oxcc, Oxdd, Gxee, Oxff};

byt&[3 eipherText = new byte3 decipherstfText = m* bytettSJ;

bytetJ keyBytes = new byte[] {0x00, 0x01, 0x02, 0x03, 0x04, 0x05,

–  –  –

Рис. 15. Применение AES 0x06, 0x07,0x08, ОхО&, ОхОа, GxOb, OxOe, QxOd, OxOe, OxGf,Qx10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x18, 0x17};

Aes a = new Aes(Aes.KeySi2e.8ttsl92, keyBytes);

Console.WriteLine("\nAdvanced Encryption System Demo in.ЯЕТ"};

CoRsole.WriteLine("\n^e plaintext is; "};

BisplayAs8ytes(plainText);

Console.WriteLineC'ViLfstng a " + Aes.KeySize.Bits192.ToString() + "-key of: ");

DisplayAsBytes(keyBytes);

a.Cipher(plainText, cipherText);

Console.WriteLlne("\nBie resulting ciphertext is; ";.

0isplayAsBytes{eipherText);

a.IflvCipher(cipherText, deciptiaredText);

Console.WritelirteC"\nAfter deciphering the ciptiertext, the result is; ");

0isplayAsBytes(ttecipheredText);

Console,WriteLine("\nDone");

Console, ReadlineO;

} // NainO static void DisplayAsBytes(byteE3 bytes) { for (Int i * 0; i bytes.Length; ++i) { Console.Write(bytesCi].ToStrlng("x2") + " " );

if ( 0 44 i X 16 == 0) Console.write("\n";

} Console.WriteliReC"");

} // DisplayAs8ytes()

–  –  –

Рис. 16. Пример программы шифрования

Поскольку размер ключа нужен и процедуре шифрования, и процедуре дешифрования, я объявил его в переменной, являющейся членом класса:

private Aes.KeySize keysize;

Заметьте: исходный ключ не вводится пользователем. В примере применяется «нулевой ключ» — массив, заполненный нулями. Этот ключ формируется, когда конструктору передается пустой аргумент new byte[16]. Размер пустого ар [у мента не имеет значения, поскольку исходный ключ также инициализируется нулями. Шифрование и дешифрование с нулевым ключом — эффективный способ избежать случайного раскрытия данных. С помощью методов Encoding.Unicode.GetBytes и Encoding.Unicode.GetString пространства имен System.Text легко преобразовать.NET-строку в массив байтов и наоборот.

Другие варианты реализации Теперь рассмотрим некоторые важные альтернативы той реализации AES, которая была показана здесь, возможные расширения кода и потенциально вероятные попытки взлома AES.

Как и любой алгоритм, с которым мне доводилось работать, AES можно реализовать разными способами. Почему это важно? AES рассчитан на применение в широком спектре систем -- от смарт-карт с крохотной памятью до громадных многопроцессорных мэйнфреймов. Во многих случаях критически важна производительность, иногда приходится сталкиваться с ограниченностью памяти или других ресурсов обработки данных. Почти каждую подпрограмму AES можно изменить, оптимизировав производительность за Защита данных с помощью Advanced Encryption Standard 49 счет памяти, или наоборот. Например, присвоение 256 значений элементам таблицы замен Sbox[] выглядит вроде бы достаточно прямолинейно. Но эти значения вычисляются в соответствии с теорией GF(2 ), и их можно генерировать программно. То же относится к таблице обратных замен и таблице итеративных констант.

Еще одна интересная возможность — разные способы реализации умножеR ния в GF(2 ), используемого методами Cipher и InvCipher. Я написал базовую функцию gfmultby02, умножающую на 0x02, и шесть дополнительных функций, вызывающих ее. Альтернатива — написать универсальную функцию умножения и использовать ее, а не семь разных функций, как в моем коде. Или экстремальный вариант — составить полную таблицу произведений всех 256 возможных значений байта на 0x01, 0x02, 0x03, 0x09, OxOb, OxOd и ОхОе. Еще один способ умножения в GF(2") — реализовать умножение как поиск в двух 256-байтовых массивах, обычно называемых alog[] и log[], поскольку такое умножение основано на некоторых свойствах GF(2 ), аналогичных свойствам логарифмов.

Показанный AES-класс вполне подходит для шифрования любых видов.NET-данных, однако его можно расширить. Во-первых, в статье основное внимание уделялось четкому описанию AES и поэтому пришлось обойти обработку ошибочных ситуаций. По моему опыту, если обеспечить в классе, подобном AES, приемлемый уровень проверки ошибок, объем исходного кода утроится. Так как в AES используется много массивов, нужно часто проверять, не вышел ли индекс за допустимый диапазон. Например, конструктор моего класса даже не проверяет размер параметра, содержащего исходный ключ.

Кроме того, можно было бы подумать о реализации дополнительной функциональности в AES-юшссе. Самое очевидное, с чего можно начать, — добавить методы шифрования и дешифрования базовых типов данных.NET, таких как System.String и System.Int32. Более амбициозное расширение — реализация класса шифрованного потока.

Насколько надежен AES? На этот вопрос сложно ответить, но, по общему мнению, это самый стойкий алгоритм шифрования из существующих. AES уделялось более пристальное внимание, чем другим современным алгоритмам шифрования. С теоретической и практической точек зрения AES считается стойким в том смысле, что единственным эффективном способом его взлома является метод грубой силы — перебор всех возможных ключей. Так как размер ключа — 256 битов, на данный момент лобовая 50 Шифрование атака не позволит взломать AES за приемлемое время (даже на самых быстрых существующих компьютерах на это уйдут годы).

Заметьте: наибольшие шансы взломать AES-шифр имеет так называемая атака с измерением времени (timing attack), возможная при некачественной реализации AES. Злоумышленник использует различные ключи и замеряет точное время, затрачиваемое на выполнение процедуры шифрования. Если процедура шифрования написана небрежно, время ее выполнения зависит от значения ключа, что позволяет получить информацию о ключе. В AES наиболее подвержена такой атаке подпрограмма MixColumns, так как в ней используется умножение в поле. Защититься от атаки можно двумя способами: вставить пустые инструкции, чтобы при всех умножениях выполнялось одинаковое число инструкций, или реализовать умножение в поле с помощью поиска по таблице, о чем я уже упоминал.

Реализовать AES можно многими способами, в частности на основе поиск по таблицам вместо вычислений. Базовый AES-класс, показанный в статье, можно использовать для шифрования и дешифрования любых видов.NET-данных или расширить, создав на его основе класс с дополнительной функциональностью.

Заключение Новый алгоритм AES явно станет стандартом де-факто на шифрование всех видов электронной информации и заменит DES. Данные, зашифрованные по алгоритму AES, нельзя взломать в том смысле, что не известно ни одной атаки криптографического анализа, которая позволила бы расшифровать AES-шифр без применения метода грубой силы, т. е. без перебора всех возможных 256-битных ключей.

Основной помехой реализации AES-класса в Microsoft.NET Framework для меня было то, что официальная спецификация написана с точки зрения математика, а не программиста. В частности, спецификация предполагает, что читатель отлично знает поле GF(2"), и не акцентирует внимание на некоторых ключевых особенностях умножения в GF(2H), необходимых для корректной реализации AES. Я попытался снять с AES покров тайны, особенно с умножения в поле GF(28).

Появление общедоступных библиотек шифрования по алгоритму AES для.NET Framework, разработанных Microsoft и сторонними поставщиками, — это лишь вопрос времени. А пока вам пригодится показанный мной код.

Эта реализация крайне проста и нетребовательна к ресурсам. Наличие исходного кода и понимание того, как он работает, позволит вам адаптироЗащита данных с помощью Advanced Encryption Standard 51 вать AES-класс к своим нуждам и более эффективно использовать любые другие его реализации.

Безопасность больше не является благим пожеланием для проектировщиков и разработчиков ПО. AES — весьма важное достижение. Понимание алгоритма AES и умение применять его значительно повысит надежность и безопасность ваших программных систем.

Джеймс Мак-Каффри (James McCaffrey) работает в компании Volt Information Sciences Inc., где руководит обучением инженеров ПО технологиям Microsoft. Работал по контракту над несколькими продуктами Microsoft, в том числе над Internet Explorer и MSN Search. С ним можно связаться по адресу jmccaffrey@volt.com или v-jammc@microsoft.com.

Кит Браун Безопасный код Хэширование паролей,атрибут Allow PartiallyTrustedCallers Отвечая на вопросы, ведущий этой рубрики поясняет, как хранить пароли в нестандартной, пользовательской базе данных и в каких случаях следует использовать в сборке (assembly) атрибут HowPartiallyTrustedCallers.

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

Вопрос Как хранить пароли в нестандартной, пользовательской базе данных?

Ответ Есть несколько вариантов. Простейший из них оставит вас с паролями в незашифрованном виде. Вот пример на XML, но то же самое легко делается в таблице базы данных;

users user name*'Alice' password='7Sy2si(Vldx'/ user name='Bob' password='mary'/ user name-'Fred' password='marv'/ /users Реализовав что-нибудь в таком духе, вы, наверное, почувствуете дискомфорт от того, что все пароли лежат в одном файле и доступны всем желающим. Нет, вы просто обязаны почувствовать дискомфорт! ЗлоумышленниПубликовалось в MSDN Magazine/Русская Редакция. 2003. № 8 (август). — Прим. изд.

Хэширование паролей, атрибут AllowPartiallyTrustedCailers 53 ку уж слишком просто заполучить пароли — он даже не вспотеет. А если злоумышленник завладеет этими паролями, пострадает не только ваш сайт, ведь многие пользуются одинаковыми паролями на разных сайтах.

Первое, что вы могли бы предпринять для защиты этих паролей, — зашифровать их. Это лучше, чем ничего, но все равно не самое удачное решение.

Для проверки пароля пользователя вам понадобится шифровальный ключ, а значит, он должен быть на машине, где обрабатываются все пароли. Это, конечно, создает хоть какой-то барьер для атакующего, потому что ему придется искать ключ, однако более эффективное решение — то, которое вообще не требует никаких ключей: необратимая функция (one-way function).

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

Это означает, что ситуация, где два сообщения могли бы привести к появлению хэша с одинаковыми значениями, в высшей степени маловероятна.

И в любом случае хэш необратим, так как представляет собой результат необратимой функции. Никаких ключей прятать не требуется.

Допустим, вы хэшируете пароли перед сохранением их в базе данных:

users user name='Alice' password»'D16E9B18FA038_'/ user name= - Bob' password='5665331B9B819...T/ user name='Fred' password='5665331B9B819.-V /users Теперь, когда вы получаете незашифрованный пароль и должны проверить его, вы не расшифровываете пароль в своей базе данных для сравнения.

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

Какая удача! Но даже и без такого везения плохие парни могут прибегнуть к словарной атаке на ваши хэшированные пароли, чтобы попытаться найти совпадения.

–  –  –

wordlists) и вычисляется хэш для каждого из них. Тогда атакующий может сравнить значения, полученные для своего словаря, со значениями, хранящимися в базе данных паролей. Найдя совпадение, он смотрит соответствующий пароль.

Чтобы замедлить атаку, используйте «расширение» (salt). Расширение способ «затемнения» паролей перед хэшированием, который обесценивает готовый словарь атакующего. Вот как это делается. Добавляя запись в базу данных, вы вычисляете последовательность произвольных символов (string of digits), которая будет задействована в качестве расширения пароля. Скажем, если вы хотите получить хэш для пароля Элис, то берете значение расширения для ее учетной записи, подписываете к паролю и хэшируете их вместе.

В итоге ваша база данных будет выглядеть примерно так:

users user name='Alice' salt='Tu72*&' password='6DB80AE7...'/ user name='8ob" salt='N5sbiX' Dassword='096B1085...'/ user name='Fred' salt='q-V3bi' password='9118812E...'/ /users Заметьте: теперь не видно, что Боб и Фред используют одинаковый пароль. Также обратите внимание, что само по себе расширение не является секретом. Важно, что оно разное для каждой учетной записи, поэтому такие строки очень удобно формировать на основе вывода от RNGCryptoServiceProvider.

На рис. 1 показана защита разных уровней стойкости. Решив хранить хэшированные пароли, вы поймете, что в этом случае нельзя переслать по электронной почте пароль тому пользователю, который его забыл. И это хорошо! Пересылать пароли по электронной почте просто глупо. Перенимайте опыт paypaf.com. На этом сайте хранится набор вопросов и ответов типа «Как зовут вашего домашнего любимца?» или «В каком городе вы родились?». Чтобы сменить пароль, пользователь должен правильно ответить на ранее выбранный вопрос.

–  –  –

Незашифрованные пароли Рис. 1. Защита разных уровней стойкости Атакующий не может применить к базе данных с затемненными паролями словарь с уже вычисленными хэшами. Но все равно может предприХэширование паролей, атрибут AHowPartiatlyTrustedCallers 55 нять словарную атаку, указывая расширение для каждой учетной записи, чтобы пересчитать хэшированные значения в своем словаре. Он также может попытаться найти короткие пароли методом грубой силы, вычислив хэш каждого из возможных паролей длиной, скажем, в четыре символа.

Чтобы замедлить эту атаку, предъявляйте к паролям ряд требований, в том числе по минимальной длине. Кроме того, вы можете потребовать использования комбинации строчных и прописных букв, цифр и знаков препинания. Конечно, если пароли окажутся слишком трудны для запоминания, пользователи начнут записывать их на бумажках. Так что ищите разумный компромисс.

Затемнение паролей — не панацея. И вы должны немедленно реагировать на компрометацию вашей базы данных паролей, даже если в ней хранятся затемненные хэши. Но затемнение дает вам чуть больше времени. Как правило, его хватает на то, чтобы успеть обнаружить попытку атаки и отключить атакованные учетные записи на тот период, пока их пользователи не сменят свои пароли.

В качестве отправной точки на рис. 2 представлен Ctf-класс, проверяющий пароли с применением затемненных хэшей. Он также создает расширения и вычисляет хэши для новых паролей при добавлении в базу данных новых учетных записей. Вам нужно лишь позаботиться о хранилище для расширения и хэша пароля (оба значения являются строками).

Вот пример использования этого класса:

string password = Console. ReadLineO;

SaltedHash sh = SaltedHash.Create(password);

// Допустим, что расширение и хэш хранятся в базе данных string salt = sh.Salt;

string hash = sh.Hash;

Console.WriteLine(."Salt: {0}", salt);

Console.WrlteLlne("Hash: {0}", hash);

// Найдя расширение и хэш, проверяем пароль SaltedHash ver = SaltedHash.Create(salt, hash);

bool isValid = ver.Verify(password);

Рис, 2. Класс SaltedHash namespace DevelopKeFitor.Se;Utils i using System;

using System.Security.Cryptography;

public sealed,class SaltedHash {

–  –  –

Рис. 2. Класс SaltedHash (продолжение) public string Salt { get { return _salt; } J public string Hash { get { return _haah; } } public static SaltedHash Cnsate(string password) { string salt = _createSaltO;

string hash = „calculateHashCsalt, password);

return new Sal tedHash( salt, hash);

I public static SaltedHash Cr3ate(strlng salt, string hash) return new SaltedHash(salt, hash);

public bool VerlfyCstrlng password) { string h = _calc«lateHash_salt, password);

return _hash.Equals(h);

I

–  –  –

private static string _create$alt() ( byte[] r = _createRandomBytes(saltLerjgth);

return Convert. ToBase64String(r);

private static byteEl „createRandomBytesfint len) { byteU г = new byte[len];

new R№3CryptoServiceProvi(Jer{}.Qet8ytes(r};.

return r;

private static string _calculateHash(strlng salt, string password) { byte[] data = _toByteArray(salt + password);

byte[J hash = _calculateHash(data);

return Convert.ToBase64String(hash);

–  –  –

Рис. 2, Класс SaltedHash (окончание) private static byte[] _calculateHash(byte[] data) { return new SflAICryptaServieeProviderO.GQmputeHashftfata);

private static byteU „toByteArray(string s) { ruturn System. Text. Encoding. UTF8.Get8ytes(s);

private readonly string „salt;

private readonly string „hash;

private const int saltLength * 6;

Вопрос В каких случаях следует использовать в сборке (assembly) атрибут AHowPartiallyTrustedCallers?

Ответ Только после всестороннего анализа вашего кода. Этот атрибут был введен на самом последнем этапе разработки Microsoft.NET Framework версии 1.0. Его не было в бета-версиях. В итоге он не попал в саму документацию, но кое-какие сведения о нем можно почерпнуть из дополнений к документации (release notes). Позвольте сначала пояснить, из каких соображений ввели такой атрибут, После компиляции сборка со строгим именем (strongly named assembly) может быть помещена в Global Assembly Cache (GAC), а значит, будет видна не только вашему коду, но и любому другому на вашей машине, в том числе мобильному (mobile code), который иногда поступает из недоверяемого источника. Если в вашем коде есть какие-то ошибки и вы не исключаете, что они могут создавать дыры в защите, тогда вы наверняка предпочтете, чтобы ваша сборка по умолчанию вызывалась только полностью доверяемым кодом. Это означает, что случайный код, загружаемый из Интернета, по умолчанию не получает прав на вызов вашего кода, так как загруженный из Интернета код считается лишь частично доверяемым.

–  –  –

допустить обращения частично доверяемого кода к базе данных. В строке подключения к базе данных используется учетная запись за, по которой с базой данных можно сделать что угодно — даже удалить. SQL-оператор формируется так, что в него включается нефильтруемый внешний ввод в виде неких имени и пароля. Это открывает возможность внедрения стороннего SQL-кода через параметры Name и Password! Короче говоря, здесь прорва ошибок, и, как это ни грустно, именно такой код обычно приводится в примерах, относящихся к программированию баз данных. Если ничего не подозревающий программист просто вставит этот код в свое приложение, оно окажется крайне уязвимым перед атаками злоумышленников.

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

Рис. 3. Так никогда не делайте public class HopeThisIsRt Used By livllCoileBecaus в ItsMot Robust { public string Name;

public string Password;

// Вся эта функция состоит из ужасающе скверного кода, // пользоваться которым НЕЛЬЗЯ Ни ПРИ КАКИХ ОБСТОЯТЕЛЬСТВАХ public bool IsValidUserO { new DbDataPeriitis5iofl{Permls3ionState.Unrestricted).Assert(3;

SqlConnection сопл = new SqlConnecton( "initial oatalQg=accQurtts; user id=sa; passwords") conn.QpenO;

cffld.CoiBinandText = string. Format ( "select eount(*) from users where narne='{0}'" + "and password8'О К'Ч Name, Password);

return {Unt)cBid.ExeeuteSca3ar()) Q;

Вот здесь и пригодится атрибут AlbwPartiallyTnistedCallers. Б Microsoft буквально в последнюю минуту решили поставить еще один барьер, который надо снять для того, чтобы частично доверяемый код получил возможность обращаться к локальному, полностью доверяемому коду. Смысл в том, что без этого атрибута сборку со строгим именем нельзя связать (скомпоновать) с частично доверяемым кодом.

Надеюсь, теперь ответ ясен. Атрибут AllowPartiallyTrustedCallers можно добавлять в сборку только в одном случае: после тщательного аудита безопасности. Будьте вдвойне бдительны, если ваша сборка обращается к неуправляемому коду. Как только вы применяете такой атрибут к сборке, помещаемой в GAC, вы делаете ее объектом возможной атаки со стороны люХэширование паролей, атрибут AllowPartiaHyTrustedCallers 59 бого недоверяемого кода. Вот почему даже не у всех базовых сборок самой.NET есть этот атрибут. Например, частично доверяемому коду не разрешается использовать System.Runtime.Remoting.dll или System.Management, dll. И причина в том, что Microsoft сама не уверена, что эти библиотеки точно не позволят частично доверяемому коду повысить уровень своих привилегий. ASP.NET лишь совсем недавно прошла соответствующие тесты, и ее System. Web.dll присвоен атрибут Allow Partially TrustedC alters в версии 1.1 инфраструктуры Microsoft.NET Framework.

Кит Браун (Keith Brown) работает в DevelopMentor как исследователь, технический писатель и преподаватель. Разъясняет программистам концепции безопасного кода. Автор книги "Programming Windows Security» (AddisonWesley, 2000). Сейчас работает над новой книгой по защите в.МЕТ. С ним можно связаться через http://www.develop.com/kbrown.

Стефен Тауб.NET Remoting Защита трафика.NET Remotlng с помощью приемников каналов асимметричного шифрования* Популярность.NET Remoting в корпоративном секторе растет, поэтому ее безопасность должна соответствовать требованиям бизнеса. Трафик удаленного взаимодействия (rernoting traffic) можно защитить, когда объекты размещаются в IIS, однако, если это не так, для защиты можно разработать собственные решения. В этой статье подробно рассказывается о создании приемников каналов (channel sinks) для.NET. В ней также описывается поток данных через нестандартные приемники каналов и поясняется, какие операции возможны с этими данными.

Remoting (механизм удаленною взаимодействия) — это решение, предлагаемое в.NET для распределенных вычислений. Как преемник DCOM,.NET Remoting устраняет многие недостатки предшественника и в то же время открывает массу новых возможностей. Но, как и в любом другом продукте, первые версии редко содержат всю необходимую функциональность, и, кроме того, в процессе эксплуатации продукта развитие потребностей пользователей может опережать выпуск новых версий. Понимая это, разработчики с самого начала проектировали.NET Remoting так, Публиковалось в MSDN Magazine/Русская Редакция. 2003. № 6 (июнь). — Прим. изд.

Защита трафика.NET Remotmg чтобы его можно было расширять, просто добавляя требуемые средства.

Одно из таких средств — защита и шифрование трафика удаленного взаимодействия.

С ростом популярности удаленного взаимодействия в корпоративном секторе возникают вопросы, касающиеся способности Microsoft.NET Framework шифровать трафик удаленного взаимодействия. Чаще всего интересуются, поддерживает ли.NET Remoting технологию SSL (Secure Sockets Layer). Увы, ответить просто «да» или «нет» тут нельзя.

Поддержка HTTPS

Когда говорят об удаленном взаимодействии, часто употребляют слово «канал* (channel). В общем случае канал — это серия приемников (sinks), через которые проходит удаленный вызов. HTTP-канал для.NET на самом деле является комбинацией двух каналов: один используется клиентом, второй — сервером. Каждый содержит свой набор приемников, называемый цепочкой (sink chain). Цепочка приемников на клиентской стороне заканчивается HttpClientTransportSink, который для работы с НТТР-трафиком использует классы Http Web Request и HttpWebReponse из пространства имен System.Net. Это дает дополнительное преимущество: поскольку HttpWebRequest и Http Web Response поддерживают HTTPS, его поддерживает и клиентский HTTP-канал. То есть, если серверный канал поддерживает SSL, у вас есть все, что нужно.

Если транспортный приемник клиента — это HTTP-клиент, то HttpServerTransportSink — не более чем специализированный Web-сервер. Чтобы это увидеть, достаточно установить простой хост с поддержкой удаленного взаимодействия (remoting host) и подключиться к нему через Web-браузер.

Серверу удаленного взаимодействия для обслуживания объекта требуется хотя бы регистрация канала и публикация типа объекта или класса (общеизвестный, активизированный и т. д.). Это делается при помощи классов из пространства имен System.Runtime.Remoting:

static void Main(string[] args) \ ChannelServices.RegisterChannel(new HttpChannel(8124));

RemotingConfiguration.RegisterWellKnownServiceType( typeof(Person), "Person.soap", WellKnownObjectMode.Singleton);

Console.WriteLine ("Press enter to stop the server.,.");

Console. HeadLlneO;

Шифрование Чтобы показать, что это приложение действительно является Web-сервером, я подключусь к выбранной конечной точке на сервере через Microsoft Internet Explorer для запроса WSDL-документа (Web Services Description Language) с описанием типа объекта, поддерживающего удаленное взаимодействие (remoted type), — так же, как и в случае Web-сервиса ASP.NET.

Полученный документ показан на рис. 1.

definitions name ^Person' targe tName зр а с е =" http: / / sch em a s. microsoft. com / ctr/ n sassem/ M sd n M a g. I 2C4t2DfublicKeyrokertM3Dniifr xmins -"http://sc:hemas.xmlsoap. org / 1 v s d I / * xm in s : tns ="http: / /sche ma 5.xml sen p.o rg / wsdl / " xmlns:xsd="httpV /www.w3.org/3Oai/XMLSchema* xrn Ins : xsi=' http: / / www. w3.org / 2 OO 1 / XM L Sch*! ma - i n sta и се ° xmlns : suds="http:/ /www. w3.org / 2OOG ' wsdl/ suds' к m In s : w 5 dl ="http : / /sc h e m a s.xmlsoa p.org / wstl 1 / " xmins : soa pe n с ="hltp:// schemes -xmtsoap.org/ soap/ encoding/" xmlns:ns2=nti tip:/ /schemas.microsaft.com/clt/nsa55em/MsdnMag.Remotii 2CW)2OPubticKeyroken443Dnuil" РИС. 1. WSDL Я подключаюсь к серверу удаленного взаимодействия и посылаю HTTP-запрос на «/Person.soap?wsdU, чтобы получить WSDL для Person — пользовательского класса, производного от MarshalByRefObject. На сервере этот запрос обрабатывается SdlChannclSink — членом серверной цепочки приемников канала, создающим соответствующий WSDL для класса Person.

Это описание отправляется клиент;' как HTTP-ответ:

C:\telnet localhost 8124 GET /Person.soap?wsdl HTTP/1.0 HTTP/1.1 200 OK Content-Type: text/xml Server; MS,NET Remoting, MS.NET CLR 1.0.3705.288 Content-Length: 8663 Connection: Close

–  –  –

Хотя теперь вы знаете, что HttpServerTransportSink — это Web-сервер, остается все тот же вопрос: можно ли подключаться к серверу через HTTPS? Увы, нет. HttpServerTransportSink не поддерживает SSL. Но нестандартные Windows-службы и другие приложения вроде показанного мной — не единственный способ хостинга объектов с поддержкой удаленного взаимодействия. Microsoft Internet Information Services (IIS) в сочетании с ASP.NET тоже поддерживает удаленное взаимодействие с объектами, и при их использовании вся инфраструктура выигрывает от поддержки HTTPS в IIS. Доступ к объекту, размещенному в ASP.NET, можно получить по URL наподобие https://localhost:8124/MyApplication/Person.soap, и тогда весь конфиденциальный трафик будет шифроваться.

К сожалению, применение IIS для хостинга объектов ограничит вас применением HTTP. Но как защитить данные от чужих глаз, когда вы используете другой транспорт или не размещаете объекты в IIS? Одно из преимуществ инфраструктуры.NET Remoting в ее расширяемости — в том множестве способов, которыми можно расширять и модифицировать ее функциональность.

Вызовы через Remoting и расширяемость

Когда клиент запрашивает ссылку на удаленный объект, ему возвращается ссылка на TransparentProxy — объект, чьи методы, свойства и функциональность кажутся такими же, как и у удаленного объекта, но на самом деле при обращении к нему начинается процесс делегирования.

Вызов к TransparentProxy передается как объект MessageData другому прокси — объекту, тип которого наследует от RealProxy (часто, но не всегда — RemotingProxy). Этот прокси в свою очередь создает IMessage из MessageData и пересылает сообщение, содержащее информацию о вызове метода, по серии приемников сообщений (message sinks) — классов, реализующих IMessageSink или IDynamicMessageSink, каждый из которых может модифицировать или заменять пересылаемое сообщение.

Последний IMessageSink в цепочке должен быть приемником форматирующее объекта (formatter sink), который нередко реализует IClientFormatterSink — комбинацию IClientChannelSink и IMessageSink. Этот приемник отвечает за создание транспортных заголовков (transport headers), а также за прием IMessage и его сериализацию в поток (stream). Затем этот поток и набор заголовков вместе с IMessage, используемым только для ссылки, передаются через серию приемников IClientChannelSink, каждый из которых может подставить новый поток данных вместо 54 Шифрование полученного от предыдущего приемника (на этой стадии обработки IMessage уже сериализован в поток и нужен лишь для сведения). Последний из этих приемников — транспортный (transport sink) — получает заголовки и поток и посредством транспорта, такого как HTTP-соединение или именованный канал (named pipe), посылает данные серверу.

Подобный процесс происходит и на сервере, но в обратном порядке. Транспортный приемник сервера получает заголовки и поток от транспортного приемника клиента. Эти данные обрабатываются серией объектов IServerChannelSink (включая SdlChannelSink, который генерирует WSDL, показанный ранее) так же, как это происходило на клиентской стороне. Форматирующий объект — он же IServerChannelSink — принимает набор заголовков и поток и десериализует их в копию исходного IMessage, который затем пересылается через серию объектов IServerChannelSink, IMessageSink и IDynamicMessageSink, пока не попадет в последний приемник — StackBuilderSink (приемник формирователя стека). Этот приемник, используя отражение (reflection), выполняет запрос и получает сообщение-ответ (return message). Оно проходит обратный путь через приемники, по транспортному уровню и через приемники клиента, пока не достигает прокси, где это сообщение разбирается на возвращаемое значение и выходные параметры.

Как видите, при использовании объекта с поддержкой удаленного взаимодействия за кулисами происходит много событий. Одно из преимуществ этого в том, что существует много мест, где можно подключить собственную функциональность (рис. 2). Можно создать приемники форматирующих объектов для сериализации и десериализации IMessage в особый формат (если встроенные форматирующие объекты SOAP и Binary вас не устраивают), а также ввести дополнительные IMessageSink для изменения передаваемого сообщения. IClientChannelSinks и IServerChannelSinks позволяют изменять потоки и наборы заголовков. Используя собственные RealProxy, можно отслеживать или заменять запускаемые методы. А транспортные приемники позволяют принимать/отправлять данные через любой транспортный уровень, который кажется подходящим разработчику.

Приемники шифрования (encryption sinks) должны изменять данные, передаваемые между клиентом и сервером. Здесь не потребуются ни специальный транспорт или форматирующий объект, ни модификация сообщения до его сериализации. Лучшее место для подключения такого приемника (и наиболее распространенная из точек перехвата) находится между форматирующим объектом и транспортным приемником. Для подключения нужно реализовать пользовательские классы IClientChannelSink и IServerChannelSink, которые шифруют проходящие через них потоки.

Защита трафика.NET Remoting

–  –  –

. йоякаошвяммв :

лринмнихи каналов ПользаваияЬйнй Рис. 2. Поток удаленного взаимодействия и точки перехвата Один приемник или два?

Иногда нужно создавать и клиентский, и серверный приемник. Делать ли так, зависит от приложения, поскольку нет правила, обязывающего создавать приемники парами. Компонентам, требующим выполнения каких-то операций на обеих сторонах, например каналам шифрования или сжатия данных, конечно, нужны два приемника. Но во многих ситуациях достаточно одного приемника. Так, ASP.NET поддерживает отличную систему кэширования вывода, где хранится результат выполнения страницы. Этими данными можно будет воспользоваться при запросе к странице в будущем без повторного выполнения страницы. Инфраструктура удаленного взаимодействия не предоставляет подобной системы, но вы можете написать серверный приемник, выполняющий такие функции. Он может проверять полученный объект IMessage, просматривать собственный объект кэширования в поисках результата предыдущего выполнения и передавать запрос дальше по цепочке, если такого результата не найдено, или сразу возвращать Return Message, сформированное на'основе содержимого кэша.

Клиенту не нужно знать, что применяется кэширование, поэтому в клиентском приемнике нужды нет (конечно, можно реализовать кэширование на клиентской стороне, и тогда потребуется только клиентский приемник). Приемник, выполняющий такие функции, содержится в исходном коде примеров для этой статьи, которые можно скачать по ссылке http:// msdn.microsoft.com/msdnmag/code03.aspx в разделе за июнь. Ранее упомянутый SdlChannelSink также работает только на сервере.

3-138 66 Шифрование В этом примере важно отметить, что приемник не обязан передавать чтолибо далее по цепочке приемников. Если он решает, что сообщение или поток не нужно передавать дальше, тогда он должен создать свой ответный поток и заголовки и сразу отправить их в обратном направлении. При этом сообщение так и не достигает конечной точки. Это позволяет, например, каптирующему приемнику извлекать данные из кэша, вместо того чтобы всегда пересылать их следующему приемнику. Приемник на стороне клиента может создать свой поток и заголовки и отправить их на сервер вместо полученных от предыдущего приемника. Благодаря этому пара приемников может отправлять и принимать множество сообщений, никогда не выходящих за пределы цепочки. Кажется, это пригодится для согласования защиты, а?

Разработка протокола

Разработка канала симметричного шифрования (symmetric encryption channel) с использованием 3DES, RC2 или другого алгоритма симметричного шифрования, предоставляемого пространством имен System.Security.Cryptography, потребует двух приемников. С каждой стороны размещается один приемник между форматирующим объектом и транспортом; так как вам нужно работать с сериализованным потоком, приемник должен размешаться после форматирующего объекта клиента и до аналогичного объекта сервера. На рис. 3 показаны важнейшие элементы этих приемников.

Поскольку это симметричное шифрование, оба приемника должны использовать один ключ и вектор инициализации (initialization vector, IV), содержащиеся в объекте-провайдере Symmetric Algorithm, который передается функциям CryptoHelper. Эта информация должна быть передана обоим приемникам заблаговременно, или один из них должен создавать ключ динамически и пересылать его другому по защищенному каналу.

Для асимметричного шифрования (asymmetric encryption), также известного как шифрование с открытым ключом (public key encryption), применяются такие алгоритмы, как RSA, EfGamal и Rabin. Они основаны на концепции пар ключей — наборе ключей, математически связанных так, что сообщение, зашифрованное с одним из них, может быть расшифровано только с другим. Любой, кто хочет получать и расшифровывать зашифрованные сообщения, должен создать пару ключей: открытый (public key) распространяется свободно, а закрытый (private key), напротив, надо тщательно охранять. Кто угодно может зашифровать сообщение открытым ключом, и только закрытым ключом его можно будет расшифровать.

Защита трафика.NET Renmting 67

Рис. 3. Клиентский и серверный приемники

// IClientCnannelSlnk.ProcessHessage public void ProcessMessage(IMessage msg, ITransportHeaders reqwestHeaders, Stream requestStrea», out ITransportHeaders responseHeaders, out Strean responseStream) f // Шифруем поток requestStream = CryptoHelper.Encrypt(requestStreain,.provider);

// Отправляем поток следующему приемнику в цеяочке _next.ProcessMessage(ms|, requestHeaders, requestStreafB, out responseHeaders, out responseStream);

// Расшифровываем ответный поток responseSt г earn = CryptoHelper.DecrypttresponseStreaiB, „provider);

// ISe rverChannelSin-k. ProcessMessage public ServerProcessing ProcessMessage( IServerChannelSinkStack sinkStack, IMessage requestMag, ITransportHeaders requestHeaders, Streas requestStream, out IHessage responseMsg, out ITransportHeaders responseHeaders, out Stream responseStream) // Расшифровываем входящий поток запроса requestStreae * CryptoHelper.Decrypt(requestStreaffl, ^provider);

// Пересыпаем запрос ServerProcessing result = _next.PropessMessage( slnkStaek, requestMsg, requestHeaders, requestStrean, out responseHsg, out responseHeaders, out responseStream);

// Шифруем поток отаета respoflseStreaui * CryptoHelper.Enerypt(responseStream,.provider);

–  –  –

Рис. 4. Алгоритм обмена ключами Асимметричное шифрование решает проблему передачи ключей между двумя приемниками (рис. 4). Клиентский приемник динамически создает пару ключей и отправляет открытый ключ серверному. Затем серверный приемник динамически создает симметричный ключ (symmetric key), шифрует его клиентским открытым ключом и отправляет обратно клиенту. Теперь клиентский приемник может расшифровать симметричный ключ своим закрытым ключом; оба приемника теперь имеют одинаковые симметричные ключи и могут обмениваться информацией. Сведущие люди заметят, что это очень похоже на то, как браузер устанавливает защищенное соединение с Web-сервером по SSL.

Реализация обмена ключами

Как уже говорилось, форматирующие объекты принимают IMessage и создают два выходных объекта; ITrarisportHeaders и Stream. Поток (stream) содержит сериализованную версию IMessage, а объект ITransportHeaders — заголовки (пары «имя-значение», отправляемые серверу транспортным приемником). Изначально этот набор содержит заголовки, предоставляемые форматирующим объектом (formatter-supplied headers), такие как SOAPAction. Однако этот набор используется не только форматирующим объектом. Любые приемники между форматирующим объектом и транспортом могут добавлять заголовки в этот набор, и все они будут так же обрабатываться транспортным приемником. Это прекрасное место для размещения информации, необходимой для согласования (handshake information), — она понадобится клиенту и серверу для взаимодействия. Такая информация может включать состояние согласования (handshake state), шифровальные ключи и идентификатор соединения.

Защита графика.NET Remoting Реализуя данное решение, помните, что и клиент, и сервер должны быть безопасными в многопоточной среде (thread-safe). Когда удаленные запросы к объекту направляются из нескольких потоков, до получения приемником эти запросы никак не упорядочиваются (если только предыдущий приемник не решил синхронизировать доступ). Не забывайте об этом при сохранении информации о состоянии. Кроме того, один сервер может использоваться многими клиентами. Поскольку клиенту и серверу потребуется хранить общий ключ шифрования (и поскольку распространять среди всех клиентов одинаковый ключ будет небезопасно), серверу придется вести список всех клиентов и учитывать связанную с ними информацию.

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

Реализация для клиента теперь вполне очевидна (рис. 5). ProcessEncryptedMessage вызывается из ProcessMessage и выполняет основную работу. Когда вызывается ProcessMessage, клиент должен проверить, получил ли он от сервера ключ для шифрования. Если да, он может шифровать поток, содержащий сериализованное сообщение, и пересылать его серверу, расшифровывая полученный ответ. Клиенту также следует учитывать, что сервер мог удалить информацию о соединении или перезапуститься и потерять ее, и поэтому клиент должен быть готов ко второй попытке в случае неудачной транзакции. Если клиент не получил общий ключ от сервера или если сервер вернул сообщение, уведомляющее клиент о том, что он не распознан, клиент должен инициировать согласование с сервером. Как я говорил, клиент создаст для себя уникальный идентификатор и пару ключей RSA и отправит идентификатор и открытый ключ серверу.

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

Рис. 5, ProcessEncryptedMessage клиентского приемника private bool ProeessEnctyptedMessageC IMessage nsg, ITransportHeaders req-uest Headers, Stream requestStream, out ITransportHeaders responseHeaders, out Stream responseStreatn)

–  –  –

id = EnsuretDAndProviderCmsg, requestHeatfers);

requestStгеав = SetupEneryptedMessaQe(requestHeacters, requestSt ream);

I // Отправляв* зашифрованный запрос на сервер „next, ProcessMessage( msg, re quest Heade rs, requestSt ream, out responseheaders, out responseStream);

–  –  –

// Возвращаем результат return respoflseStreara 1= null;

Серверу нужна реализация, не требующая логического соединения (connectionless implementation). Он должен быть готов получать запросы на разных стадиях согласования or любого числа клиентов. При поступлении запроса сервер ищет идентификатор и состояние согласования в транспортных заголовках (рис. 6). На основе этих двух параметров он может выбрать нужную линию поведения. Если клиент запрашивает общий ключ, сервер создает его и отправляет клиенту, зашифровав общий ключ открытым ключом клиента (и не забыв сохранить ключ для дальнейшего использования). Если клиент запрашивает обработку зашифрованного сообщения, сервер пытается найти ключ клиента и, если ключ найден, расшифровывает с его помощью запрос, пересылает запрос по цепочке приемников, шифрует полученный результат и передает клиенту. Если ключ не обнаружен, он должен сообщить об этом клиенту. Пример такого обмена показан на рис. 7.

Защита трафика.NET Remoiing Рис. 6. ProcessMessage серверного приемника public ServerProcessing ProcessHessage( IServerChannelSinkStack sinkStaefcj, IMessage requestHsg, ITransportHeaders requestHeaders, Stream requests-tree», out IMessage responseHsg, out ITransportHeaders responseHeaders, out Streaie responseStream) // Получаем из заголовка информацию о транзакция string strTransactlD = {string)reque9tHeaders[CoatffionHeaders.IDl;

Quid transactID = (strTransactJD == null) ?

Quid.Empty : new 3uitl(strTransactID);

Secure! ransactlon. transactType SecureTransaction)Convert.ToInt32 (stflng)requestHeaders[Coii»onHeaders. Transaction]);

// выясняем, icro к нам подключился IPAddress clientAddress requestHeadersECommonTranSBOrtKeys.IPAddress] as IPAddress;

// Помещаем этот приемник в стек приемников sinkStack.Push(thls, null);

// Обрабатываем транзакцию в соответствии с ее типом ServerProcessing proeessingFLesult;

switch{transactType)

–  –  –

// Клиент посыпает зашифрованное сообщение case SecureTransactlon.SerKM.ngEneryptedMe'Ssage;

if (PreviousTransactlo^KithGlienttrarisaetID»

–  –  –

нужных транспортных заголовков (предполагается, что клиентское приложение не использует клиентский приемник), то может передать информацию следующему приемнику без изменений. Но вы не обязаны применять эту возможность. А как быть, если если вы хотите ввести обязательное требование о шифровании трафика для всех или лишь отдельных клиентов?

В этом вам помогут HttpServerTransportSink и Тер Server Transport Sink, которые предоставляют IP-адрес подключившегося клиента. Извлечь IP-адрес можно из транспортных заголовков:

IPAddress clientAddress = requestHeaders[CommonTransportKeys.IPAddress] as IPAddress;

А затем определить по этому адресу, принимать ли незашифрованный трафик:

case SecureTransaction.Uninitialized:

if (!RequireSecurlty(clientAddress)) { processingResult = _next.ProcessMessage( sinkStack, requestMsg, requestHeaders, requestStream, out responseMsg, out responseNeaders, out responseStream);

} else throw new SecureRemotingException("Security required.");

break;

Асинхронная обработка В.NET Framework версии 1.0 нет поддержки асинхронной обработки для пользовательских приемников канала на сервере (несмотря на наличие метода с броским именем AsyncProcessResponse в интерфейсе IServerChannelSink). Все запросы к вашему серверному приемнику будут синхронными, даже если клиент выдаст асинхронный запрос; поэтому в серверный приемник не нужно добавлять функциональность для асинхронной обработки.

Реализовать асинхронную обработку на клиенте не так просто. Клиентский ProcessMessage служит только для синхронных запросов. Для односторонних и асинхронных вызовов логика обработки делится между двумя методами интерфейса IClientChannel Sink: AsyncProcessRequest и AsyncProcessResponse. AsyncProcessRequest предоставляет приемнику возможность изменять поток и заголовки, отправляемые серверу, a AsyncProcessResponse позволяет изменять поток и заголовки, принимаемые от сервера,

–  –  –

AsyncProcessRequest вызывает EnsurelDAndProvider для получения этих сведений. Для этого он должен совершить синхронный обмен с сервером так же, как это делает ProcessMessage. Если повезет, это делается лишь раз, и остальные запросы, выполняемые через AsyncProcessRequest, будут асинхронными. В отличие от синхронной обработки здесь вы не сможете выдать другие асинхронные запросы при неудаче одного из запросов. Так как AsyncProcessResponse должен возвращать исходящие заголовки и поток и вы не можете повторить асинхронный запрос, для новой попытки второй запрос должен быть синхронным. Для этого AsyncProcessRequest заталкивает в стек приемника полученные аргументы как состояние (state), передаваемое AsyncProcessResponse. Если AsyncProcessResponse получает от сервера неправильный ответ, он может использовать эти данные для создания синхронного запроса через ProcessMessage.

Рис. 8. Метод AsyncProcessReqtiest клиента public void AsyncProcessRequest С IClientChawielSlrjkStack sinkStack, message «sg, ITr an spor traders headers, Stream stream) { AsyneProcessingState state = null;

Stream encryptedStreaffl « null;

Guid id;

lock(_t ransact iontoc k ) { // Согласуем с сервером информацию о соединении // (надеясь, что это понадобится только при первом запросе) id = nsEireIDAndProvider(aisg, headers);

//.Сохраняем информация о состоянии на случай сбоев stete * new AsyncP recess ingStatefmsg, headers, ref stream, id) // Шифруй* поток encryptedStrean = S9tupEncryptedHessaae(fteacfers, stream);

// Заталкиваем себя в стек с необходимым состоянием // и переходим к следующему приемнику SinkStack. PusMthis, state);

_next.A8yncProGessHeqtiest(sirikStack, ragg, headers, eneryptedStreaat);

Защита трафика.NET Remoting Я намеренно не рассказываю об односторонних методах (one-way methods), так как они не поддерживаются этой парой приемников. По своей природе клиентский приемник никогда не получит ответ для одностороннего метода, и поэтому обмен с сервером для получения общего ключа потребует много дополнительного кода.

Очистка сервера Сервер должен хранить информацию о соединениях для всех подключенных клиентов, иначе он не сможет расшифровать отправляемые клиентом данные из-за отсутствия общего ключа, нужного для расшифровки. Если к серверу подключено много клиентов (которые могут быть активны длительное время), в таблице соединений может оказаться устаревшая информация. Поэтому сервер должен периодически очищать эту таблицу. В своей реализации я решил вместе с ключом записывать время последнего обращения клиента и определять, прошло ли с момента последнего обращения время, большее заданного значения. На рис. 9 показан код, периодически перебирающий все клиентские соединения и, если они устарели, удаляющий их из таблицы. Если клиент, информация о котором удалена, попытается отправить зашифрованное сообщение, сервер сообщит, что клиент не опознан. Тогда клиент и сервер проведут новое согласование и начнут весь процесс сначала.

Рис. 9. Очистка информации о соединениях private void StartCennectionSweeperO // Создаем и запускаем тай«ер, который будет запускать // SweepGonnections If (_sweepTiffler != null) { _sweepTimer = new System. Tiners.Ttiser(_swespFrequency*1000);

jsweepTimer. Elapsed •*•= tie» ElapsedEventHandler{SweepConnectiorts);

_sweepTiffler.Start();

private void SweepCorweeticmsCobject sender, ElapsedEventArgs e) { lock (_connectiofis.SyncRoot { ArrayList toCelete * new ftrrayUstCcormeetionE. Count);

// Ищем записи, которые еяедует удалить foreeDhi(DictionaryEntry entry in..connections)

–  –  –

Рис. 9. Очистка информации о соединениях (окончание) ClientConnectionlnfo cci = (ClientConnectionlnfo)entry.Value;

if(cci. LastUsed,AdcfSeconds(_connectiofiAgeLIfflit}.GofflpareTo{ DateTiw.UtcNow) 0) {

toBelete.Add(entry.Key):

(IDi$posabie)cci).DispiseO;

// Удаляем все просроченные записи foreach(0bject obj in toDeletn _connections.flemove(obj);

toDelete = null;

Такой процесс способен обеспечить еще один уровень защиты. Кроме времени последнего обращения сервер может записывать число обращений клиента или объем переданной информации. По достижении этими величинами установленного порога сервер может выполнять операцию очистки и удалять информацию о соединении для этого клиента. Клиент может сделать то же самое, удалив общий ключ и далее действуя так, будто он его не получал. Это заставит клиент и сервер принудительно сменить общий ключ по аналогии с процессом, используемым для автоматического обновления ключей в IPSec.

Создание приемников Теперь, когда приемники написаны, нужно помочь инфраструктуре создать их экземпляры. Инфраструктура.NET Remoting не создает приемники напрямую. Вместо этого создаются «провайдеры приемников» (sink providers), которые и отвечают за создание самих приемников. Провайдеры и приемники почти всегда связываются по принципу «один к одному», т. е. один провайдер служит для создания только одного приемника, однако ничто не мешает провайдеру создать несколько приемников.

Наибольшее преимущество от передачи работы по созданию экземпляров приемников провайдерам в том, что это добавляет еще один уровень, через который разработчик приемника получает больший контроль. Если провайдер реализует корректный конструктор, инфраструктура передаст провайдеру {Dictionary, содержащий атрибуты и элементы, указанные для провайдера в конфигурационном файле удаленного взаимодейЗащита трафика.NET Remoting 77 ствия (если конфигурационный файл не используется, этот словарь можно предоставить и программно). Провайдер принимает эту информацию и использует ее для создания приемника с любым конструктором по желанию разработчика. Нужно лишь, чтобы провайдер уведомил приемник о следующем приемнике в цепочке, дабы тот мог пересылать обрабатываемые запросы.

Когда инфраструктура создает провайдеры, они формируют собственную цепочку. Интерфейсы IClientChannelSinkProvider и IServerChannelSinkProvider требуют реализации свойства Next, через которое инфраструктура указывает следующий провайдер в цепочке. Инфраструктура создает экземпляры провайдеров и вызывает метод CreateSink первого провайдера. Метод CreateSink отвечает за создание реального приемника и возврат ссылки на него (рис. 10).

Поскольку приемник следует уведомить о следующем приемнике в цепочке, метод CreateSink должен переслать вызов следующему провайдеру в цепочке, который вернет ссылку на свой приемник:

public IServerChannelSink CreateSink(IChannelReceiver channel) I IServerChannelSink nextSink = null;

if (_next != null) { if (nextSink= _next.CreateSink(channel)) == null) return null;

} return new SecureServerChannelSink(nextSink,... );

I

–  –  –

Это предписывает инфраструктуре использовать HTTP-канал и форматирующий объект по умолчанию — SoapFormatter.

Чтобы переопределить параметры по умолчанию, укажите нужный форматирующий объект явным образом:

|.

channels channel ref="http" port="8124" serverProviders formatter ref="binary" / /serverProviders /channel /channels Когда тэг serverProviders/ (или clientProviders/ на стороне клиента) используется как элемент channel, он сообщает инфраструктуре, что вместо цепочки приемников по умолчанию канал должен быть настроен на использование провайдеров, указанных в списке, в соответствии с порядком их перечисления. В данном случае вы сообщаете инфраструктуре задействовать двоичный форматирующий объект (binary formatter).

Чтобы добавить еще провайдеры, внесите дополнительные элементы в список серверных провайдеров:

serverProviders provider type= "MsdnMag.Remoting.SecureserverChannelSinkProvider,SecureChannel" / formatter ref="blnary" / /serverProviders Заметьте: я добавил провайдер до форматирующего объекта, так как мне нужна возможность изменять поток до того, как он попадет в этот объект.

В конфигурационном файле клиента провайдеры, наоборот, должны идти после форматирующего объекта.

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

После применения RemotingConfiguration.Configure на клиенте и сервере с соответствующими конфигурационными файлами пользовательские приемники станут частью цепочек приемников.

Защита трафика.NET Remoting 79 Заключение Канал, описанный в этой статье, не является идеальным решением. Приемники не используют серьезные механизмы защиты потоков. Нет аутентификации и подписи сообщений. Нет защиты от атак посредника (manin~ the-mid die attacks) или атак типа «отказ в обслуживании» (denial of service attacks). Но надеюсь, суть решения понятна. Моя статья дает представление о типах решений, возможных в.NET Remoting. От них могут выиграть многие защищенные каналы, но возможности для творчества не ограничены: транспортные приемники для поддержки именованных каналов или MSMQ, IMessageSinks для кэширования возвращаемых значений, каналы со сжатием (compression channels), ведением журналов (logging channels), уведомлениями (notification channels) — на этом список не кончается. По-моему, очень скоро появятся самые разнообразные расширения от Microsoft и сторонних разработчиков, которые заполнят многие пробелы, но это, безусловно, не должно помешать вам сегодня же начать работу над приемником вашей мечты.

Стефен Тауб (Stephen Toub) — разработчик и консультант Microsoft в Нью-Йорке. Выпускник и бывший преподаватель Гарвардского университета. С ним можно связаться по адресу stoub@microsoft.com.

Дон Бокс

–  –  –

Сервисы признаков, политик, разрешений и применения политик в инфраструктуре CLR У общеязыковой исполняющей среды (common language runtime, CLR) в.NET Framework есть своя модель безопасного выполнения кода, независимая от ограничений операционной системы, в которой она работает. Более того, в отличие от старой модели защиты на основе участников безопасности (principal-based security) CLR реализует политику, исходя из того, откуда поступает код, а не из того, кто является его пользователем. Эта модель защиты по правам доступа кода (code access security) имеет больший смысл в современных условиях, поскольку немалая часть кода устанавливается через Интернет и даже доверяемый пользователь {trusted user) не знает, какой код действительно безопасен. В этой статье автор объясняет, как работает защита по правам доступа кода в CLR. Он рассматривает признаки (evidence), требуемые политикой (policy), а также рассказывает, как выдаются разрешения (permissions) и каким образом исполняющая среда применяет, или вводит в действие (enforce), политику безопасности.



Pages:   || 2 | 3 | 4 | 5 |

Похожие работы:

«ИСПОЛЬЗУЕМЫЕ СОКРАЩЕНИЯ В настоящей программе используются следующие сокращения: СПО – среднее профессиональное образование; ФГОС СПО – федеральный государственный образовательный стандарт среднего профессионального образования; ППССЗ – программа подготовки специалистов среднего звена ОК – общая компетенция; ПК – профессиональная компетен...»

«Миша Мельниченко Советский анекдот (Указатель сюжетов) http://www.litres.ru/pages/biblio_book/?art=9638114 Советский анекдот (Указатель сюжетов): ISBN 978-5-4448-0381-3 Аннотация Вниманию читателей предлагается первая...»

«КЛУБЫ, КРУЖКИ И ДРУГИЕ МЕРОПРИЯТИЯ ВОСТОЧНОГО АДМИНИСТРАТИВНОГО ОКРУГА ГБУ ТЦСО "Сокольники" филиал "Богородское", ул. Ивантеевская, д. 13, тел.: 8 (499) 160 18 86 Музыкальный кружок "Незабудка...»

«ПАМЯТКА ВЫПУСКНИКАМ ПО ВЫПОЛНЕНИЮ ЗАДАНИЯ № 7 Грамматические синтаксические ошибки – это ошибки в построении словосочетания и предложения. В задании №7 дано 5 предложений, в к...»

«Proceedings of Theriological School. Vol. 7 (2006) Праці Теріологічної Школи. Вип. 7 (2006): Теріофауна сходу України УДК 599.4 (472.5) Современное состояние фауны рукокрылых Луганского заповедника 1 Александр Кондратенко, Михаил Колесников, Татьяна Соловьева Сучасний стан фауни кажанів Луганського заповідника. — Кондратенко О., Колесніков М., Соло...»

«Шпаргалка для родителей Памятка для родителей Вниманию родителей! В России прогрессирует эпидемия наркомании и токсикомании. С каждым днем все больше регистрируются людей, употребляющих наркотики. Все шире становится список веществ, употребляемых для...»

«Государственное бюджетное образовательное учреждение высшего профессионального образования Московской области "Международный университет природы, общества и человека "Дубна" (университет "Дубна")...»

«Пояснительная записка Данная рабочая программа по предмету "Русский язык" для работы с учащимися 5 класса разработана в соответствии с положениями ФГОС ООО, на основе примерной программы ООО по русскому языку и программы по русскому языку к учебнику для 5 класса Разумовской М,М,, Львова...»

«Министерство образования, науки и молодежной политики Забайкальского края Государственное профессиональное образовательное учреждение "Краснокаменский промышленно – технологический техникум" СОГЛАСОВАНО: Утверждаю ГУ "ЦРПО Забайкальского края" Директор ГПОУ "Краснокаменс...»

«1 НАЗНАЧЕНИЕ Моноблок КРУ "Онега-М" ТУ 3414-032-45567980-2003 комплектное распределительное устройство, предназначеное для работы в составе распределительных устройств трехфазного переменного тока частотой 50 Гц, номинальным напряжением (6) 10 кВ, с...»

«Willkommen! Powitanie! Welcome! Bienvenue! Hoan nghnh! добро пожаловать! Foto: Doris Seyfert aus Fotowettbewerb von myheimat www.integration-burgenlandkreis.de In dieser Willkommensbroschre finden Sie: Seite Trger Angebot 2 Burgenlandkreis Vorwort des Land...»

«LUMINA 17 r LUMINA 17 LUMINA 17 TOUCH F17 РУКОВОДСТВО ПО УСТАНОВКЕ ВЕРСИЯ A2 Примечание. Исходной и подлинной версией настоящего руководства является его версия на английском языке, выпущенная фирмой Fancom B.V. или одной из ее дочерних ком...»

«АННОТАЦИЯ Выпускная работа 107 с., 14 табл., 14 источн., 6 л. графич. материала. ВОДЯНОЕ ОТОПЛЕНИЕ, ГАЗОВОЕ ОТОПЛЕНИЕ, ТЕПЛОВЫЕ ПОТЕРИ, КОМФОРТНЫЕ УСЛОВИЯ, ГИДРАВЛИЧЕСКИЙ РАСЧЕТ, АЭРОДИНАМИЧЕСКИЙ РАСЧЕТ, РАСЧЕ...»

«MOLANA Эндопротез первого плюснефалангового сустава Разработка и производство керамических имплантатов из цирконоксидной керамики СОДЕРЖАНИЕ Эндопротез плюснефалангового сустава Инструменты для установки Хирургическая техника Реабилитация Важна...»

«Руководство пользователя по настройке и запуску RetailRotor на устройствах Android. Установка Для того чтобы установить приложение RetailRotor на устройство с операционной системой (ОС) Android потребуется подключить...»

«­ уть­ П преодоления­кризиса Сибирская Благозвонница Москва если сегодня мир переживает эконо мический кризис, значит, суд Божий обнаруживает некую глобальную человеческую непр...»

«2010 ВЕСТНИК ПОЛОЦКОГО ГОСУДАРСТВЕННОГО УНИВЕРСИТЕТА. Серия Е ЗДОРОВЫЙ ОБРАЗ ЖИЗНИ УДК 504.75 ТЕОРЕТИКО-МЕТОДОЛОГИЧЕСКИЕ И ПРАКТИЧЕСКИЕ ОСНОВЫ ФОРМИРОВАНИЯ ЗДОРОВОГО ОБРАЗА ЖИЗНИ канд. пед. наук, доц. В.М. НАСКАЛОВ, д-р мед. наук, проф. А.Н. ИЛЬНИЦКИЙ, В.Н. СПАЩАНСКА...»

«ПОЯСНИТЕЛЬНАЯ ЗАПИСКА Программа составлена на основе письма Министерства образования России "О деятельности музеев образовательных учреждений" от 12.03. 2003г., Примерного положения о музее образовательного учреждения, программы "Туристскокраеведческого движения обучающихся...»

«ТРИНАДЦАТЫЙ АРБИТРАЖНЫЙ АПЕЛЛЯЦИОННЫЙ СУД ПОСТАНОВЛЕНИЕ от 4 марта 2010 г. по делу N А42-3764/2009 Резолютивная часть постановления объявлена 04 марта 2010 года Постановление изготовлено в полном объеме 04 марта 201...»

«UB 90 / UB 100 UB 90 UB 100 D Wrme-Unterbett UB 90. I Coprimaterasso termico spann-Wrme-Unterbett UB 100 UB 90 / UB 100 Gebrauchsanweisung G electric underblanket UB 90. T istmal Yatak alt UB 90 fitted-type heated underblanket. Dek zerine gerilen elektrikli UB 100 battaniye UB 100 instruction for Use F Chauffe-ma...»

«18 апреля – День победы русских воинов князя Александра Невского над немецкими рыцарями на Чудском озере, 1242 год Не в силе Бог, а в правде. (князь Александр Невский) Середина XIII века — время суровых испытаний для Руси. Воспользовавшись нашествием татаро-монгол под предводительством Батыя и ослаблением русских княжеств, в северо-зап...»

«1 Аклима: дистрибьютор климатического оборудования Компания Аклима (Aclima) представляет на украинском рынке системы вентиляции и кондиционирования от ведущих мировых производителей. Сегодня в пул нашей компании входит 17 брендо...»

«Оценка грамматической стороны речи и связной речи Оценка грамматической стороны речи и связной речи Оценка связной речи. Постепенно у ребенка развивается способность описывать, объяснять, пересказыват...»

«ПРАКТИЧЕСКОЕ ПОСОБИЕ ДЛЯ ЧЛЕНОВ ОБЩЕСТВЕННЫХ НАБЛЮДАТЕЛЬНЫХ КОМИССИЙ 3-е издание, дополненное и переработанное При реализации проекта используются средства государственной поддержки, выделенные в качестве гранта в...»










 
2017 www.book.lib-i.ru - «Бесплатная электронная библиотека - электронные ресурсы»

Материалы этого сайта размещены для ознакомления, все права принадлежат их авторам.
Если Вы не согласны с тем, что Ваш материал размещён на этом сайте, пожалуйста, напишите нам, мы в течении 1-2 рабочих дней удалим его.