Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Диплом.docx
Скачиваний:
44
Добавлен:
05.06.2015
Размер:
2.39 Mб
Скачать

Раздел 1 «специальный»

Программный комплекс автоматизации телефонных соединений

Консультант:

доцент каф. САУиК

В.Т.Николаев

Выполнил:

студент гр.ЭТМО-51

С.О.Шимков

    1. Обзор существующих программных продуктов

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

В качестве сервера телефонии используется AsteriskIP-PBX. В качестве системы ведения учета – 1С:Предприятие. Данные программные продукты выбраны путем глубокого анализа существующих продуктов. Существует множество решений телефонии и систем учета. Самыми известными серверами телефонии являются Cisco, Avaya, Asterisk и Infinity. Рассмотрим их подробнее.

Cisco предоставляет комплексное решение, включающее вычисление, сеть, сеть хранения данных, безопасность и сервисы L4-7. Кроме того, они предоставляют большое количество опций и много возможностей масштабирования центров обработки данных. Cisco предоставляет FEX (Fabric Extenders) для Gigabit Ethernet, унифицированные порты. С помощью DCB (Data Center Bridging) позволяет основанной на Ethernet сети транспортировать трафик LAN/SAN.Однако настройка потребует значительных материальных затрат от компании. Так же, даже имея на руках инструкцию, далеко не каждый системный администратор сможет разобраться в начинке. Для администрирования этой системы необходимо приобрести лицензию, а так же оплатить обучение своего специалиста, либо вызвать для наладки специалиста из Cisco, что тоже далеко не дешево. Добавление новых пользователей, расширение количества IP-телефонов и т.д. стоит вложений. А для того, чтобы установить дополнительные модули интерфейсов VOIP, FANSO или другие платы, компании потребуются дополнительные лицензии. Cisco производит собственное оборудование, так что если компании не хватает бюджета для полностью укомплектованного АТС с соответствующей маркировкой, возможности поставить себе ПО так же отпадает. Cisco адекватно работают с любой АТС, но вот с АТС Cisco – работают далеко не всеIP-телефоны.Cisco предлагаетполныйкомплектвиртуализированныхпродуктов: Cisco Nexus 1000V Series Switches, Virtualized Security Gateway (VSG), virtual network access module (vNAM) и Virtual Wide Area Application Services (vWAAS). Но расширяемость ПО Cisco серьезно ограничена, когда дело доходит до интеграции единых поставщиков связи.

Avaya похожа на Cisco в плане предоставляемых услуг. Это такая же готовая АТС с оборудованием собственного производства. Надежность здесь, как правило, исходит из стоимости. Настроить её может обученный специалист, если разберется в такой же замысловатой инструкции. Call-центр Avaya применяет технологии распознавания человеческого голоса, что при исходящем обзвоне увеличивает количество соединений с человеком, а не с IVR, факсом или голосовой почтой. Avaya работает с системой Communication Manager, которая в свою очередь вышла из MutiVantage. Из-за чего, по моему мнению, имеется много ненужных по сути функций, настроек и т.д. Например, то же качество документации изрядно страдает.

Infinity выигрывает удобством и простотой: у неё большой функционал по статистики, по сбору информации из IVR. В отличие от других платформ с преимущественно древовидным IVR-меню, Infinity постарались и сделали его графическим. Оно хорошо визуализировано, а это не только приятно глазу, но и удобно. Огромным минусом является то, что Infinity работает на Windows, в то время как остальные предпочитает более надежный Linux. Для Asterisk тоже есть подобные интерфейсы, но они идут в виде отдельных модулей, в том числе и OpenSource.

Asterisk более всего выделяется из этой группы одним существенным показателем: на ПО Asterisk не нужны дополнительные лицензии, как при подключение дополнительных SIP-номеров, так и при интеграции с подключаемыми модулями[1]. Тем более, найти соответствующие темы в форумах по настройке Asterisk может любой системный администратор. Инструкции в разы понятнее и удобнее [2]. А значит, не надо тратить лишние деньги на обучение или вызов технической поддержки.

У системы автоматизации 1С:Предприятие так же существует довольно много конкурентов:

  • ИНФИН;

  • Парус;

  • Галактика;

  • БЭСТ;

  • ИнфоБухгалтер;

  • Турбо Бухгалтер (он же ТБ Корпорация);

  • Компас;

  • КомТех;

  • Инотек;

  • ИнфоСофт (продукт Флагма);

  • Омега;

  • Контур;

  • SAP;

  • Oracle.

Самой распространенной системой управления предприятием в крупном бизнесе является SAP. Доля данного программного продукта составляет 49,6 % от всего рынка интегрированных систем управления предприятием. За ним идетOracleс 14,9 % и 1С:Предприятие с 14,4 %. Однако, в среднем и малом бизнесе картина кардинально меняется. Лидером в этом секторе является 1С:Предприятие, имеющееся практически у каждой компании.

Исходя из полученных данных и специфики работы компании было решено разработать систему автоматизации телефонных соединений на базе 1С и Asterisk. На данный момент уже имелось несколько разработок от сторонних компаний. Среди них самыми известными являются:

  • 1С-Рарус:СофтФон;

  • Панель телефонии Asterisk 1С от MyAsterisk;

  • 1С:Телефония от Simplit.

Рассмотрим каждый продукт подробнее.

Программный продукт «1С-Рарус: СофтФон, Проф, ред. 1» выпускается компанией «1С-Рарус». Решение обеспечивает интеграцию телефонной системы с CRM-модулем типовых конфигураций «1С:Предприятие 8.0». Тесная интеграция телефонной системы с CRM-системой обеспечивает быстрый доступ к информации о клиенте и способствует внедрению CRM-технологий (CRM — Customer Relations Management). Программа помогает в работе диспетчерского отдела, справочной службы, отдела продаж и маркетинга.

Панель телефонии Asterisk 1С от MyAsterisk обеспечивает связь между 1С:Предприятие и IP АТС на базе телефонии Asterisk 1.6 и выше. Реализованы следующие возможности:

  • прием звонков в 1С;

  • звонки из 1С телефонии;

  • встроенный софтфон;

  • удержание вызовов;

  • перевод вызовов;

  • отображение информации о звонящем;

  • автоматическое открытие карточки;

  • привязка клиентов по номеру телефона на определенного менеджера;

  • прослушивание записей разговоров из 1С Asterisk;

  • отправка факсов из 1С через Fax Asterisk;

  • прием СМС в 1С.

1С:Телефония от Simplit представляет из себя дополнительную DLL-библиотеку для 1С, с помощью которой организован сетевой обмен данными с сервером IP-телефонии на базе Asterisk. Функции аналогичны панели телефонии Asterisk 1С от MyAsterisk.

Если рассматривать данные программные продукты более детально, то оказывается, что ни один из них не подходит под все требования. 1С-Рарус:СофтФон не может работать с Asterisk. Панель телефонии Asterisk 1С от MyAsterisk не поддерживает современный управляемый режим запуска 1С и, соответственно, не будет работать в браузере и в тонком клиенте.Управляемые формы – новая концепция интерфейса программ 1С, позволяющая, кроме всего прочего, работать через браузер. В ближайшее время все программы 1С: Предприятие перейдут на управляемые формы. Так же у нее отсутствует шифрование данных, что делает небезопасной работу через интернет. 1С:Телефония от Simplit подходит практически всем, однако большая часть логики перенесена в 1С, что не эффективно. К тому же компания Simplit находится в Киеве и в основном ориентирована на Украину. Таким образом, техническая поддержка и сопровождение их продукта в России будет значительно затруднено.

Проанализировав недостатки существующих систем интеграции телефонии, было решено разработать собственный продукт, лишенный этих недостатков. При разработке учитывалась возможность работы в разных конфигурациях 1С:Предприятия и разных режимах запуска. Выбор языка разработки C# позволит, при необходимости, быстро разработать компоненту соединения с сервером для других операционных систем и даже для мобильных устройств (смартфонов). Поддерживаются все новые версии сервера телефонии, начиная с 1.4 и все 103 команды управления сервером, что позволяет адаптировать решение под любые требования клиента. Так же реализовано шифрование соединения с сервером телефонии.

1.2 Выбор среды разработки

Интегрированная среда разработки, ИСР (англ. IDE, Integrated development environment или integrated debugging environment) — система программных средств, используемая программистами для разработки программного обеспечения (ПО) [4].

Среда разработки включает в себя:

    • текстовый редактор;

    • компилятор и/или интерпретатор;

    • средства автоматизации сборки;

    • отладчик.

ИСР иногда содержит также средства для интеграции с системами управления версиями и разнообразные инструменты для упрощения конструирования графического интерфейса пользователя. Многие современные среды разработки также включают браузер классов, инспектор объектов и диаграмму иерархии классов — для использования при объектно-ориентированной разработке ПО. Хотя и существуют ИСР, используемые для нескольких языков программирования — такие, как Eclipse, NetBeans, Embarcadero RAD Studio, Qt Creator или Microsoft Visual Studio, но обычно в ИСР используется один определённый язык программирования – как, например, Visual Basic, Delphi, Dev-C++.

Частный случай ИСР — среды визуальной разработки, которые включают в себя возможность визуального редактирования интерфейса программы.

