,  Linux

Сервер для ЭЦП на Node.js

Author

NMitin

Date Published

cades-bes

Задача отправить из 1С на сервер документ и в ответ получить в Base64 этот документ и открепленную подпись CAdES-BES.
На ОС MX Linux установлена демо-версия КриптоПро CSP.
Скрипт сервера:

1const express = require('express');
2const fs = require('fs');
3const { execFile } = require('child_process');
4const app = express();
5const port = 3000;
6
7// Включаем приём необработанных данных
8app.use(express.raw({ type: '*/*', limit: '10mb' }));
9
10app.post('/sign', (req, res) => {
11 const thumbprint = req.headers['thumbprint'];
12
13 if (!thumbprint) {
14 return res.status(400).send({ error: 'Не указан заголовок thumbprint' });
15 }
16
17 const inputPath = '/tmp/input.cda';
18 const sigPath = '/tmp/output.sig';
19
20 // Сохраняем тело запроса в файл
21 fs.writeFileSync(inputPath, req.body);
22
23 const cryptcpPath = '/opt/cprocsp/bin/amd64/cryptcp';
24 const args = [
25 '-sign',
26 '-detached',
27 '-der',
28 '-dn',
29 '-add',
30 '-cert',
31 '-strict',
32 '-thumbprint',
33 thumbprint,
34 inputPath,
35 sigPath
36 ];
37
38 execFile(cryptcpPath, args, (err, stdout, stderr) => {
39 if (err) {
40 console.error('Ошибка при подписи:', stderr.toString());
41 return res.status(500).send({ error: stderr.toString() });
42 }
43
44 const doc64 = fs.readFileSync(inputPath).toString('base64');
45 const sig64 = fs.readFileSync(sigPath).toString('base64');
46
47 fs.unlinkSync(inputPath);
48 fs.unlinkSync(sigPath);
49
50 res.send({
51 Документ64: doc64,
52 Подпись64: sig64
53 });
54 });
55});
56
57app.listen(port, () => {
58 console.log(`CAdES-sign server listening at http://localhost:${port}`);
59});
60

Теперь обработка для тестирования.
 Реквизиты : ПутьКФайлу, Документ64, Подпись64 - строковые.
Код модуля формы:

1
2&НаКлиенте
3Процедура ПодписатьДокументНаNodeJS(Команда)
4
5 Если Не ЗначениеЗаполнено(ПутьКФайлу) Тогда
6 ПоказатьПредупреждение(,"Файл не выбран.");
7 Возврат;
8 КонецЕсли;
9
10 // Помещаем файл во временное хранилище
11 Двоичные = Новый ДвоичныеДанные(ПутьКФайлу);
12 АдресХранилища = ПоместитьВоВременноеХранилище(Двоичные);
13
14 // Запрос отпечатка
15 Отпечаток = "ОтпечатокПоУмолчанию_Base64";
16 Если Не ВвестиСтроку(Отпечаток, "Введите отпечаток сертификата (без пробелов):", 0, Ложь) Тогда
17 Возврат;
18 КонецЕсли;
19
20 // Передаём на сервер
21 Результат = ПодписатьНаСервере(АдресХранилища, Отпечаток);
22
23 Если ТипЗнч(Результат) = Тип("Структура") Тогда
24 Текст = "Документ64: " + Лев(Результат.Документ64, 100) + "...";
25 Текст = Текст + Символы.ПС + "Подпись64: " + Лев(Результат.Подпись64, 100) + "...";
26 Сообщение = Новый СообщениеПользователю;
27 Сообщение.Текст = Текст;
28 Сообщение.Сообщить();
29 Иначе
30 ПоказатьПредупреждение(,"Ошибка: " + Результат);
31 КонецЕсли;
32
33КонецПроцедуры
34
35
36&НаСервере
37Функция ПодписатьНаСервере(АдресХранилища, Отпечаток)
38
39 Попытка
40 ДвоичныеДанные = ПолучитьИзВременногоХранилища(АдресХранилища);
41 Если ДвоичныеДанные = Неопределено Тогда
42 Возврат "Не удалось получить данные из временного хранилища.";
43 КонецЕсли;
44
45 HTTP = Новый HTTPСоединение("localhost", 3000);
46
47 Запрос = Новый HTTPЗапрос("/sign");
48 Запрос.Заголовки.Вставить("thumbprint", Отпечаток);
49 Запрос.Заголовки.Вставить("Content-Type", "application/octet-stream");
50
51
52 Запрос.УстановитьТелоИзДвоичныхДанных(ДвоичныеДанные);
53
54 Ответ = HTTP.ОтправитьДляОбработки(Запрос);
55
56 Если Ответ.КодСостояния <> 200 Тогда
57 Возврат "Ошибка HTTP: " + Ответ.КодСостояния ;
58 КонецЕсли;
59 JSON = Ответ.ПолучитьТелоКакСтроку();
60
61 Чтение = Новый ЧтениеJSON;
62 Чтение.УстановитьСтроку(JSON);
63
64 Результат = ПрочитатьJSON(Чтение, , , , , ЭтотОбъект);
65
66 Исключение
67 Возврат "Ошибка соединения: " + ОписаниеОшибки();
68 КонецПопытки;
69 Документ64 = Результат.Документ64;
70 Подпись64 = Результат.Подпись64;
71КонецФункции
72
73
74&НаКлиенте
75Процедура ПутьКФайлуНачалоВыбора(Элемент, ДанныеВыбора, ВыборДобавлением, СтандартнаяОбработка)
76 Диалог = Новый ДиалогВыбораФайла(РежимДиалогаВыбораФайла.Открытие);
77 Диалог.Фильтр = "XML-файлы (*.xml)|*.xml";
78 Диалог.Заголовок = "Выберите документ XML";
79
80 Если Диалог.Выбрать() Тогда
81 ПутьКФайлу = Диалог.ПолноеИмяФайла;
82 Сообщить("Выбран файл: " + ПутьКФайлу);
83 КонецЕсли;
84
85КонецПроцедуры
86