Начиная с версии 2008.2, в Caché и в Ensemble встроена поддержка
WS-Security, включающая механизмы проверки и формирования электронной подписи SOAP-сообщений. На текущий момент имеется «out of the box» поддержка ЭЦП на базе крипто алгоритмов семейства
RSA.
К системам, создаваемым для российских заказчиков, зачастую предъявляется требование применения сертифицированных СКЗИ, использующих крипто алгоритмы ГОСТ.
Под хабракатом рассказывается, как делать веб-сервисы Caché, защищенные ЭЦП на базе ГОСТ’овской криптографии.
В качестве сертифицированного СКЗИ, будем использовать продукт
КриптоПро JCP, представляющий собой набор Java библиотек, в которых реализован алгоритм подписи —
ГОСТ Р 34.10-2001, а также алгоритм хэширования
ГОСТ Р 34.11-94.
Установка КриптоПро JCP на Windows
Прежде всего, на сервер системы установите
Java Runtime Environment (JRE) версии не ниже 1.6.
Загрузите дистрибутив КриптоПро JCP с сайта производителя, распакуйте его в папку на сервере и запустите скрипт установки install.bat. Скрипт находится в папке lib дистрибутива. При его запуске необходимо указать путь к JRE:
install.bat «C:\Program Files\Java\jdk1.6.0_20\jre»
В случае если имеется лицензия, при запуске скрипта также указывается серийный номер и название компании:
install.bat «C:\Program Files\Java\jdk1.6.0_20\jre» XXXXX-XXXXX-XXXXX-XXXXX-XXXXX «Your Company»
Под Windows 7 скрипт установки необходимо запускать с правами администратора. После завершения работы скрипта, убедитесь в том, что в папке jre\lib\ext появились следующие библиотеки:
| asn1rt.jar |
JCP.jar |
JCPRevCheck.jar |
JCP_ASN.jar |
| AsnLite.jar |
JCPinst.jar |
JCPRevTools.jar |
JCryptoP.jar |
| forms_rt.jar |
JCPRequest.jar |
JCPxml.jar |
|
Дополнительные библиотеки Java
Нам понадобится библиотека iscjcp.jar (исходники
здесь), в которой содержится ряд вспомогательных классов для работы с JCP из Caché. Кроме этого, потребуются три open source библиотеки —
Commons Logging,
Santuario (aka XML Security) и
WSS4J. Их использование регулируется лицензией
Apache Software License 2.0.
Загрузите архив
jars.zip с четырьмя перечисленными библиотеками и распакуйте его в папку jre\lib\ext.
В случае использования Windows 7, необходимо выдать полномочия группе «Все» на чтение и выполнение всех библиотек в папке jre\lib\ext.
Настройка и запуск Java Gateway, создание проекций классов
Чтобы стало возможным вызывать Java-классы из Caché/Ensemble, необходимо настроить и запустить Java Gateway, а также создать проекции используемых Java-классов.
Добавим новую запись в таблицу настроек Java Gateway в области %SYS:
insert into %Net_Remote.ObjectGateway(Name, Type, Server, Port, JavaHome) values ('JCPGate', '1', '127.0.0.1', '55555', 'C:\Program Files\Java\jdk1.6.0_20\jre')
Здесь в поле Name указано значение “JCPGate” – это имя нового Java Gateway. В поле JavaHome необходимо указать путь к JRE, для которой была произведена установка JCP. В поле Port указывается TCP-порт, используемый для общения с этим Java Gateway из Caché.
Теперь можно запустить новый Java Gateway, выполнив в терминале Caché следующую команду:
write ##class(%Net.Remote.Service).StartGateway(«JCPGate»)
Чтобы остановить его, вызовите метод StopGateway:
write ##class(%Net.Remote.Service).StopGateway(«JCPGate»)
Запускать/останавливать Java Gateway можно из любой области.
Перейдем в область, где ведется разработка веб-сервисов, и создадим проекцию для Java-класса
isc.jcp.JcpFacade, выполнив в терминале Caché следующую команду:
do ##class(%Net.Remote.Java.JavaGateway).%ExpressImport(«isc.jcp.JcpFacade», «55555»)
Здесь 55555 – это номер TCP-порта, который используется для общения с Java Gateway. Этот порт был указан нами ранее при добавлении записи в таблицу %Net_Remote.ObjectGateway.
Проверка ЭЦП во входящих SOAP-сообщениях
Загрузите и распакуйте архив
iscjcp-cos-sources.zip с исходным кодом классов smev.JcpUtils и smev.JcpSignature. Импортируйте класс
smev.JcpUtils в Caché с помощью Studio, предварительно перейдя в область, где ведется разработка веб-сервисов. Откройте импортированный класс в Studio и отредактируйте значения параметров JAVAGATEWAYPORT и JAVAGATEWAYSERVER, указав соответственно TCP-порт и IP-адрес используемого Java Gateway. Скомпилируйте класс.
Теперь, чтобы добавить в существующий веб-сервис проверку ЭЦП, достаточно внести в класс веб-сервиса следующий метод:
Method OnPreSOAP(mode As %String, action As %String, request)
{
#dim stream As %Stream.Object = request
if '$isObject(stream)
{
// на случай MIME attachments
#dim index As %Integer = %request.NextMimeData("")
set stream = $select(index="":"", 1:%request.GetMimeData(index))
}
if $isObject(stream)
{
#dim fault As %SOAP.Fault = ##class(smev.JcpUtils).verifySignatureOnPreSoap(stream)
if $isObject(fault) set ..SoapFault = fault
}
}
Это работает на версиях Caché/Ensemble, начиная с 2009.1. Ниже приведен пример веб-сервиса, который осуществляет проверку подписи всех входящих SOAP-сообщений.
Class test.TestService Extends %SOAP.WebService
{
Parameter SERVICENAME = "TestService";
Parameter NAMESPACE = "http://test/wsdl";
Method echo(request As %String) As %String [ ProcedureBlock = 1, SoapAction = "urn:echo", SoapBindingStyle = document, SoapBodyUse = literal, WebMethod ]
{
quit request
}
Method OnPreSOAP(mode As %String, action As %String, request)
{
#dim stream As %Stream.Object = request
if '$isObject(stream)
{
// на случай MIME attachments
#dim index As %Integer = %request.NextMimeData("")
set stream = $select(index="":"", 1:%request.GetMimeData(index))
}
if $isObject(stream)
{
#dim fault As %SOAP.Fault = ##class(smev.JcpUtils).verifySignatureOnPreSoap(stream)
if $isObject(fault) set ..SoapFault = fault
}
}
}
Формирование ЭЦП для исходящих SOAP-сообщений веб-сервиса
Далее рассмотрим случай, когда все ответы веб-сервиса должны быть подписаны ЭЦП организации. В такой ситуации на сервере системы размещается хранилище, содержащее секретный ключ, который используется при формировании подписи. Кроме того, должен быть доступен сертификат, соответствующий этому ключу. В библиотеке iscjcp.jar реализована работа с хранилищем типа “FloppyStore”. Поэтому, для формирования ЭЦП нам потребуется виртуальная дискета с хранилищем ключей.
Размещение секретного ключа и сертификата на виртуальной дискете на сервере системы
Чтобы создать такую дискету, выполните следующие действия:
- Установите драйвер, имитирующий FDD привод, например, ImDisk.
- Из панели управления Windows запустите программу настройки «ImDisk Virtual Disk Driver» и настройте диск с параметрами:
- Drive letter: A,
- Size of virtual disk: 1 Megabyte,
- Device type: Floppy.
- Отформатируйте виртуальную дискету, задав файловую систему FAT.
- Распакуйте содержимое архива FDD.zip на диск A:\.
В результате описанных манипуляций на диске A:\ сервера имеем хранилище ключей, содержащее тестовый секретный ключ. Файл A:\SelfSigned.cer представляет собой тестовый сертификат, соответствующий секретному ключу.
Вы можете самостоятельно генерировать ключи и сертификаты с помощью КриптоПро JCP. Эти процедуры описаны в документации продукта.
Формирование ЭЦП
Загруженный ранее архив
iscjcp-cos-sources.zip с исходным кодом Caché Object Script содержал класс
smev.JcpSignature. Импортируйте этот класс в Caché с помощью Studio.
Откройте класс
smev.JcpUtils в Studio и отредактируйте значение параметра CERTFILENAME, указав полный путь к файлу сертификата – “A:\SelfSigned.cer”. Этот сертификат соответствует секретному ключу, который будет использоваться при формировании ЭЦП. Скомпилируйте класс.
Теперь, чтобы добавить в метод веб-сервиса функционал по формированию ЭЦП для возвращаемых сообщений, необходимо вставить следующую строку в код этого метода:
do ..SecurityOut.AddElement(##class(smev.JcpSignature).%New())
Это работает на версиях Caché/Ensemble, начиная с 2009.1, по 2011.1 включительно. Ниже приведен пример веб-сервиса, где в метод echo() добавлено подписывание ответа.
Class test.TestService Extends %SOAP.WebService
{
Parameter SERVICENAME = "TestService";
Parameter NAMESPACE = "http://test/wsdl";
Method echo(request As %String) As %String [ ProcedureBlock = 1, SoapAction = "urn:echo", SoapBindingStyle = document, SoapBodyUse = literal, WebMethod ]
{
do ..SecurityOut.AddElement(##class(smev.JcpSignature).%New())
quit request
}
Method OnPreSOAP(mode As %String, action As %String, request)
{
#dim stream As %Stream.Object = request
if '$isObject(stream)
{
// на случай MIME attachments
#dim index As %Integer = %request.NextMimeData("")
set stream = $select(index="":"", 1:%request.GetMimeData(index))
}
if $isObject(stream)
{
#dim fault As %SOAP.Fault = ##class(smev.JcpUtils).verifySignatureOnPreSoap(stream)
if $isObject(fault) set ..SoapFault = fault
}
}
}
Тестирование веб-сервиса
Чтобы протестировать проверку/формирование ЭЦП веб-сервисом test.TestService, исходный код которого приведен выше, выполним следующие действия.
- При помощи Studio создадим класс test.TestService в той же области, куда были импортированы классы smev.*. Исходный код нового класса скопируем из вышеприведенного листинга.
- С помощью Блокнота создадим файл C:\Temp\input.xml, содержащий тело входящего SOAP-сообщения для сервиса test.TestService:
<SOAP-ENV:Body wsu:Id=«id1»
xmlns:SOAP-ENV=«schemas.xmlsoap.org/soap/envelope/»
xmlns:wsu=«docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd»>
<s0:echo xmlns:s0=«test/wsdl»>
<s0:request>Здравствуй, мир!</s0:request>
</s0:echo>
</SOAP-ENV:Body>
- Запустим в терминале Caché метод signFile() класса smev.JcpUtils:
write ##class(smev.JcpUtils).signFile(«id1», «C:\Temp\input.xml», «C:\Temp\output.xml»)
Убедимся в том, что в результате работы метода был создан файл C:\Temp\output.xml, содержащий подписанное SOAP-сообщение (Envelope): в заголовке (Header) находится ЭЦП в формате WS-Security, а тело (Body) представляет собой XML-документ, взятый из файла input.xml.
Готовый файл output.xml имеется в архиве xmls.zip.
Направим полученное SOAP-сообщение с ЭЦП на вход сервиса test.TestService. Для этого выполним в терминале Caché следующие команды (при необходимости, замените порт веб-сервера – “57772” на правильный, а также область “user” в предпоследней команде):
set httprequest = ##class(%Net.HttpRequest).%New()
set httprequest.Server = «localhost»
set httprequest.Port = «57772»
set httprequest.WriteRawMode = 1
set httprequest.ContentType = «text/xml»
do httprequest.SetHeader(«SOAPAction»,«urn:echo»)
set fileStream = ##class(%Library.FileBinaryStream).%New()
set fileStream.Filename = «C:\Temp\output.xml»
do httprequest.EntityBody.CopyFrom(fileStream)
do httprequest.Post("/csp/user/test.TestService.cls")
do httprequest.HttpResponse.OutputToDevice()
Если успешно прошла проверка ЭЦП входящего сообщения, а затем и формирование ЭЦП исходящего, то в окне терминала появится ответ веб-сервиса примерно такого вида:
HTTP/1.1 200 OK
CACHE-CONTROL: no-cache
CONNECTION: close
CONTENT-LENGTH: 2352
CONTENT-TYPE: text/xml; charset=UTF-8
DATE: Thu, 01 Dec 2011 20:08:40 GMT
EXPIRES: Thu, 29 Oct 1998 17:04:19 GMT
PRAGMA: no-cache
SERVER: Apache
SET-COOKIE: CSPSESSIONID-SP-57774-UP-csp-user-=0000000100001odLLhtp000000igxIuSVnd12z6BtrSIFFJA—; path=/csp/user/;
<?xml version=«1.0» encoding=«UTF-8» ?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV=’http://schemas.xmlsoap.org/soap/envelope/’ xmlns:xsi=’http://www.w3.org/2001/XMLSchema-instance’ xmlns:s=’http://www.w3.org/2001/XMLSchema’ xmlns:wsse=’http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd’ xmlns:wsu=’http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd’>
<SOAP-ENV:Header>
<ds:Signature xmlns:ds=«www.w3.org/2000/09/xmldsig#»>
<ds:SignedInfo>
<ds:CanonicalizationMethod Algorithm=«www.w3.org/2001/10/xml-exc-c14n#»/>
<ds:SignatureMethod Algorithm=«www.w3.org/2001/04/xmldsig-more#gostr34102001-gostr3411»/>
<ds:Reference URI="#Body-B290AFEC-9812-49E4-9177-6D617D30622C">
<ds:Transforms>
<ds:Transform Algorithm=«www.w3.org/2001/10/xml-exc-c14n#»/>
</ds:Transforms>
<ds:DigestMethod Algorithm=«www.w3.org/2001/04/xmldsig-more#gostr3411»/>
<ds:DigestValue>vs42sfoxCX8naSV2NnBSoIoCvUb1ydvvyNnp5XC7nKQ=</ds:DigestValue>
</ds:Reference>
</ds:SignedInfo>
<ds:SignatureValue>
ES4aVXFSkHr1cnXUcdZYJTPTa+e5//ASQRYhrRMy46pWSwRW93VxgrW+GhATD2xwK3l+8T1Dfsi2
beVfrkQS0g==
</ds:SignatureValue>
<ds:KeyInfo>
<ds:X509Data>
<ds:X509Certificate>
MIIB9TCCAaSgAwIBAgIIRdAY3dqebKUwCAYGKoUDAgIEMCkxJzAlBgNVBAMeHgB0AC4AZQAuAG0A
LgBwAEAAbQBhAGkAbAAuAHIAdTAeFw0xMTExMjkxMzQwMTFaFw0xMjExMjkxMzQwMTFaMCkxJzAl
BgNVBAMeHgB0AC4AZQAuAG0ALgBwAEAAbQBhAGkAbAAuAHIAdTBjMBwGBiqFAwICEzASBgcqhQMC
AiMBBgcqhQMCAh4BA0MABECebxtl5EDpwaWKy2MeJQ7v+NCiIRHiXBeqaqJnNi5AS4aW+14FKKHH
Llu7jjggB06d+/4U29OtDbjfIkPqRUcio4GtMIGqMB0GA1UdDgQWBBTz0qujqn+CC0O9L1aSv39a
ga3EhDALBgNVHQ8EBAMCAcYwEwYDVR0lBAwwCgYIKwYBBQUHAwMwDwYDVR0TBAgwBgEB/wIBATBW
BgNVHQEETzBNgBTz0qujqn+CC0O9L1aSv39aga3EhKErMCkxJzAlBgNVBAMeHgB0AC4AZQAuAG0A
LgBwAEAAbQBhAGkAbAAuAHIAdYIIRdAY3dqebKUwCAYGKoUDAgIEA0EANUalM3ag0xYJ7MqzmCzh
w8ejPqUds37UXKadbyqogZ2yJBMbhWUCsQFyZZZzfc6gXQbRThBTAftfdXxjW8Yusg==
</ds:X509Certificate>
</ds:X509Data>
</ds:KeyInfo>
</ds:Signature>
</SOAP-ENV:Header>
<SOAP-ENV:Body wsu:Id=«Body-B290AFEC-9812-49E4-9177-6D617D30622C»>Здравствуй, мир!</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
Все действия, описанные в статье, можно посмотреть в следующем видеоролике:
комментарии (1)