Интегрированные среды разработки были созданы для того, чтобы максимизировать производительность программиста благодаря тесно связанным компонентам с простыми пользовательскими интерфейсами. Это позволит разработчику сделать меньше действий для переключения различных режимов, в отличие от дискретных программ разработки. Однако, так как IDE является сложным программным комплексом, то лишь после долгого процесса обучения среда разработки сможет качественного ускорить процесс разработки ПО.

IDE обычно представляет из себя единственную программу, в которой проводилась вся разработка. Она обычно содержит много функций для создания, изменения, компилирования, развертывания и отладки программного обеспечения. Цель среды разработки заключается в том, чтобы абстрагировать конфигурацию, необходимую, чтобы объединить утилиты командной строки в одном модуле, который позволит уменьшить время, чтобы изучить язык, и повысить производительность разработчика. Также считается, что трудная интеграция задач разработки может далее повысить производительность. Например, IDE позволяет проанализировать код и тем самым обеспечить мгновенную обратную связь и уведомить о синтаксических ошибках. В то время как большинство современных IDE является графическим, они использовались еще до того, как появились системы управления окнами (которые реализованы в Microsoft Windows или X11 для *nix-систем). Они были основаны на тексте, используя функциональные клавиши или горячие клавиши, чтобы выполнить различные задачи (например, Turbo Pascal). Использование IDE для разработки программного обеспечения является прямой противоположностью способа, в котором используются несвязанные инструменты, такие как vi (текстовый редактор), GCC (компилятор), и т.п.

На данный момент существуют несколько сред для разработки приложений на языке C#, основные из них приведены в таблице 1.1.

Таблица 1.1 - Сравнение сред разработки C#

Среда разработки

Разработчик

Платформа

Лицензия

Geany

Team

UNIX / Windows

GPL

Microsoft Visual Studio

Microsoft

Windows

Закрытая

MonoDevelop

Novell и Mono community

Cross-platform

GPL

SharpDevelop

ICSharpCode Team

Windows

LGPL

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

Лицензия LGPL позволяет линковать с данной библиотекой или программой программы под любой лицензией, несовместимой с GNU GPL, при условии, что такая программа не является производной от объекта, распространяемого под (L)GPL, кроме как путём линкования. Главное различие между GPL и LGPL в том, что последняя позволяет и такое линкование с данным объектом других, которое создаёт производную от данного работу, если лицензия слинкованных объектов позволяет «модификации для внутреннего использования потребителем и обратную разработку для отладки таких модификаций». Т.е. LGPL, в отличие от GPL позволяет связывание библиотеки с любой программой, не обязательно свободной.

Закрытое (проприетарное) программное обеспечение (англ. proprietary software) — программное обеспечение, являющееся частной собственностью авторов или правообладателей и не удовлетворяющее критериям свободного ПО (наличия открытого программного кода недостаточно). Правообладатель проприетарного ПО сохраняет за собой монополию на его использование, копирование и модификацию, полностью или в существенных моментах. Обычно проприетарным называют любое несвободное ПО, включая полусвободное.

Geany — свободная среда разработки программного обеспечения, написанная с использованием библиотеки GTK2. Доступна для следующих операционных систем: BSD, Linux, Mac OS X, Solaris и Windows. Geany распространяется согласно GNU General Public License. Geany не включает в свой состав компилятор. Вместо этого используется GNU Compiler Collection (или любой другой компилятор) для создания исполняемого кода.

