I'm not dead... yet.
На работе поставили задачу организовать отправку XML-файлов на федеральный портал N.
Файлы должны быть подписаны по стандарту XMLDSIG, но ГОСТовскими алгоритмами. Поэтому большинство готовых решений из гугла отпадают.
Такие файлы можно создать средствами КриптоПро, но лицензия на десятки тысяч клиентов обойдется недешево.
Я стал пытаться написать свою реализацию, но на все файлы приходил ответ: "ЭЦП не валидна".
Тут нужно сделать лирическое отступление. ЭЦП по сути представляет собой что-то вроде хеш-функции. На вход мы подаем данные (любой набор байтов) и указываем закрытый ключ, с помощью которого хотим создать подпись. На выходе получаем строку из 64 байт. Эта строка и называется ЭЦП. Для одних и тех же данных ЭЦП каждый раз генерируется разная, но свойства у нее одни и те же:
Имея ЭЦП, исходные данные и открытый ключ отправителя, можно точно установить, является ли этот отправитель создателем ЭЦП, и соответствует ли эта ЭЦП исходным данным. Если в процессе передачи данных кто-то внесет изменения хотя бы в один бит, ЭЦП уже перестанет соответствовать этим данным. То есть можно будет установить фальсификацию данных.
Если кто-то изменит данные и захочет сгенерировать новую ЭЦП - это будет невозможно сделать без закрытого ключа, который хранится только у отправителя данных. Можно использовать другой закрытый ключ, но тогда ЭЦП будет невоможно сопоставить с открытым ключом исходного отправителя, т.е. мы опять же узнаем, что данные были сфальсифицированы.
А также, ЭЦП это просто набор байт, по которому никак нельзя определить исходные данные, которые были ей подписаны. Что-то типа контрольной суммы.
Стандарт XMLDSIG предполагает, что мы подписываем не весь XML файл, а только интересующие нас элементы. Существуют особые правила преобразования XML-дерева в байты. При этом вырезаются комментарии, символы переноса строки и т.д. Этот процесс называется каноникализацией.
Отправитель файла берет элемент, который нужно подписать, проводит каноникализацию и подписывает его, после чего добавляет подпись в тот же файл. Получатель файла точно так же берет элемент, проводит каноникализацию и сверяет указанную ЭЦП с полученными данными.
Понятное дело, что если на стороне отправителя и получателя алгоритмы каноникализации реализованы с расхождениями, то они будут получать разные данные и ЭЦП никогда не сойдется.
Я предположил, что именно такой вариант и имеет место (и как оказалось, был прав).
После недели экспериментов я уже был в отчаянии и хотел сдаться, но тут мне кинули ссылку на программу, которая делает то же самое, что нужно сделать мне. Она не использовала сторонние компоненты и, судя по всему, в ней была собственная реализация алгоритмов, написанная на C++.
Я скачал OllyDbg (дизассемблер-отладчик), и хотя никогда раньше этим не занимался, за 3 дня сумел найти в экзешнике место, в котором вызывается функция Windows Crypto API по созданию ЭЦП, и вытащил из стека параметры, которые в нее передаются. Потом я сравнил эти параметры со своей реализацией, нашел расхождение и дело было в шляпе.
Теперь горжусь собой и чувствую себя хакером.
Файлы должны быть подписаны по стандарту XMLDSIG, но ГОСТовскими алгоритмами. Поэтому большинство готовых решений из гугла отпадают.
Такие файлы можно создать средствами КриптоПро, но лицензия на десятки тысяч клиентов обойдется недешево.
Я стал пытаться написать свою реализацию, но на все файлы приходил ответ: "ЭЦП не валидна".
Тут нужно сделать лирическое отступление. ЭЦП по сути представляет собой что-то вроде хеш-функции. На вход мы подаем данные (любой набор байтов) и указываем закрытый ключ, с помощью которого хотим создать подпись. На выходе получаем строку из 64 байт. Эта строка и называется ЭЦП. Для одних и тех же данных ЭЦП каждый раз генерируется разная, но свойства у нее одни и те же:
Имея ЭЦП, исходные данные и открытый ключ отправителя, можно точно установить, является ли этот отправитель создателем ЭЦП, и соответствует ли эта ЭЦП исходным данным. Если в процессе передачи данных кто-то внесет изменения хотя бы в один бит, ЭЦП уже перестанет соответствовать этим данным. То есть можно будет установить фальсификацию данных.
Если кто-то изменит данные и захочет сгенерировать новую ЭЦП - это будет невозможно сделать без закрытого ключа, который хранится только у отправителя данных. Можно использовать другой закрытый ключ, но тогда ЭЦП будет невоможно сопоставить с открытым ключом исходного отправителя, т.е. мы опять же узнаем, что данные были сфальсифицированы.
А также, ЭЦП это просто набор байт, по которому никак нельзя определить исходные данные, которые были ей подписаны. Что-то типа контрольной суммы.
Стандарт XMLDSIG предполагает, что мы подписываем не весь XML файл, а только интересующие нас элементы. Существуют особые правила преобразования XML-дерева в байты. При этом вырезаются комментарии, символы переноса строки и т.д. Этот процесс называется каноникализацией.
Отправитель файла берет элемент, который нужно подписать, проводит каноникализацию и подписывает его, после чего добавляет подпись в тот же файл. Получатель файла точно так же берет элемент, проводит каноникализацию и сверяет указанную ЭЦП с полученными данными.
Понятное дело, что если на стороне отправителя и получателя алгоритмы каноникализации реализованы с расхождениями, то они будут получать разные данные и ЭЦП никогда не сойдется.
Я предположил, что именно такой вариант и имеет место (и как оказалось, был прав).
После недели экспериментов я уже был в отчаянии и хотел сдаться, но тут мне кинули ссылку на программу, которая делает то же самое, что нужно сделать мне. Она не использовала сторонние компоненты и, судя по всему, в ней была собственная реализация алгоритмов, написанная на C++.
Я скачал OllyDbg (дизассемблер-отладчик), и хотя никогда раньше этим не занимался, за 3 дня сумел найти в экзешнике место, в котором вызывается функция Windows Crypto API по созданию ЭЦП, и вытащил из стека параметры, которые в нее передаются. Потом я сравнил эти параметры со своей реализацией, нашел расхождение и дело было в шляпе.
Теперь горжусь собой и чувствую себя хакером.