Microsoft Visual Studio — линейка продуктов компании Майкрософт, включающих интегрированную среду разработки программного обеспечения и ряд других инструментальных средств. Данные продукты позволяют разрабатывать как консольные приложения, так и приложения с графическим интерфейсом, в том числе с поддержкой технологии Windows Forms, а также веб-сайты, веб-приложения, веб-службы как в родном, так и в управляемом кодах для всех платформ, поддерживаемых Microsoft Windows, Windows Mobile, Windows CE, .NET Framework, .NET Compact Framework и Microsoft Silverlight. Visual Studio включает в себя редактор исходного кода с поддержкой технологии IntelliSense и возможностью простейшего рефакторинга кода. Встроенный отладчик может работать как отладчик уровня исходного кода, так и как отладчик машинного уровня. Остальные встраиваемые инструменты включают в себя редактор форм для упрощения создания графического интерфейса приложения, веб-редактор, дизайнер классов и дизайнер схемы базы данных. Visual Studio позволяет создавать и подключать сторонние дополнения (плагины) для расширения функциональности практически на каждом уровне, включая добавление поддержки систем контроля версий исходного кода (как например, Subversion и Visual SourceSafe), добавление новых наборов инструментов (например, для редактирования и визуального проектирования кода на предметно-ориентированных языках программирования или инструментов для прочих аспектов цикла разработки программного обеспечения (например, клиент Team Explorer для работы с Team Foundation Server).

MonoDevelop — свободная среда разработки, предназначенная для создания приложений C#, Java, Boo, Nemerle, Visual Basic .NET, Vala, CIL, C и C++. Также планируется поддержка Oxygene со стороны Embarcadero Technologies. Изначально это был порт SharpDevelop на Mono/GTK+, но с того времени проект далеко ушёл от своего начального состояния. MonoDevelop является частью проекта Mono.

SharpDevelop — свободная среда разработки для C#, Visual Basic .NET, Boo, IronPython, IronRuby, F#, C++. Обычно используется теми, кто не хочет пользоваться Visual Studio .NET. Существует также форк на Mono/Gtk+ — MonoDevelop. SharpDevelop 2.0 предоставляет интегрированный отладчик, который использует собственные библиотеки и взаимодействует с исполняющей средой .NET через COM Interop. Хотя SharpDevelop 2.0 (как и VS2005) использует файлы проекта в формате MSBuild, он по-прежнему может использовать компиляторы от .NET Framework 1.0 и 1.1, а также от Mono.

Для разработки необходимо активно использовать все средства языка программирования. Однако среда MonoDevelop использует собственный компилятор, который не полностью поддерживает язык С# в силу того, что является свободной мультиплатформенной разработкой, независимой от создателей языка. Хотя она и обеспечивает мультиплатформенность, но невозможно предсказать поведение языка в новых версиях. А одной из ключевых составляющих проекта является его отказоустойчивость и стабильность и в то же время мультиплатформенность не требуется (пользователей 1С на Linux исчезающе мало). Поэтому эта среда не подходит для разработки данного проекта.

SharpDevelop и Geany не имеют собственных компиляторов. Поэтому для разработки с использованием этих сред все равно придется использовать проприетарное ПО, что делает их использование оправданным лишь в некоторых случаях. Например на низкопроизводительных компьютерах или при сильно ограниченном бюджете проекта. Несмотря на то, что что они могут запускаться и работать в ОС Linux, данные среды разработки в силу отсутствия собственных компиляторов не смогут создать мультиплатформенное приложение, и разработка все равно ограничится операционными системами Windows.

Microsoft Visual Studio также не лишена недостатков. Основными из них являются тяжеловесность, требующая довольно большой вычислительной мощности компьютера; платность; отсутствие мультиплатформенности. Несмотря на эти недостатки, Visual Studio остается предпочитаемой средой разработки большинства C# программистов. Причиной этому является полная поддержка языка, расширенные средства разработки, энергично развивающаяся документация и сама среда. Данную среду разработки будем использовать в проекте.

1.3 Разработка графического интерфейса пользователя

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

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

  • простота – возможность интуитивного понимания пользователем элементов интерфейса;

  • использование стандартных элементов 1С - обеспечение единого интерфейса 1С и панели телефонии как в обычном, так и в управляемомприложении;

  • масштабируемость – возможность легко настраивать и расширять как интерфейс, так и само приложение при увеличении числа пользователей, количества линий и иных требований заказчика;

  • адаптивность к действиям пользователя – приложение должно допускать возможность ввода данных и команд множеством разных способов (клавиатура, мышь, другие устройства), кроме того, программа должна учитывать возможность перехода и возврат от окна к окну, от режима к режиму, и правильно обрабатывать такие ситуации;

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

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

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

  • сокращению длительности операций звонка выбранному контрагенту;

  • уменьшению времени поиска информации о контрагенте;

  • повышению общей продуктивности пользователя, заключающейся в объёме обработанных звонков за определённый период времени;

  • увеличению длительности устойчивой работы пользователя и др.

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

  • прозрачной для пользователя навигации и целевой ориентации в программе;

  • ясности и чёткости понимания пользователем текстов и значения икон;

  • быстроты обучения при работе с программой, для чего необходимо использовать преимущественно стандартные элементы взаимодействия, их традиционное или общепринятое их расположение;

Исходя из данных требований, выбор интерфейса для проектируемой системы очевиден. Это близкий любому пользователю интерфейс 1С. Он во многом повысит удобство использования при эксплуатации приложения. Также интерфейс 1С отличается простотой настройки и использования.

Разработка интерфейса ведется в конфигураторе 1С. Существуют два типа форм – обычные и управляемые. В режиме управляемого приложения интерфейс не «рисуется», а «описывается». Разработчик определяет только общую схему командного интерфейса и общую схему форм. Это описание платформа использует при построении интерфейса для конкретного пользователя с учетом различных факторов: прав пользователя, особенностей конкретного внедрения, настроек, сделанных самим пользователем.Разрабатываемое приложение должно одинаково работать на обоих типах форм.

Главное окно приложения должно состоять из нескольких функциональных частей. Должно быть главное окно приложения и закладка с настройками подключения. В главном окне необходимо реализовать возможность набрать введенный номер и отклонить звонок. При этом кнопка для принятия звонка не нужна, так как телефон сам оповестит об этом сервер при ответе на звонок. Таким образом, необходимо поле ввода номера телефона с кнопкой очистки и кнопки “Набрать” и “Отбой”. Однако на устройствах с сенсорными экранами неудобно будет для набора номера каждый раз вызывать клавиатуру, поэтому необходим блок кнопок набора номера. Разместим их под полем с номером телефона. Теперь необходимо отображать информацию о собеседнике. Для этого подойдут поля с его номером и именем. Так же необходимо сделать поддержку двух линий. Поэтому добавим еще 2 поля с номером и именем собеседника по второй линии. При отсутствии звонков эти поля нам не требуются, поэтому сделаем их поумолчанию неактивными. При разговоре по первой линии может так случиться, что не будет возможности ответить на входящий вызов по второй. Поэтому для второй линии необходимо сделать быстрый перевод на другого сотрудника. Для этого добавим кнопку “Перевести” для второй линии. При нажатии на кнопку будет появляться выпадающий список с именами остальных сотрудников. При выборе определенного сотрудника будет произведен быстрый (без консультации) перевод звонка. Однако при разговоре может потребоваться перевод на другого сотрудника текущего звонка. Для этого добавим кнопку “Перевести” и для первой линии. Причем, если текущий пользователь на звонок еще не ответил, логичным будет перевести звонок без консультации, то есть без предварительного разговора с другим сотрудником. И напротив, при отвеченном вызове и необходимости перевода правильнее при переводе звонка сначала поговорить с сотрудником о контрагенте, целях звонка, и только потом переводить. Поэтому кнопки “Перевести” для обоих линий будут выполнять немного разные переводы в зависимости от того, был ли входящий вызов отвечен. При отвеченном вызове произойдет перевод с консультацией, при не отвеченном - перевод без консультации. Так же при одновременных звонках на обе линии необходимо давать возможность отклонять любую линию. Поэтому требуется как минимум еще одна кнопка “Отбой”. Однако для избежания перекосов интерфейса и возможных затруднений со стороны пользователя необходимо добавить кнопку “Отбой” и для второй линии. Таким образом получится три кнопки “Отбой” на форме - по одной для каждой линии и одна общая рядом с кнопкой “Набрать” и полем ввода номера телефона. Логично предположить, что общая кнопка должна завершать последний вызов. То есть при одной активной линии она будет завершать текущий звонок, а при двух активных линиях - звонок по второй. Так же необходима кнопка поиска номеров по контрагенту. Расположим ее рядом с полем ввода номера. На этом основная функциональность главного окна реализована. Можно позвонить любому контрагенту, набрав его номер на клавиатуре или на экранных кнопках или же найдя его в списке, окончить телефонное соединение, перевести вызов на другого сотрудника, поддерживать 2 телефонных соединения одновременно.

Однако необходимо дополнительно реализовать возможности по просмотру истории звонков, отправке смс и факса. Кнопку “История звонков” лучше разместить в основной части формы, недалеко от кнопок “Набрать” и “Отбой”. При нажатии на эту кнопку должно выводиться окно с историей звонков на определенную дату.

Не остались рассмотренными две возможности: посылка факса и смс. СМС обычно отправляют вне активного голосового соединения. Поэтому разместим кнопку “СМС” в основной части формы, рядом с кнопкой “История звонков”. При нажатии на кнопку отображается окно с полем ввода текста смс и двумя кнопками: “Послать” и “Отмена”. Номер для отправки берется из поля номер телефона главной формы. Факс же часто бывает необходимо отправить во время разговора, прямо на тот номер, с которым установлено соединение. Поэтому разместим две кнопки “Факс” для первого и второго каналов. При нажатии открывается стандартный диалог выбора файла и пользователем выбирается документ, который и отправляется по факсу. На этом функции основной закладки главного окна закончились.Главное окно панели телефониив управляемом режиме показано на рисунке 1.1.

Рисунок 1.1 – Главное окно панели телефонии

Теперь необходимо рассмотреть закладку “Настройки”. На этой вкладке необходимо собрать все параметры, передаваемые во внешнюю компоненту для подключения и работоспособности панели. Для начала необходимо определиться с адресом сервера телефонии. Для этого добавим два поля ввода: “Адрес астериска” и “Порт AMI (5038)”. 5038 - стандартный порт интерфейса AMI. Далее необходимо авторизоваться на сервере. Для этого добавим еще два поля: “Имя пользователя” и “Пароль”. Следом необходимо поле для указания контекста звонков. Это служебное поле Астериска для правильной адресации. Также необходимо предусмотреть флажок “Открывать карточку” для открытия карточки контрагента при входящем звонке от него и флажок “Отладочные сообщения” для вывода отладочных сообщений при возникновении проблем панелью. В режиме отладочных сообщений будут отображаться в панели служебных сообщений практически все сообщения от внешней компоненты и частично от Астериска. По полученной информации можно быстро локализовать и исправить проблему. Следующим полем будет поле флажка “Не учитывать код города”. При установленном флажке код города будет автоматически выбрасываться из номера. Код города указывается чуть ниже в одноименном поле ввода. Это необходимо, если провайдер телефонии требует строго определенный формат номера телефона. Так же, флажок “Не учитывать код города” необходим, если в информационной базе 1С:Предприятия возможны ситуации, когда код города не заполнен или заполнен неверно. Далее необходимо предусмотреть кнопку “Сохранить”, нажатие на которую сохранит изменения в настройках. Для принятия новых настроек необходимо переподключаться к серверу Asterisk, поэтому добавим кнопку “Переподключиться к серверу” для завершения текущего соединения с сервером и установления нового. Так же при проблемах с соединением необходимо предусмотреть проверку доступности сервера. Для этого добавим кнопку “Пинг соединения с сервером”, нажатие на которую посылает специальный пакет серверу. В случае доступности сервера, установленного соединения и успешной авторизации сервер вернет пакет “Pong”. В окне служебных сообщений появится строка “Был получен ответ сервера Ping: Pong”, а это значит, что сервер телефонии Asterisk доступен и работает. Данная вкладка будет практически одинаково выглядеть как в управляемом режиме запуска 1С:Предприятия, так и в обычном. Настройки панели телефонии в управляемом режиме показаны на рисунке 1.2.

Рисунок 1.2 – Закладка “Настройки”

Нажатие на копку «История звонков» открывает новое окно. В окне истории звонков должно быть поле выбора даты, на которую получаем историю звонков. При изменении даты необходимо сразу же получать историю без дополнительных действий со стороны пользователя. При открытии формы дату следует устанавливать равной текущей дате. Так же в этом окне необходимо отображать табличную часть, в которой описаны сами звонки. В каждой строке - один звонок. Для каждого звонка должны быть указаны:

  • дата и время начала звонка;

  • тип (входящий или исходящий);

  • номера телефонов и имена обеих сторон;

  • длительность звонка;

  • отвечен или нет;

  • дата и время окончания звонка.

Для каждого номера, если он занесен в базу 1С, необходимо дать пользователю возможность вызвать карточку того контрагента или клиента, для которого назначен номер в текущей строке истории звонков. Для этого разместим на форме кнопку “Инфо”. Так же необходимо предоставить возможность позвонить по любому из телефонов в истории (разумеется, кроме телефонов текущего пользователя). Для этого разместим в шапке формы, рядом с полем выбора даты, кнопку “Позвонить”. При нажатии на нее происходит вызов номера, указанного в текущей строчке истории. АТС Asterisk по умолчанию записывает все разговоры, поэтому можно разместить на форме кнопку “Прослушать”. При нажатии на эту кнопку формируется команда Астериску для прослушивания разговора в текущей строке. Астериск, в свою очередь, совершает звонок на телефон пользователя и начинает воспроизводить ранее записанный телефонный вызов при поднятии трубки. На этом функциональность данного окна можно считать исчерпанной. Окно истории звонков в управляемом режиме показано на рисунке 1.3.

Рисунок 1.3 – История звонков

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

1.4 Разработка алгоритма программы

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

Поскольку для построения компонентов системы, а также для построения внедряемых в компоненты элементов выбрана технология Microsoft .Net Framework, то следует воспользоваться всеми её преимуществами.

.NET Framework — программная платформа, выпущенная компанией Microsoft в 2002 году. Основой платформы является исполняющая среда Common Language Runtime (CLR), способная выполнять как обычные программы, так и серверные веб-приложения. NET Framework поддерживает создание программ, написанных на разных языках программирования [5].

Основной идеей при разработке .NET Framework являлось обеспечение свободы разработчика за счёт предоставления ему возможности создавать приложения различных типов, способные выполняться на различных типах устройств и в различных средах. Вторым принципом стало ориентирование на системы, работающие под управлением семейства операционных систем Microsoft Windows. Для обеспечения максимальной переносимости компоненты будем использовать только управляемый код C#.

Управляемый код (англ. managed code) — термин, введённый Microsoft для обозначения кода программы, исполняемой под «управлением» виртуальной машины .NET — Common Language Runtime. При этом обычный машинный код называется неуправляемым кодом. Программа для .NET Framework, написанная на любом поддерживаемом языке программирования, сначала переводится компилятором в единый для .NET понятный человеку низкоуровневый язык Common Intermediate Language (CIL) (ранее назывался Microsoft Intermediate Language, MSIL). Затем компилятор производит перевод CIL-кода в объектный байт-код (в терминах .NET получается сборка, англ. assembly), а уже байт-код либо исполняется виртуальной машиной CLR, либо транслируется утилитой NGen.exe в исполняемый код для конкретного целевого процессора. Использование виртуальной машины предпочтительно, так как избавляет разработчиков от необходимости заботиться об особенностях аппаратной части. В случае использования виртуальной машины CLR, встроенный в неё JIT-компилятор «на лету» (just in time) преобразует промежуточный байт-код в машинные коды нужного процессора. Современная технология динамической компиляции позволяет достигнуть высокого уровня быстродействия. Виртуальная машина CLR также сама заботится о базовой безопасности, управлении памятью и системе исключений, избавляя разработчика от части работы.

Для начала рассмотрим основные аспекты протокола AMI. AMI — мощный и удобный программный интерфейс (API) Asterisk для управления системой из внешних программ. Благодаря AMI внешние программы могут осуществлять соединения с Астериском посредством TCP протокола, инициировать выполнение команд, считывать результат их выполнения, а так же получать уведомления о происходящих событиях в реальном времени [3].

Между сервером Астериск и клиентской программой для установления связи используется простой построчный протокол. Пакет, передаваемый от клиента в Астериск сервер и назад, определяется следующими ключевыми характеристиками:

  • Action, пакеты отправляемые клиентом, соединенным с AMI. После обработки сервером такого пакета будет осуществлено некоторое действие. Существуют относительно гибкие ограничения на действия, осуществляемые клиентом. Один пакет - одно действие. В пакете Action должно содержаться имя операции, которую необходимо выполнить, а также все необходимые параметры;

  • Response, определяет ответ, отсылаемый Астериском клиенту по факту выполнения действия;

  • Event, данные, относящиеся к событию, которое сгенерировано внутри ядра Астериска или модуля расширения.

Как правило, клиент отсылает пакеты Action в Астериск (они так же называются командами). Астериск, в свою очередь, выполняет запрос и возвращает результат (часто результат – успешность действия с кратким описанием в случае неудачи), получаемое в пакете Response. Нет гарантии касательно порядка прихода результатов (пакетов Response), поэтому в клиентском запросе обычно включают параметр [ActionID] в каждый пакет Action, при этом соответствующий пакет Response будет содержать такое же значение в поле [ActionID]. Таким образом, клиент может обрабатывать Action и Response пакеты, в любом желаемом порядке, не ожидая пакетов Response, чтобы произвести следующее действие.

Передача данных в AMI похожа на обычный Telnet. Для управления сервером используются пакеты Action, а возвращает он Response или Event. Пакет представляет собой комбинацию строк, разделенных символами перевода строк (CRLF). Окончанием пакета служат два подряд идущих символа перевода строки. Общий формат пакетов управления сервером показан в распечатке 1.1.

Распечатка 1.1

Action: <action type><CRLF>

<key 1>: <value 1><CRLF>

<key 2>: <value 2><CRLF>

.......

<CRLF>

Параметр “action type” указывает название действия. Далее идут пары “ключ: значение”, определяющие необходимые переменные. Аналогичный вид у пакетов Response и Event.

Самым простым решением будет написание процедур, которые будут вызываться из 1С и которые будут посылать серверу сформированный пакет. Таким образом можно значительно уменьшить количество параметров у процедур, так так некоторые пары “ключ: значение” являются константами и их можно задать один раз для всех пакетов.

Одной из таких констант является “ActionID”. Это необязательный, но крайне необходимый параметр. Дело в том, что если к серверу будут подключены одновременно несколько клиентов, то возникнет путаница с ответами. Без “ActionID” невозможно точно определить кому предназначается ответный пакет. Это маркер клиента, который он использует для определения себя из общего потока. Он возвращается сервером в ответных пакетах и так клиент понимает, что пакет предназначается именно ему. Данная константа будет генерироваться случайным при запуске компоненты. Для того, чтобы у двух компонент не было одинаковых “ActionID” выберем числа много большие, чем количество пользователей. Так, максимальное число пользователей будет 50-100, а случайные числа будут порядка 100000-1000000. Таким образом вероятность получения двух одинаковых “ActionID” будет крайне низка.

Для начала необходимо реализовать функцию, получающую строку от сервера. Есть два способа получать данные: посимвольно и построчно. При посимвольном способе в цикле из пришедших данных за раз можно получить только один символ. В ходе тестирования данный метод показал неудовлетворительные результаты: для получения одного символа необходимо пройти полный цикл получения символа от сервера телефонии Asterisk, что приводит к повышенной нагрузке на процессор при большом объеме передаваемых данных. В процессе тестирования наблюдалась полная загрузка процессора уже при восьми активно работающих телефонных аппаратах. При большем количестве использование панели телефонии Asterisk было невозможно. Поэтому в данном программном продукте был выбран второй способ, в котором используется построчное чтение данных от сервера. При построчном чтении данные с сетевой карты сначала помещаются в буфер, а при вызове функции чтения данные из буфера передаются в программу одной строкой, в виде типа String. Для начала необходимо определить какое максимальное количество байт мы можем принять за раз. Для этого используется свойство ServicePoint.ReceiveBufferSize, которое возвращает размер приемного буфера для сокета. Размер буфера будет равен количеству символов в строке, которую мы получим при чтении из него. При этом можно оптимизировать период цикла чтения из буфера, добившись максимальной производительности. Однако, при построчном чтении из буфера часто возникает ситуация, когда количество данных будет больше, чем размер буфера. В таком случае последний пакет от Asterisk может быть не полным, и последующий его разбор может привести к ошибкам. Для исключения этой возможности необходимо при каждом чтении из потока проверять получен ли последний пакет целиком. Проще всего это сделать, найдя последний символ перевода строки и последний двойной символ перевода строки. Если двойной символ будет стоять раньше на 2 символа (“\r\n”), чем простой перевод строки, значит, пакет получен полностью и можно принимать следующие данные. Если же положения символов не совпадают (что в большинстве случаев и происходит), тогда последний пакет получен не целиком, необходимо сохранить его начало и добавить при следующем чтении из потока.Для этого ищется последний двойной символ перевода строки, и в отдельную переменную заносятся все данные, идущие после данного символа. В текущей строке данные, стоящие после символа двойного перевода строки, удаляются. Схема алгоритма подпрограммы получения строки от сервера телефонии показана на рисунке 1.4.

Рисунок 1.4– Схема алгоритма подпрограммы получения строки от Asterisk

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

  • входящий вызов;

  • окончание соединения с абонентом;

  • исходящий вызов для получения названий каналов;

  • история звонков;

  • пакеты “Response”.

Канал – соединение каждого абонента. Он необходим для избегания путаницы когда, например, один абонент принимает одновременно два звонка. Новый канал создается для каждого соединения абонента. Поэтому при разговоре двух абонентов каналов будет два – по одному для каждого абонента.

Остановимся на каждом из видов подробнее. Входящий и исходящий вызовы можно объединить, так как в данном случае будут меняться местами только номера каналов. Для начала необходимо определить пакет. Он будет начинаться со строки “Event: Dial”. Примертекста пакета, посылаемого сервером телефонии при звонке с номера 36 на 34, приведен в распечатке 1.2.

Распечатка 1.2

Event: Dial

Privilege: call,all

SubEvent: Begin

Channel: SIP/36-0000054d

Destination: SIP/34-0000054e

CallerIDNum: 36

CallerIDName: Sergey Shimkov

UniqueID: 1322047772.2227

DestUniqueID: 1322047772.2228

Dialstring: 34

ПараметрыChannelиDestinationуказываютнаканалы, которыесоздалисьпризвонке. Они потребуются для того, чтобы была возможность перевести звонок. При переводе звонка необходимо использовать именно каналы, а не номера телефонов. Ссылки на каналы отображаются как [протокол, на котором происходит звонок]/[номер абонента]-[случайные символы]. Таким образом,определяется следующая схема действий:

  • найти пакет в потоке, определить его начало и конец;

  • попытаться найти строку в пакете: “Destination: ” + SIP/[наш номер]. Если найдена, значит это входящий звонок и тогда Destination - наш канал, Channel - канал звонящего;

  • попытаться найти строку в пакете: “Channel: ” + SIP/[наш номер]. Если найдена, значит это исходящий звонок и тогда Destination -канал звонящего, Channel - наш канал;

  • проверить, занята ли первая линия. Если свободна, тогда сохранить значения каналов как звонка по первой линии, иначе по второй;

  • вызвать внешнее событие в 1С, оповестить о входящем звонке и сообщить номер.

Далее рассмотрим ситуацию прекращения звонка. Текст пакетаот Asterisk с сообщением об окончании телефонного соединения показан в распечатке 1.3.

Распечатка 1.3

Event: Hangup

Privilege: call,all

Channel: SIP/34-0000054e

Uniqueid: 1322047772.2228

CallerIDNum: 34

CallerIDName: <unknown>

Cause: 16

Cause-txt: Normal Clearing

Пакетс сообщением об окончании телефонного соединения начинаетсясостроки “Event: Hangup”. Соответственно будем искать пакет с таким началом. Параметр Channel указывает на канал, с которого повесили трубку. Алгоритм будет следующим:

  • найти пакет, начинающийся со строки “Event: Hangup” в потоке, определить его начало и конец;

  • попытаться найти строку в пакете: “Channel: ” + [наш канал]. Проверить во всеми запомненными каналами (максимум четыре). Если найдена, значит очистить;

  • вызвать внешнее событие в 1С, оповестить о входящем звонке и сообщить номер.

Необходимо добавить, что звонок может быть инициализирован не только нашей компонентой, но и обычным телефонным аппаратом. А значит нельзя опираться на “ActionID”.Схема алгоритмапроверки начала вызова показана на рисунке 1.5.

Рисунок 1.5– Схема алгоритма подпрограммы проверки начала вызова

Запрос истории звонков так же происходит через пакеты Action, а сама история звонков возвращается Event пакетами. Период истории звонков в стандартной реализации можно установить равным только одному дню. Для получения истории звонков мало только послать Action пакет. Необходимо еще установить переменные канала “date” и “chan”. Они так же устанавливаются через Action пакеты. В первой необходимо передать число, за которое нужна история, а во второй - основу канала (“SIP/[наш номер]”). Переменные устанавливаются для канала, содержащегося в пакете с заголовком “Event: NewCallerid”. После этого необходимо дождаться пакета, начинающегося с “Event: UserEvent” и содержащего наши переменные, которые были нами отправлены ранее. В нем будет история звонков. В одном пакете может быть отражено до 10 звонков. Если количество звонков за день больше, тогда будут высланы дополнительные пакеты. Для каждого звонка указаны номера, с которого и на который совершен звонок, наименование звонящего, время звонка, длительность, отвечен или нет, а так же имя файла на сервере с записью звонка.

Общий алгоритм будет такой:

  • посылается Action пакет с запросом истории звонков;

  • ожидается Event пакет “Event: NewCallerid”. При получении сравнивается номер (CallerIDNum) с номером, переданным в Action пакете, и если они совпадают, тогда запоминается канал и для него устанавливаются переменные канала;

  • ожидается Event пакет “Event: UserEvent”. У него сравниваются канал и дата с запомненными, если совпадают, то начинается построчный разбор истории звонков;

  • каждая строка содержит один звонок, все данные разбираются и упаковываются в более понятный вид;

  • вызывается внешнее событие в 1С, передается строка с данными о звонке;

  • разбирается следующая строка или, если пакет закончился, начинается ожидание следующего пакета.

Схема алгоритма получения истории вызовов приведена на рисунке 1.6.

Рисунок 1.6– Схема алгоритма подпрограммы получения истории вызовов

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

Шифрование — способ преобразования открытой информации в закрытую и обратно. Применяется для хранения важной информации в ненадёжных источниках или передачи её по незащищённым каналам связи. Согласно ГОСТ 28147-89, шифрование подразделяется на процесс зашифровывания и расшифровывания.

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

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

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

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

Достоинства симметричного шифрования:

  • скорость (по данным Applied Cryptography — на 3 порядка выше);

  • простота реализации (за счёт более простых операций);

  • меньшая требуемая длина ключа для сопоставимой стойкости;

  • изученность (за счёт большего возраста).

Недостатки:

  • сложность управления ключами в большой сети. Означает квадратичное возрастание числа пар ключей, которые надо генерировать, передавать, хранить и уничтожать в сети. Для сети в 10 абонентов требуется 45 ключей, для 100 уже 4950, для 1000 — 499500 и т. д.;

  • сложность обмена ключами. Для применения необходимо решить проблему надёжной передачи ключей каждому абоненту, так как нужен секретный канал для передачи каждого ключа обеим сторонам.

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

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

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

В настоящее время симметричные шифры — это:

    • блочные шифры. Обрабатывают информацию блоками определённой длины (обычно 64, 128 бит), применяя к блоку ключ в установленном порядке, как правило, несколькими циклами перемешивания и подстановки, называемыми раундами. Результатом повторения раундов является лавинный эффект — нарастающая потеря соответствия битов между блоками открытых и зашифрованных данных;

    • поточные шифры, в которых шифрование проводится над каждым битом либо байтом исходного (открытого) текста с использованием гаммирования. Поточный шифр может быть легко создан на основе блочного (например, ГОСТ 28147-89 в режиме гаммирования), запущенного в специальном режиме.

Большинство симметричных шифров используют сложную комбинацию большого количества подстановок и перестановок. Многие такие шифры исполняются в несколько (иногда до 80) проходов, используя на каждом проходе «ключ прохода». Множество «ключей прохода» для всех проходов называется «расписанием ключей» (key schedule). Как правило, оно создается из ключа выполнением над ним неких операций, в том числе перестановок и подстановок.

Типичным способом построения алгоритмов симметричного шифрования является сеть Фейстеля. Алгоритм строит схему шифрования на основе функции F(D, K), где D — порция данных, размером вдвое меньше блока шифрования, а K — «ключ прохода» для данного прохода. От функции не требуется обратимость — обратная ей функция может быть неизвестна. Достоинства сети Фейстеля — почти полное совпадение дешифровки с шифрованием (единственное отличие — обратный порядок «ключей прохода» в расписании), что сильно облегчает аппаратную реализацию.

Операция перестановки перемешивает биты сообщения по некоему закону. В аппаратных реализациях она тривиально реализуется как перепутывание проводников. Именно операции перестановки дают возможность достижения «эффекта лавины». Операция перестановки линейна — f(a) xor f(b) == f(a xor b)

Операции подстановки выполняются как замена значения некоей части сообщения (часто в 4, 6 или 8 бит) на стандартное, жестко встроенное в алгоритм иное число путем обращения к константному массиву. Операция подстановки привносит в алгоритм нелинейность.

Зачастую стойкость алгоритма, особенно к дифференциальному криптоанализу, зависит от выбора значений в таблицах подстановки (S-блоках). Как минимум считается нежелательным наличие неподвижных элементов S(x) = x, а также отсутствие влияния какого-то бита входного байта на какой-то бит результата — то есть случаи, когда бит результата одинаков для всех пар входных слов, отличающихся только в данном бите.

Существует множество (не менее двух десятков) алгоритмов симметричных шифров, существенными параметрами которых являются:

    • стойкость;

    • длина ключа;

    • число раундов;

    • длина обрабатываемого блока;

    • сложность аппаратной/программной реализации;

    • сложность преобразования.

Распространенные алгоритмы:

    • AES (англ. Advanced Encryption Standard) - американский стандарт шифрования

    • ГОСТ 28147-89 — отечественный стандарт шифрования данных;

    • DES (англ. Data Encryption Standard) - стандарт шифрования данных в США до AES;

    • 3DES (Triple-DES, тройной DES);

    • RC6 (Шифр Ривеста );

    • Twofish;

    • IDEA (англ. International Data Encryption Algorithm);

    • SEED - корейский стандарт шифрования данных;

    • Camellia - сертифицированный для использовании в Японии шифр;

    • CAST (по инициалам разработчиков Carlisle Adams и Stafford Tavares);

    • XTEA - наиболее простой в реализации алгоритм.

Отбросив устаревшие и специфичные стандарты, остановимся на двух наиболее подходящих для данного проекта: AES и ГОСТ 28147-89.

Advanced Encryption Standard (AES), также известный как Rijndael — симметричный алгоритм блочного шифрования (размер блока 128 бит, ключ 128/192/256 бит), принятый в качестве стандарта шифрования правительством США по результатам конкурса AES[6]. Этот алгоритм хорошо проанализирован и сейчас широко используется, как это было с его предшественником DES. Национальный институт стандартов и технологий США (англ. National Institute of Standards and Technology, NIST) опубликовал спецификацию AES 26 ноября 2001 года после пятилетнего периода, в ходе которого были созданы и оценены 15 кандидатур. 26 мая 2002 года AES был объявлен стандартом шифрования. По состоянию на 2009 год AES является одним из самых распространённых алгоритмов симметричного шифрования. Поддержка AES (и только его) введена фирмой Intel в семейство процессоров x86 начиная с Intel Core i7-980X Extreme Edition, а затем на процессорах Sandy Bridge.

ГОСТ 28147-89 — советский и российский стандарт симметричного шифрования, введённый в 1990 году, также является стандартом СНГ. Полное название — «ГОСТ 28147-89 Системы обработки информации. Защита криптографическая. Алгоритм криптографического преобразования». Блочный шифроалгоритм. При использовании метода шифрования с гаммированием, может выполнять функции поточного шифроалгоритма.

По некоторым сведениям, история этого шифра гораздо более давняя. Алгоритм, положенный впоследствии в основу стандарта, родился, предположительно, в недрах Восьмого Главного управления КГБ СССР (ныне в структуре ФСБ), скорее всего, в одном из подведомственных ему закрытых НИИ, вероятно, ещё в 1970-х годах в рамках проектов создания программных и аппаратных реализаций шифра для различных компьютерных платформ.

С момента опубликования ГОСТа на нём стоял ограничительный гриф «Для служебного пользования», и формально шифр был объявлен «полностью открытым» только в мае 1994 года. История создания шифра и критерии разработчиков по состоянию на 2010 год не опубликованы.

ГОСТ 28147-89 — блочный шифр с 256-битным ключом и 32 циклами преобразования, оперирующий 64-битными блоками. Основа алгоритма шифра — Сеть Фейстеля. Базовым режимом шифрования по ГОСТ 28147-89 является режим простой замены (определены также более сложные режимы гаммирование, гаммирование с обратной связью и режим имитовставки).

В ГОСТе используется 256-битовый ключ и объем ключевого пространства составляет 2256. Ни на одном из существующих в настоящее время или предполагаемых к реализации в недалеком будущем электронном устройстве нельзя подобрать ключ за время, меньшее многих сотен лет. Эта величина стала фактическим стандартом размера ключа для симметричных криптоалгоритмов в наши дни, – так, новый стандарт шифрования США также его поддерживает. Прежний же американский стандарт, DES с его реальным размером ключа в 56 бит и объемом ключевого пространства всего 256 уже не является достаточно стойким в свете возможностей современных вычислительных средств. Это было продемонстрировано в конце 90-х годов несколькими успешными попытками взлома DES переборным путем. Кроме того, DES оказался подвержен специальным способам криптоанализа, таким как дифференциальный и линейный. В этой связи DES может представлять скорее исследовательский или научный, чем практический интерес. В 1998 году его криптографическая слабость была признана официально, – национальный институт стандартов США рекомендовал использовать троекратное шифрование по DES. А в конце 2001 года был официально утвержден новый стандарт шифрования США, AES, построенный на иных принципах и свободный от недостатков своего предшественника.

Общеизвестно, что отечественный стандарт шифрования является представителем целого семейства шифров, построенных на одних и тех же принципах. Самым известным его «родственником» является прежний американский стандарт шифрования, алгоритм DES. Все эти шифры, подобно ГОСТу, содержат алгоритмы трех уровней. В основе всегда лежит некий «основной шаг», на базе которого сходным образом строятся «базовые циклы», и уже на их основе построены практические процедуры шифрования и выработки имитовставки. Таким образом, специфика каждого из шифров этого семейства заключена именно в его основном шаге, точнее даже в его части. Хотя архитектура классических блочных шифров, к которым относится ГОСТ, лежит далеко за пределами темы настоящей статьи, все же стоит сказать несколько слов по ее поводу.

Алгоритмы «основных шагов криптопреобразования» для шифров, подобных ГОСТу, построены идентичным образом, и эта архитектура называется сбалансированная сеть Файстеля (balanced Feistel network) по имени человека, впервые предложившего ее. Схема преобразования данных на одном цикле(раунде) приведена на рисунке 1.7.

Рисунок 1.7 – Содержание основного шага криптопреобразования для блочных шифров, подобных ГОСТу.

На вход основного шага подается блок четного размера, старшая и младшая половины которого обрабатываются отдельно друг от друга. В ходе преобразования младшая половина блока помещается на место старшей, а старшая, скомбинированная с помощью операции побитового «исключающего или» с результатом вычисления некоторой функции, на место младшей. Эта функция, принимающая в качестве аргумента младшую половину блока и элемент ключевой информации (X), является содержательной частью шифра и называется его функцией шифрования. По разным соображениям оказалось выгодно разделить шифруемый блок на две одинаковые по размеру части: |N 1|=|N 2| – именно этот факт отражает слово «сбалансированная» в названии архитектуры. Впрочем, шифрующие несбалансированные сети также используются время от времени, хотя и не так часто, как сбалансированные. Кроме того, соображения стойкости шифра требуют, чтобы размер ключевого элемента не был меньше размера половины блока: в ГОСТе все три размера равны 32 битам.

Если применить сказанное к схеме основного шага алгоритма ГОСТ, станет очевидным, что блоки 1,2,3 алгоритма определяют вычисление его функции шифрования, а блоки 4 и 5 задают формирование выходного блока основного шага исходя из содержимого входного блока и значения функции шифрования.

Сравним DES и ГОСТ по функциональному содержанию и удобству реализации. В циклах шифрования ГОСТа основной шаг повторяется 32 раза, для DES эта величина равна 16. Однако сама функция шифрования ГОСТа существенно проще аналогичной функции DES, в которой присутствует множество нерегулярных битовых перестановок. Эти операции чрезвычайно неэффективно реализуются на современных неспециализированных процессорах. ГОСТ не содержит подобных операций, поэтому он значительно удобней для программной реализации.

Ни одна из реализаций DESа для платформы Intel x86 не достигает даже половины производительности предложенной в настоящей реализации ГОСТа несмотря на вдвое более короткий цикл. Все сказанное выше свидетельствует о том, что разработчики ГОСТа учли как положительные, так и отрицательные стороны DESа, а также более реально оценили текущие и перспективные возможности криптоанализа. Впрочем, брать DES за основу при сравнении быстродействия реализаций шифров уже не актуально. У нового стандарта шифрования США дела с эффективностью обстоят гораздо лучше – при таком же как у ГОСТа размере ключа в 256 бит AES работает быстрее него примерно на 14% – это если сравнивать по числу «элементарных операций». Кроме того, ГОСТ практически не удается распараллелить, а у AES возможностей в этом плане намного больше. На некоторых архитектурах это преимущество AES может быть меньше, на других – больше. Так, на процессоре Intel Pentium оно достигает 28%. Процессоры Intel Core за счет аппаратной поддержки стандарта демонстрируют значительное преимущество в скорости шифрования AES.

Кроме того, в мае 2011 года известный криптоаналитик Николя Куртуа заявил об обнаружении серьезных уязвимостей в ГОСТ. Практический результат пока скромен: 2^64 известных открытых текста и 2^64 памяти для хранения пар “открытый текст/шифртекст” позволяют взломать ГОСТ в 2^8 быстрее, чем простой перебор. Но в плане криптоанализа это делает полностью справедливым утверждение о том, что “ГОСТ взломан”. Так же у ГОСТ есть и другие недостатки. Основные проблемы ГОСТа связаны с неполнотой стандарта в части генерации ключей и таблиц замен. Тривиально доказывается, что у ГОСТа существуют «слабые» ключи и таблицы замен, но в стандарте не описываются критерии выбора и отсева «слабых». Также стандарт не специфицирует алгоритм генерации таблицы замен (S-блоков). С одной стороны, это может являться дополнительной секретной информацией (помимо ключа), а с другой, поднимает ряд проблем:

    • нельзя определить криптостойкость алгоритма, не зная заранее таблицы замен;

    • реализации алгоритма от различных производителей могут использовать разные таблицы замен и могут быть несовместимы между собой;

    • возможность преднамеренного предоставления слабых таблиц замен лицензирующими органами РФ;

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

Исходя из этого, выберем AES для шифрования данных в нашей компоненте.

Схема основного алгоритма программы приведена на рисунке 1.8. Схема алгоритма обработки дополнительных событий от сервера показана на рисунке 1.9.

Рисунок1.8– Схема алгоритма программы

Рисунок1.9– Схема алгоритма проверки дополнительных событий от сервера телефонии

1.5Разработка алгоритма AES

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

Исходный текст:

00 11 22 33 44 55 66 77 88 99 aa bb cc dd ee ff

192-битный ключ:

00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 11 12 13 14 15 16 17

Зашифрованныйтекст:

dd a9 7c a4 86 4e df e0 6e af 70 a0 ec 0d 71 91

Результат расшифровки:

00 11 22 33 44 55 66 77 88 99 aa bb cc dd ee ff

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

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

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

00 11 22 33 44 55 66 77 88 99 аа bb cc dd ее ff

0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15

и используется 192-битный исходный ключ:

00 01 02 03 04 05 06 07 08 09 0a 0b 0с 0d 0e 0f 10 11 12 13 14 15 16 17

0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23

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

Таблица1.2 – Первые пять строк и столбцов матрицы замен Sbox

x\y

0

1

2

3

4

0

63

7c

77

7b

f2

1

ca

82

c9

7d

fa

2

b7

fd

93

26

36

3

04

c7

23

c3

18

4

09

83

2c

1a

1b

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

Таблица1.3 – Таблица ключей

0

00

01

02

03

1

04

05

06

07

2

08

09

0a

0b

3

0c

0d

0e

0f

4

10

11

12

13

5

14

15

16

17

6

58

46

f2

f9

7

5c

43

f4

fe

Продолжение таблицы 1.3

8

54

48

fe

f5

9

58

47

f9

fa

10

48

56

e2

e9

...

...

...

...

...

49

fa

76

dc

09

50

c4

18

c2

7d

51

e3

a4

1d

5d

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

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

Таблица1.4 – Матрица State

0

1

2

3

0

00

44

88

сс

1

11

55

99

dd

2

22

66

aa

ee

3

33

77

bb

ff

Процедура шифрования называется Cipher. Она работает с матрицей State[ ] и описывается псевдокодом, приведенным в распечатке 1.4.Псевдокод – язык описания алгоритмов, использующий ключевые слова языков программирования, но опускающий подробности и специфический синтаксис.

Распечатка 1.4

Cipher(byte[] input, byte[] output)

{

byte[4,4] State;

Копирование input[] s State[]

AddRoundKey

for (round = 1; round < Nr-1; ++round)

{

SubBytes

ShiftRows

MixColumns

AddRoundKey

}

SubBytes

Shift Rows

AddRoundKey

Копирование Stated в output[]

}

Алгоритм шифрования выполняет операцию предварительной обработки, которая в спецификации называется 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, cc}, а первый столбец таблицы ключей имеет вид {00,04, 08, 0с}, то новым значением State[0,2] будет 0x80 — результат операции XOR над State[0,2] (0x88) и w[2,0] (0x08):

1 0 0 0 1 0 0 0

0 0 0 0 1 0 0 0 XOR

1 0 0 0 0 0 0 0

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

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

Таблица1.5 – Обработка State операцией ShiftRows

0

1

2

3

0

00

44

88

сс

1

11

55

99

dd

2

22

66

aa

ee

3

33

77

bb

ff

MixColumns — это операция замены, самая сложная для понимания часть алгоритма AES. Она заменяет каждый байт результатом математических операций сложения и умножения в поле (mathematical field additions and multiplications), применяемых к элементам столбца, который содержит этот байт. Я расскажу о сложении и умножении элементов полей в следующем разделе.

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

+

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

Четыре подпрограммы 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 заменяет байты результатами операций сложения и умножения элементов поля.

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

Поле GF(28) состоит из 256 значений от 0x00 до 0xff, над которыми определены операции сложения и умножения. Отсюда (28) в названии. Поле GF (Galois Field) названо в честь Галуа, математика, основавшего теорию полей. Одной из характеристик GF(28) является то, что результат сложения или умножения всегда принадлежит множеству {0x00 … 0xff}. Теория полей достаточно сложна, но применительно к сложению в GF(28) получается простой конечный результат: сложение в GF(28) — это обыкновенная операция XOR.

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

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

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

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

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

Эту операцию можно выполнить, зная, как умножать на 0x02 и 0x01 и как складывать. Аналогично умножение произвольного байта на 0x0d сводится к следующим операциям:

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

Подведем итог. Сложение в GF(28) — это операция XOR. Умножение в GF(28) сводится к операциям сложения и умножения на 0x02, а умножение на 0x02 — это сдвиг на 1 бит влево, выполняемый в зависимости от условия.

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

Распечатка 1.5

KeyExpansion(byte[] key, byte[][4] w)

{

Копирование исходного ключа в первые строки w

Для каждой оставшейся строки w

{

Создание новой строки по двум предыдущим

}

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

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

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

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

Распечатка 1.6

private void BuildRcon()

{

this.Rcon = new byte[11,4] { {0x00, 0x00, 0x00, 0x00},

{0x01, 0x00, 0x00, 0x00},

{0x02, 0x00, 0x00, 0x00},

{0x04, 0x00, 0x00, 0x00},

{0x08, 0x00, 0x00, 0x00},

{0x10, 0x00, 0x00, 0x00},

{0x20, 0x00, 0x00, 0x00},

{0x40, 0x00, 0x00, 0x00},

{0x80, 0x00, 0x00, 0x00},

{0x1b, 0x00, 0x00, 0x00},

{0x36, 0x00, 0x00, 0x00} };

} // BuildRcon()

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

Теперь более пристально рассмотрим цикл подпрограммы KeyExpansion. Если детализировать показанный выше псевдокод, этот цикл приобретет следующий вид, показанный в распечатке 1.7.

Распечатка 1.7

for (row = Nk; row < (4 * Nr+1); ++row)

{

temp = w[row-1]

if (row % Nk == 0)

temp = SubWord(RotWord(temp)) xor Rcon[row/Nk]

else if (Nk == В and row % Nk == 4)

temp = SubWord(temp)

w[row] = w[row-Nk] xor temp

}

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

Посмотрим, как KeyExpansion приступает к обработке ключа, показанного в исходном примере. Исходный ключ — 192-битное значение (6 слов):

00 01 02 03 04 05 06 07 08 09 0а 0b 0с 0d 0e 0f 10 11 12 13 14 15 16 17

В таблице ключей 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) истинно, поэтому к строке применяется подпрограмма RotWord:

temp = 15 16 17 14

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

temp = 59 47 f0 fa

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

temp = 58 47 f0 fa

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

w[6] = 58 46 f2 f9

Этот процесс повторяется для остальных строк таблицы ключей w[ ].

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

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

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

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

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

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

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

1.6Разработка текста программы

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

Для начала необходимо, чтобы компонента реализовывала COM-соединение с 1С.

1С:Предприятие подключает внешнюю компоненту двумя способами: по указанному ProgID через CreateInstance, либо по имени файла компоненты. В последнем случае 1С пытается выполнить DllRegisterServer для саморегистрации файла через rgs-скрипт, затем ищется строка с ID=100 из ресурсов компоненты, содержащая ProgID объекта. При написании CLR-кода последняя возможность отпадает, поскольку у NET-компонент совсем другой принцип регистрации и другой метод хранения ресурсов. Поэтому из 1С:Предприятия необходимо использовать только метод ПодключитьВнешнююКомпоненту(ProgID), где ProgID должен иметь вид AddIn.xxx, для чего используется атрибут ProgIdAttribute.

Ключевым для работы внешней компоненты является интерфейс IInitDone (Распечатка 1.8).

Распечатка 1.8

[Guid("AB634001-F13D-11d0-A459-004095E1DAEA")]

[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]

public interface IInitDone

{

// Инициализациякомпонента

void Init(

[MarshalAs(UnmanagedType.IDispatch)]

object connection);

// Вызывается перед уничтожением компонента

void Done();

// Возвращается инициализационная информация

voidGetInfo(

[MarshalAs(UnmanagedType.SafeArray, SafeArraySubType=VarEnum.VT_VARIANT)]

ref object[] info);

}

Кроме того, библиотека типов, поставляемая фирмой 1С, содержит ряд других интерфейсов для расширения встроенного языка и других целей, рассматривать которые мы не будем. Конечно можно воспользоваться готовым tlb-файлом и средствами NET создать COM callable wrapper (проще говоря добавить ссылку на COM-библиотеку из проекта), но тогда следует согласится с тем, что множество параметров типа VARIANT будут преобразованы в малоудобные System.Array или Object, в то время как из документации известна их более строгая типизация. Наш интерфейс в NET будет выглядеть, как показано в распечатке 1.9.

Распечатка 1.9

private IAsyncEvent asyncEvent = null;

private IStatusLine statusLine = null;

public void Init(

[MarshalAs(UnmanagedType.IDispatch)]

object connection)

{

asyncEvent = (IAsyncEvent)connection;

statusLine = (IStatusLine)connection;

}

public void GetInfo(

[MarshalAs(UnmanagedType.SafeArray, SafeArraySubType=VarEnum.VT_VARIANT)]

ref object[] info)

{

info[0] = 2000;

}

Интерфейс IAsyncEvent реализован 1С для получения событий от внешнего компонента. Ссылка на него получена путем приведения параметра функции Init интерфейса IInitDone к типу IAsyncEvent при инициализации компонента. Внешнее событие обрабатывается 1С в функции ОбработкаВнешнегоСобытия.

В распечатке 1.10 показан текст реализованного в компонентеинтерфейсаIAsyncEvent.

Распечатка 1.10

[Guid("AB634004-F13D-11D0-A459-004095E1DAEA"),InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]

public interface IAsyncEvent

{

void SetEventBufferDepth(Int32 depth);

void GetEventBufferDepth(ref Int32 depth);

void ExternalEvent([MarshalAs(UnmanagedType.BStr)] String source,

[MarshalAs(UnmanagedType.BStr)] String message,

[MarshalAs(UnmanagedType.BStr)] String data);

void CleanBuffer();

}

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

Для начала создадим подключение, как показано в распечатке 1.11.

Распечатка 1.11

TcpClient tcpSocket;

string Hostname = "sip.miko.ru";

int Port = 5038;

tcpSocket = new TcpClient(Hostname, Port);

Таким образом мы уже подключились к серверу телефонии. При соединении необходимо указать имя сервера и порт. Далее необходимо авторизоваться на сервере, для этого пошлем по только что установленному соединению Actionпакет, текст которого представлен в распечатке 1.12.

Распечатка 1.12

stringres = "Action: Login" + "\r\n"

+ "ActionID: " + ActionID + "\r\n"

+ "Username: " + Username + "\r\n"

+ "Secret: " + Secret + "\r\n";

SendString(tcpSocket, res);

ЗдесьSendString(tcpSocket, res) – функция, котораяпосылаетстрокувпоток. Еекодпредставленвраспечатке 1.13.

Распечатка 1.13

publicstaticvoidSendString(TcpClienttcpSocket, stringStroka) //Послатьстрокувпоток

{

Stroka = Stroka + "\r\n";

byte[] buf = Encrypt(System.Text.ASCIIEncoding.ASCII.GetBytes(Stroka));

tcpSocket.GetStream().Write(buf, 0, buf.Length);

}

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

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

Потоки позволяют программе выполнять параллельную обработку, за счет чего появляется возможность одновременного выполнения нескольких операций [5]. Например, потоки можно использовать для наблюдения ввода данных пользователем, выполнения фоновых задач и обработки одновременных потоков ввода.

Потоки имеют следующие свойства.

  • потоки позволяют программе выполнять параллельную обработку;

  • пространство имен .NET Framework System.Threading упрощает использование потоков;

  • потоки используют одни и те же ресурсы приложения.

По умолчанию программа на языке C# имеет один поток. Однако параллельно основному потоку могут создаваться и использоваться вспомогательные потоки. Эти потоки называются рабочими потоками.

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

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

В данном случае у нас получается почти полное разграничение обязанностей потоков – основной поток будет служить для соединения с 1С и изредка для передачи команд серверу. Дополнительный поток в свою очередь будет поддерживать соединение с сервером телефонии и слушать поступающие от него события. Здесь слово “изредка” означает много меньшее время использования сети основным потоком. Поэтому и взаимные блокировки потоков будут происходить достаточно редко, чтоб не мешать работе компоненты. На этом закончим с потоками и перейдем к реализации алгоритма AES.

Выше мы рассмотрели все компоненты алгоритма шифрования AES, и теперь реализуем этот алгоритм на С#. ОфициальнаяспецификацияалгоритмаAESсодержитсявдокументеFederalInformationProcessingStandardsPublication 197[6]. Было решено, что реализация должна соответствовать ей максимально точно, но вскоре было обнаружено, что спецификация скорее теоретический документ, чем руководство по реализации. Для удобства было решено использовать те же имена переменных, что и в опубликованном стандарте (даже такие загадочные, как «Nr> и «w»).

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

Распечатка 1.14

public enum KeySize { Bits128, Bits192, Bits256 };

private int Nb;

private int Nk;

private int Nr;

private byte[] key;

private byte[,] Sbox;

private byte[,] iSbox;

private byte[,] w;

private byte[,] Rcon;

private byte[,] State;

Поскольку ключи бывают только 128-, 192- и 256-битные, для описания размера ключа очень удобно создать перечислимый тип:

public enum KeySize { Bits128, Bits192, Bits256 };

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

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

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

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

Распечатка 1.15

а.Cipher(plainText, cipherText);

a.InvCipher(cipherText, decipheredText);

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

Распечатка 1.16

publicAes(KeySizekeySize, byte[] keyBytes)

{

SetNbNkNr(keySize);

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

keyBytes.CopyTo(this.key, 0);

BuildSbox();

BuildlnvSbox();

BuildRcon();

KeyExpansion();

}

Сначала конструктор присваивает значения полям Nb, Nk и Nr, вызывая вспомогательный метод SetNbNkNr, приведенный в распечатке 1.17.

Распечатка 1.17

private void SetNbNkNr(KeySize keySize)

{

this.Nb = 4; // block size always = 4 words = 16 bytes = 128 bits for AES

if (keySize == KeySize.Bits128)

{

this.Nk = 4; // key size = 4 words = 16 bytes = 128 bits

this.Nr = 10; // rounds for algorithm = 10

}

else if (keySize == KeySize.Bits192)

{

this.Nk = 6; // 6 words = 24 bytes = 192 bits

this.Nr = 12;

}

else if (keySize == KeySize.Bits256)

{

this.Nk = 8; // 8 words = 32 bytes = 256 bits

this.Nr = 14;

}

} // SetNbNkNr()

Затем байты, передаваемые конструктору, копируются в поле класса. Ключ объявлен как поле класса и инициализируется операторами (распечатка 1.18).

Распечатка 1.18

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

keyBytes.CopyTo(this.key, 0);

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

Распечатка 1.19

privatevoidBuildSbox()

{

this.Sbox = newbyte[16,16] { // populatetheSboxmatrix

/* 0 1 2 3 4 5 6 7 8 9 abcdef */

/*0*/ {0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76},

/*1*/ {0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0},

/*2*/ {0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15},

/*3*/ {0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75},

/*4*/ {0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84},

/*5*/ {0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf},

/*6*/ {0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8},

/*7*/ {0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2},

/*8*/ {0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73},

/*9*/ {0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb},

/*a*/ {0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79},

/*b*/ {0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08},

/*c*/ {0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a},

/*d*/ {0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e},

/*e*/ {0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf},

/*f*/ {0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16} };

} // BuildSbox()

Лучше всего объявить таблицу ключей w[ ], таблицу итеративных констант Rcon[ ] и таблицу State[ ] членами класса и присваивать значения таблицам Rcon[ ] и w[ ] закрытыми вспомогательными методами.

Вспомним, что левый байт каждой строки Rcon[ ] — это степень 2 в поле GF(28), следовательно, эту таблицу можно заполнить, выполнив вычисления вида:

newVal = prevVal * 0x02;

В конце конструктора AES-класса вызывается метод KeyExpansion, формирующий таблицу ключей w[ ]. Код метода довольно прост. В спецификации используется гипотетический тип данных — 4-байтное слово. В С# такого типа нет, поэтому он был смоделирован как массив из 4 байтов. Сначала таблице w[ ]выделяется память оператором new. Затем первые Nk (4, 6 или 8) строк w[ ] заполняются значениями массива key[ ], передаваемого конструктору (распечатка 1.20).

Распечатка 1.20

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];

private void KeyExpansion()

{

this.w = new byte[Nb * (Nr+1), 4]; // 4 columns of bytes corresponds to a word

for (int row = 0; row < Nk; ++row)

{

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];

}

byte[] temp = new byte[4];

for (int row = Nk; row < Nb * (Nr+1); ++row)

{

temp[0] = this.w[row-1,0]; temp[1] = this.w[row-1,1];

temp[2] = this.w[row-1,2]; temp[3] = this.w[row-1,3];

if (row % Nk == 0)

{

temp = SubWord(RotWord(temp));

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

temp[1] = (byte)( (int)temp[1] ^ (int)this.Rcon[row/Nk,1] );

temp[2] = (byte)( (int)temp[2] ^ (int)this.Rcon[row/Nk,2] );

temp[3] = (byte)( (int)temp[3] ^ (int)this.Rcon[row/Nk,3] );

}

else if ( Nk > 6 && (row % Nk == 4) )

{

temp = SubWord(temp);

}

// w[row] = w[row-Nk] xor temp

this.w[row,0] = (byte) ( (int)this.w[row-Nk,0] ^ (int)temp[0] );

this.w[row,1] = (byte) ( (int)this.w[row-Nk,1] ^ (int)temp[1] );

this.w[row,2] = (byte) ( (int)this.w[row-Nk,2] ^ (int)temp[2] );

this.w[row,3] = (byte) ( (int)this.w[row-Nk,3] ^ (int)temp[3] );

} // for loop

} // KeyExpansion()

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

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

вместо:

temp[0] = temp[0] ^ this.Rcon[row/Nk,0];

При выполнении условия метод KeyExpansion вызывает закрытые методы SubWord и RotWord. Выбор имен методов объясняется тем, что такие имена используются в спецификации. И в этом случае, поскольку в С# нет типа word, он моделируется массивом из четырех байтов. Код методов SubWord и RotWord весьма прост.

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

Распечатка 1.21

int х = word[0] >> 4;

int у = word[0] & 0x0f;

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

result[0] = substitute;

Но был использован код, представленный в распечатке 1.22.

Распечатка 1.22

result[0] = this.Sbox[word[0] >> 4, word[0] & 0x0f ];

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

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

Распечатка1.23

public void Cipher(byte[] input, byte[] output}

{

// state = input

this.State = new byte[4, Nb];

for (int i = 0; 1 < (4 * Nb); ++1)

{

this.State[i % 4, i/43] = input[i];

}

AddRoundKey(0);

for (int round = 1; round <= (Nr - 1); ++round)

{

SubBytes();

ShiftRows();

MixColumns() ;

AddRoundKey(round) ;

}

SubBytes();

ShiftRows();

AddRoundKey(Nr);

// output = state

for (int i=0; i < (4 * Nb); ++i)

{

output[i] = this.State[i % 4, i / 4];

}

} // Cipher()

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

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

Распечатка1.24

privatevoidAddRoundKey(intround)

{

for (int r = 0; r < 4; ++r)

{

for (int с = 0; с< 4; ++с)

{

this.State[r,c] * (byte) С (int)this.State[r,c] * (int)w[(round*4)+c,r]>;

}

}

} // AddRoundKey()

private void SubBytes()

{

for (int r = 0; r < 4; ++r)

{

for (int с = 0; с< 4; ++c)

{

this. State[r,c] = this,Sbox[ (tnis.State[r,c] >> 4), (this.State[r,c] & 0x0f) ];

}

}

}// SubBytes

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

Распечатка 1.25

private void ShiftRows()

{

byte[,] temp = new byte[4,4];

for (int f = 0; f < 4; ++f)

{

for (int с = 0; с< 4; ++c)

{

temp[r,c] = this.State[r,c];

}

}

for (int r = 1; r < 4; ++r)

{

for (int с = 0; с< 4; ++c)

{

this.State[r,c] = temp[ r, (c + r) % Nb ];

}

}

}// ShiftRows()

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

this.State[r, (с + r) % Nb ] = temp[r,c];

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

Распечатка 1.26

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 * State[1,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 * State[1,c] + 0x01 * State[2,c] + 0x02 * State[3,c]

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

Распечатка 1.27

return (byte) ( (int)gfmultby02(b) ” (int)b );

private void MixColusms()

{

byte[,] temp * new byte[4,4];

for (int r = 0; r < 4; ++r)

{

for (int с = 0; с< 4; ++с)

{

temp[r,c] = this.State[r, c];

}

}

for (int c = 0 ; с< 4; ++c)

{

this.State[0,c] = (byte) ( (int)gfmultby02(temp[0,c]) * (int)gfmultby03(temp[1,c]) * (int)gfmultby01(temp[2,c]) * (int)gfmultby01(temp[3,c]) );

this.State[1,c] = (byte) ( (int)gfmultby01(temp[0,c]) * (int)gfmultby02(temp[1,c]) * (int)gfmultby03(temp[2,c]) * (int)gfmultby01(temp[3,c]) );

this.State[2,c] = (byte) ( (int)gfmultby01(temp[0,c]) * (int)gfmultby01(temp[1,c]) * (int)gfmultby02(temp[2,c]) * (int)gfmultby03(temp[3,c]) );

tnis.State[3,c] = (byte) ( (int)gfmultby03(temp[0,c]) * (int)gfmultby01(temp[1,c]) * (int)gfmultby01(temp[2,c]) * (int)gfmultby02(temp[3,c]) );

}

}// MixColumns

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

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