Zip enc криптопро

Zip enc криптопро Электронная цифровая подпись
Содержание
  1. Читаем закрытый ключ и конвертируем
  2. В какие сроки нужно подать декларацию
  3. Файл header. key
  4. Попытки интеграций с API
  5. Используемые технологии
  6. Какие штрафы можно получить за ошибки в декларации и опоздания
  7. Расчетный счет в Тинькофф
  8. Шифрование файлов и данных с gpg
  9. Функциональность расширяется
  10. Как пользоваться openssl (команды openssl)
  11. КриптоПро CSP
  12. Создание подписанного документа
  13. Доступ к объектам токена pkcs#11
  14. С помощью команды smime
  15. Как заполнить декларацию по форме 8
  16. Симметричное шифрование файлов в openssl
  17. Как заполнить титульный лист
  18. Как и куда подавать декларацию
  19. Кто должен подавать декларацию в Росалкогольрегулирование
  20. Файл masks. key
  21. Добавление шаблонизатора
  22. Порядок подачи декларации в ФСРАР
  23. Для представления деклараций нужно:
  24. Немного статистики
  25. Предыстория проекта
  26. Тесты
  27. Сборка дистрибутива
  28. Какую отчетность нужно сдавать в Росалкогольрегулирование
  29. Генерация XML
  30. Установка сертификатов
  31. Как заполнить декларацию по форме 7

Читаем закрытый ключ и конвертируем

Файл privkey.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <openssl/pem.h>
#include <openssl/cms.h>
#include <openssl/err.h>
#include "gost_lcl.h"

/* Convert little-endian byte array into bignum */
BIGNUM *reverse32bn(char *b, BN_CTX *ctx)
{
	BIGNUM *res;
	char buf[32];
	BUF_reverse(buf, b, 32);
	res = BN_bin2bn(buf, 32, BN_CTX_get(ctx));
	OPENSSL_cleanse(buf, sizeof(buf));
	return res;
}

void xor_material(char *buf36, char *buf5C, char *src)
{
	int i;
	for(i = 0; i < 32; i++)
	{
		buf36[i] = src[i] ^ 0x36;
		buf5C[i] = src[i] ^ 0x5C;
	}
}

int make_pwd_key(char *result_key, char *start12, int start12_len, char *passw)
{
	int result;
	int i;
	char pincode4[1024];
	int pin_len;
	char current[32];
	char material36[32];
	char material5C[32];
	char hash_result[32];
	gost_hash_ctx ctx;
	init_gost_hash_ctx(&ctx, &GostR3411_94_CryptoProParamSet);
	memset(pincode4, 0, sizeof(pincode4));
	pin_len = strlen(passw);
	if (pin_len*4 > sizeof(pincode4)) {	result = 1;	goto err; }
	for(i = 0; i < pin_len; i++)
		pincode4[i*4] = passw[i];

	start_hash(&ctx);
	hash_block(&ctx, start12, start12_len);
	if (pin_len) 
		hash_block(&ctx, pincode4, pin_len * 4);
	finish_hash(&ctx, hash_result);

	memcpy(current, (char*)"DENEFH028.760246785.IUEFHWUIO.EF", 32);

	for(i = 0; i < (pin_len?2000:2); i++)
	{
		xor_material(material36, material5C, current);
		start_hash(&ctx);
		hash_block(&ctx, material36, 32);
		hash_block(&ctx, hash_result, 32);
		hash_block(&ctx, material5C, 32);
		hash_block(&ctx, hash_result, 32);
		finish_hash(&ctx, current);
	}

	xor_material(material36, material5C, current);

	start_hash(&ctx);
	hash_block(&ctx, material36, 32);
	hash_block(&ctx, start12, start12_len);
	hash_block(&ctx, material5C, 32);
	if (pin_len) 
		hash_block(&ctx, pincode4, pin_len * 4);
	finish_hash(&ctx, current);

	start_hash(&ctx);
	hash_block(&ctx, current, 32);
	finish_hash(&ctx, result_key);

	result = 0; //ok
err:
	return result;
}

BIGNUM *decode_primary_key(char *pwd_key, char *primary_key, BN_CTX *bn_ctx)
{
	BIGNUM *res;
	char buf[32];
	gost_ctx ctx;
	gost_init(&ctx, gost_cipher_list->sblock);
	gost_key(&ctx, pwd_key);
	gost_dec(&ctx, primary_key, buf, 4);
	res = reverse32bn(buf, bn_ctx);
	OPENSSL_cleanse(buf, sizeof(buf));
	return res;
}

BIGNUM *remove_mask_and_check_public(char *oid_param_set8, BIGNUM *key_with_mask, BIGNUM *mask, char *public8, BN_CTX *ctx)
{
	int result;
	EC_KEY *eckey = NULL;
	const EC_POINT *pubkey;
	const EC_GROUP *group;
	BIGNUM *X, *Y, *order, *raw_secret, *mask_inv;
	char outbuf[32], public_X[32];
	ASN1_OBJECT *obj;
	int nid;

	order = BN_CTX_get(ctx);
	mask_inv = BN_CTX_get(ctx);
	raw_secret = BN_CTX_get(ctx);
	X = BN_CTX_get(ctx);
	Y = BN_CTX_get(ctx);
	if (!order || !mask_inv || !raw_secret || !X || !Y) { result = 1; goto err; }

	obj = ASN1_OBJECT_create(0, oid_param_set8+1, *oid_param_set8, NULL, NULL);
	nid = OBJ_obj2nid(obj);
	ASN1_OBJECT_free(obj);

	if (!(eckey = EC_KEY_new())) { result = 1; goto err; }
	if (!fill_GOST2001_params(eckey, nid)) { result = 1; goto err; }
	if (!(group = EC_KEY_get0_group(eckey))) { result = 1; goto err; }
	if (!EC_GROUP_get_order(group, order, ctx)) { result = 1; goto err; }

	if (!BN_mod_inverse(mask_inv, mask, order, ctx)) { result = 1; goto err; }
	if (!BN_mod_mul(raw_secret, key_with_mask, mask_inv, order, ctx)) { result = 1; goto err; }

	if (!EC_KEY_set_private_key(eckey, raw_secret)) { result = 1; goto err; }
	if (!gost2001_compute_public(eckey)) { result = 1; goto err; }
	if (!(pubkey = EC_KEY_get0_public_key(eckey))) { result = 1; goto err; }
	if (!EC_POINT_get_affine_coordinates_GFp(group, pubkey, X, Y, ctx)) { result = 1; goto err; }

	store_bignum(X, outbuf, sizeof(outbuf));
	BUF_reverse(public_X, outbuf, sizeof(outbuf));
	if (memcmp(public_X, public8, 8) != 0) { result = 1; goto err; }

	result = 0; //ok
err:
	if (eckey) EC_KEY_free(eckey);
	if (result == 0) return raw_secret;
	return NULL;
}

int file_length(char *fname)
{
	int len;
	FILE *f = fopen(fname, "rb");
	if (f == NULL) return -1;
	fseek(f, 0, SEEK_END);
	len = ftell(f);
	fclose(f);
	return len;
}

int read_file(char *fname, int start_pos, char *buf, int len)
{
	int read_len;
	FILE *f = fopen(fname, "rb");
	if (f == NULL) return 1;
	if (start_pos) fseek(f, start_pos, SEEK_SET);
	read_len = fread(buf, 1, len, f);
	fclose(f);
	if (read_len != len) return 1;
	return 0; //ok
}

int get_asn1_len(unsigned char *buf, int *size_hdr)
{
	int n, i, res;
	int pos = 0;
	if ((buf[pos]&0x80) == 0) {
		*size_hdr = 1;
		return buf[pos];
	}
	n = buf[pos++]&0x7f;
	res = 0;
	for(i = 0; i < n; i++) {
		res = res*256 + buf[pos++];
	}
	*size_hdr = n+1;
	return res;
}

#define MAX_HEADER 20000
int read_container(char *fpath, int flag2, char *salt12, char *primary_key, char *masks_key, char *public8, char *oid_param_set8)
{
	int result;
	char primary_path[1024+30];
	char masks_path[1024+30];
	char header_path[1024+30];
	char header_buf[MAX_HEADER];
	int header_len;
	int i, len, pos, size_hdr;

	if (strlen(fpath)>1024) { result = 1; goto err; }

	sprintf(header_path, "%s/header.key", fpath);
	if (flag2 == 0)
	{
		sprintf(primary_path, "%s/primary.key", fpath);
		sprintf(masks_path, "%s/masks.key", fpath);
	}
	else
	{
		sprintf(primary_path, "%s/primary2.key", fpath);
		sprintf(masks_path, "%s/masks2.key", fpath);
	}

	if (read_file(primary_path, 4, primary_key, 32)) { result = 1; goto err; }
	if (read_file(masks_path, 4, masks_key, 32)) { result = 1; goto err; }
	if (read_file(masks_path, 0x26, salt12, 12)) { result = 1; goto err; }

	header_len = file_length(header_path);
	if (header_len < 0x42 || header_len > MAX_HEADER) { result = 1; goto err; }
	if (read_file(header_path, 0, header_buf, header_len)) { result = 1; goto err; }

//------------- skip certificate ---------------------------
	pos = 0;
	for(i = 0; i < 2; i++)
	{
		get_asn1_len(header_buf+pos+1, &size_hdr);
		pos += size_hdr+1;
		if (pos > header_len-8) { result = 2; goto err; }
	}

//------------------ get oid_param_set8 -----------------------
#define PARAM_SET_POS 34
	if (memcmp(header_buf+pos+PARAM_SET_POS, "\x6\x7", 2) != 0) { result = 2; goto err; }
	memcpy(oid_param_set8, header_buf+pos+PARAM_SET_POS+1, 8);

//------------------ get public8 -----------------------
	result = 2; //not found
	pos += 52;
	for(i = 0; i < 3; i++)
	{
		len = get_asn1_len(header_buf+pos+1, &size_hdr);
		if (len == 8 && memcmp(header_buf+pos, "\x8a\x8", 2) == 0)
		{
			memcpy(public8,header_buf+pos+2,8);
			result = 0; //ok
			break;
		}
		pos += len+size_hdr+1;
		if (pos > header_len-8) { result = 2; goto err; }
	}
err:
	OPENSSL_cleanse(header_buf, sizeof(header_buf));
	return result;
}

#define START_OID 0x12
#define START_KEY 0x28
unsigned char asn1_private_key[72] = {
	0x30,0x46,2,1,0,0x30,0x1c,6,6,0x2a,0x85,3,2,2,0x13,0x30,0x12,6,7,0x11,
	0x11,0x11,0x11,0x11,0x11,0x11,6,7,0x2a,0x85,3,2,2,0x1e,1,4,0x23,2,0x21,0
};

int main(int argc, char **argv)
{
	int result;
	char *container_path;
	char *passw;
	char salt12[12];
	char primary_key[32];
	char masks_key[32];
	char public8[8];
	char oid_param_set8[8];
	BN_CTX *ctx;
	BIGNUM *key_with_mask;
	BIGNUM *mask;
	BIGNUM *raw_key;
	char pwd_key[32];
	char outbuf[32];

	ctx = BN_CTX_new();

	if (argc == 2)
	{
		container_path = argv[1];
		passw = "";
	}
	else
	if (argc == 3)
	{
		container_path = argv[1];
		passw = argv[2];
	}
	else
	{
		printf("get_private container_path [passw]\n");
		result = 1;
		goto err;
	}

	if (read_container(container_path, 0, salt12, primary_key, masks_key, public8, oid_param_set8) != 0 &&
		read_container(container_path, 1, salt12, primary_key, masks_key, public8, oid_param_set8) != 0)
	{
		printf("can not read container from %s\n", container_path);
		result = 2;
		goto err;
	}

	make_pwd_key(pwd_key, salt12, 12, passw);
	key_with_mask = decode_primary_key(pwd_key, primary_key, ctx);
	OPENSSL_cleanse(pwd_key, sizeof(pwd_key));
	mask = reverse32bn(masks_key, ctx);
	raw_key = remove_mask_and_check_public(oid_param_set8, key_with_mask, mask, public8, ctx);

	if (raw_key)
	{
		BIO *bio;
		store_bignum(raw_key, outbuf, sizeof(outbuf));
		memcpy(asn1_private_key+START_OID, oid_param_set8, 8);
		memcpy(asn1_private_key+START_KEY, outbuf, 32);
		//bio = BIO_new_file("private.key", "w");
		bio = BIO_new_fp(stdout, BIO_NOCLOSE | BIO_FP_TEXT);
		PEM_write_bio(bio, "PRIVATE KEY", "", asn1_private_key, sizeof(asn1_private_key));
		BIO_free(bio);
		OPENSSL_cleanse(outbuf, sizeof(outbuf));
		OPENSSL_cleanse(asn1_private_key, sizeof(asn1_private_key));
		result = 0; //ok
	}
	else
	{
		printf("Error check public key\n");
		result = 3;
	}

err:
	BN_CTX_free(ctx);
	OPENSSL_cleanse(salt12, sizeof(salt12));
	OPENSSL_cleanse(primary_key, sizeof(primary_key));
	OPENSSL_cleanse(masks_key, sizeof(masks_key));
	return result;
}

Небольшой комментарий.

Основную работу выполняют следующие 3 функции:

1. Создаем ключ хранения исходя из 12-ти байтовой «соли» и пароля.

make_pwd_key(pwd_key, salt12, 12, passw);

2. Расшифровываем основной ключ на ключе хранения.

key_with_mask = decode_primary_key(pwd_key, primary_key, ctx);

3. Делим ключ с маской на маску.

raw_key = remove_mask_and_check_public(oid_param_set8, key_with_mask, mask, public8, ctx);

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

if (!BN_mod_inverse(mask_inv, mask, order, ctx)) { result = 1; goto err; }
if (!BN_mod_mul(raw_secret, key_with_mask, mask_inv, order, ctx)) { result = 1; goto err; }

В какие сроки нужно подать декларацию

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

Когда сдают алкогольную декларацию в 2022 году:

Отчетный периодКрайний срок подачи декларации
I квартал 202220 апреля 2022
II квартал 202220 июля 2022
III квартал 202221 октября 2022
IV квартал 202220 января 2023

Если уже после подачи компания нашла в декларации ошибку, ее еще можно исправить — на это есть три месяца с даты крайнего срока подачи декларации. Для этого нужно подать корректирующую декларацию. Например, если до 20 января 2022 года подали первичную декларацию за IV квартал 2021 года с недочетами, декларацию с корректными данными нужно сдать до 20 апреля.

Файл header. key

Из этого файла нам потребуется параметры электронной подписи CryptoProParamSet (подчеркнуто красным).

  • GostR3410_2001_CryptoPro_A_ParamSet — 1.2.643.2.2.35.1
  • GostR3410_2001_CryptoPro_B_ParamSet — 1.2.643.2.2.35.2
  • GostR3410_2001_CryptoPro_C_ParamSet — 1.2.643.2.2.35.3
  • GostR3410_2001_CryptoPro_XchA_ParamSet — 1.2.643.2.2.36.0
  • GostR3410_2001_CryptoPro_XchB_ParamSet — 1.2.643.2.2.36.1

А также первые 8 байт открытого ключа (подчеркнуто) для контроля правильности чтения закрытого.

header.key

Попытки интеграций с API

За время существования проекта было две попытки интеграций с API операторов маркировки.

Первая интеграция была с СУЗ (станцией управления заказами) оператора ЦРПТ (Центра развития перспективных технологий). Она прошла успешно, все работало, но в какой-то момент оператор без предупреждения изменил поведение API. Это привело к массовым обращениям от пользователей, которые сообщали об ошибках в работе приложения.

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

Вообще работа с криптографией — это весьма интересная задача. Но использовать отечественного криптопровайдера типа КриптоПро в десктопном Java-приложении — это задача «со звездочкой».

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

  • Во-первых, была плохая коммуникация с поддержкой оператора РБ, нежелание разбираться и что-то решать. Их позиция: «У нас все работает, проблема на вашей стороне».

  • Во-вторых, нарушение стандарта RFC 8259 при реализации API. В чате поддержка оператора сказала, что символы GS не нужно экранировать при использовании их API. Это дважды нарушает 7-ой пункт стандарта.

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

Используемые технологии

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

При разработке десктопного приложения на Java выбор в принципе стоит между JavaFx и Swing. JavaFx показался дружелюбным в разработке, поэтому выбор пал на него. Swing все-таки считается устаревшей технологией и даже Oracle говорит о замене Swing на JavaFx.

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

Какие штрафы можно получить за ошибки в декларации и опоздания

За несвоевременную подачу или непредставление декларации и за искажение данных можно получить штраф.

Штрафы для ИП и компаний различаются:

  • для ИП — от 5000 до 10 000 ₽;
  • для компании — от 50 000 до 100 000 ₽.

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

Об аннулировании лицензии — пп. 6. п. 3.1 ст. 20 закона № 171-ФЗ

Расчетный счет в Тинькофф

Предложение Тинькофф

Расчетный счет в Тинькофф

  • До 4 месяцев бесплатного обслуживания
  • До 500 000 ₽ на сервисы партнеров
  • Бесплатная онлайн-бухгалтерия


Подробнее

Шифрование файлов и данных с gpg

Про шифрование в gpg нужно знать, что оно может быть:

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

Второе, что нужно знать: шифрование можно совмещать с подписыванием файла. Подписывание файла и проверку подписи мы рассмотрим далее. Также далее мы рассмотрим одновременное шифрование и подпись файла.

Третье: зашифровать можно одним или более публичными ключами.

Для шифрования файла используя симметричный метод с паролем используйте опцию -c (либо её длинный аналог –symmetric):

Следующая команда для шифрования файла test.php паролем в gpg:

gpg -c test.php

В результате шифрования будет создан файл с расширением .gpg (в данном случае это будет файл test.php.gpg).

Читайте также:  Как подписать документ электронной подписью?

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

  • — означает шифрование данных
  • — означает симметричное шифрование
  • — означает зашифровать данные для пользователя с определённым id

Пример команды симметричного шифрования файла test.php для пользователя Alexey Miloserdov с возможностью его расшифровки приватным ключом ЛИБО для расшифровки паролем:

gpg -e -c -r 'Alexey Miloserdov' test.php

Точнее говоря, комбинирование двух опций -e и -c шифрует ключ сессии публичным ключом и симметричным шифром, поэтому для расшифровки может использоваться И приватный ключ, И пароль (на выбор). Если на другом компьютере, где вы расшифровываете файл, имеется ваш приватный ключ, то при расшифровке будет запрошен пароль приватного ключа. Если приватный ключ отсутствует, то будет запрошен пароль, который использовался при шифровании файла.

Для шифрования публичным ключом (-e), чтобы файл (test.php) мог расшифровать только владелец соответствующего парного приватного ключа (-r ‘Alexey Miloserdov’):

gpg -e -r 'Alexey Miloserdov' test.php

Вместо опции -r ‘Имя Адресата’ можно использовать опцию -R ‘Имя Адресата’ или её длинный аналог –hidden-recipient ‘Имя Адресата’. Она также шифрует файл для указанного адресата, но имя этого адресата шифруется.

Пример шифрования файла test.php публичным ключом пользователя Alexey Miloserdov, но с зашифрованным именем адресата.

gpg -e -R 'Alexey Miloserdov' test.php

Обратите внимание, что во всех случаях шифрования оригинальный файл остаётся!!! Вам самим нужно решать, что с ним делать, например, удалить его.

Чтобы каждый раз не вводить имя получателя, можно установить значение по умолчанию опцией –default-recipient. Также с ней в комплекте идут опции –default-recipient-self и –no-default-recipient.

Функциональность расширяется

Как скрестить ужа с ежом?

Проект развивался по классическому Agile: вначале скейт, потом самокат, потом велосипед. На старте основная функциональность заключалась в печати датаматриксов. Потом добавилось генерирование XML-файлов, а в самой программе появились настройки.

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

К счастью, есть проект JavaFX Weaver, который решает проблемы получения колючей проволоки путем скрещивания ежа и ужа: контроллеры превращаются в бины, dependency injection становятся доступными и становятся доступными прочие плюшки Spring. Но главное — не требуется полностью переписывать все приложение.

В качестве базы данных была выбрана H2. Она легковесная и не требует установки. Пользователь просто запускает приложение и все работает без предварительной установки СУБД.

Как пользоваться openssl (команды openssl)

Команды OpenSSL не столько сложные, сколько запутанные.

Во-первых, их много (48 основных команд, 28 digest команд, 84 cipher команды, а также алгоритмы и методы), некоторые из них выполняют более чем одну функцию, некоторые имеют пересекающиеся функции и не всегда непонятно, какую команду выбрать.

Синтаксис использования команд OpenSSL:

openssl КОМАНДА ОПЦИИ

Ещё один пример как команды OpenSSL могут сбить с толку: у команды x509 есть опция -req, а у команды req есть опция -x509.

Если вы хотите получить справку по командам OpenSSL, то вам нужно знать, что это делается так:

man openssl-КОМАНДА
# ИЛИ
man КОМАНДА


Например:

man openssl-req
man openssl-x509
man openssl-genpkey
man openssl-enc
man openssl-rsa
# ИЛИ
man req
man x509
man genpkey
man enc
man rsa

При этом если по аналогии попытаться использовать в командной строке openssl-req или req, то такие команды будет не найдены (нужно использовать openssl req …).

Команды openssl могут быть громоздкими за счёт того, что через одну из опций команды передаются опции сертификата.

На самом деле, для типичных задач используется всего несколько команд и несколько опций. Поэтому если понимать суть, то всё довольно просто.

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

  • (заменяет ,  и ) — генерирует приватные ключи
  •  — утилита для создания запросов на подпись сертификата и для создания самоподписанных сертификатов PKCS#10
  •  — утилита для подписи сертификатов и для показа свойств сертификатов
  • — утилита для работы с ключами RSA, например, для конвертации ключей в различные форматы
  •  — различные действий с симметричными шифрами
  •  — создаёт и парсит файлы PKCS#12
  •  — программа для конвертирования CRL в PKCS#7
  •  — выполняет операции с файлами PKCS#7 в DER или PEM формате
  •  — программа для проверки цепей сертификатов
  • — команда реализует клиент SSL/TLS, который подключается к удалённому хосту с использованием SSL/TLS. Это очень полезный инструмент диагностики для серверов SSL
  • — является минимальным CA-приложением. Она может использоваться для подписи запросов на сертификаты в различных формах и генерировать списки отзыва сертификатов. Она также поддерживает текстовую базу данных выданных сертификатов и их статус
  •  — эта команда генерирует указанное число случайных байтов, используя криптографически безопасный генератор псевдослучайных чисел (CSPRNG)
  • — команда может быть использована для подписи, проверки, шифрования и дешифрования данных с использованием алгоритма RSA
  • — команда обрабатывает S/MIME почту. Она может шифровать, расшифровывать, подписывать и проверять сообщения S/MIME

Чтобы увидеть полный список команд выполните:

openssl list -commands


Пример вывода:

asn1parse         ca                ciphers           cms               
crl               crl2pkcs7         dgst              dhparam           
dsa               dsaparam          ec                ecparam           
enc               engine            errstr            gendsa            
genpkey           genrsa            help              list              
nseq              ocsp              passwd            pkcs12            
pkcs7             pkcs8             pkey              pkeyparam         
pkeyutl           prime             rand              rehash            
req               rsa               rsautl            s_client          
s_server          s_time            sess_id           smime             
speed             spkac             srp               storeutl          
ts                verify            version           x509

КриптоПро CSP

Для подписи электронных документов, будем использовать программное обеспечение КриптоПро CSP.

Zip enc криптопро

Использование иных криптографических программ под административную ответственность руководителей и лиц, использующих ПО СКЗИ, не имеющих действующего сертификата соответствия ФСБ РФ.

Создание подписанного документа

Подписать можно абсолютно любой файл.

Перейдите в пункт “Создание подписи”

Zip enc криптопро

Нажмите на кнопку “Выбрать файл для подписи” и выберите файл, который нужно подписать.

Zip enc криптопро
Zip enc криптопро

Выберите подпись, которой необходимо подписать документ. Обратите внимание – ЭЦП должно быть актуальной.

После выбора ЭЦП станет активной кнопка “Подписать”. Необходимо её нажать. Если документ подписан и нет ошибок – под кнопкой “подписать появится соответствующая надпись. Либо будет указана ошибка с кодом.

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

Zip enc криптопро

Доступ к объектам токена pkcs#11

Для доступа к объектам (ключи, сертификаты) токенов PKCS#11 служит утилита certutil. Отметим, что по своей функциональности утилита certutil не уступает утилите openssl. Для просмотра функциональности утилиты certutil достаточно выполнить команду:

$certutil -H

Но нас сейчас интересует только

. Напомним, что при хранении на токене PKCS#11 и тем и другим, как правило, приписываются атрибуты CKA_ID и CKA_LABEL. Для просмотра списка сертификатов на том или ином токене необходимо выполнить следующую команду:

$certutil -L [-d <хранилище NSS>] [-h <метка токена>]

В качестве метки токена может быть указана реальная метка токена или одно из ключевых слов — all или internal. В первом случае (метка токена all) перебираются все токены, подключенные к хранилищу NSS, а во втором случае (internal или «NSS Certificate DB») будет просматриваться внутренний токен хранилища «NSS Certificate DB».

Например, для получения списка сертификатов, находящихся на токене с меткой «LS11SW2021», модуль доступа к которому зарегистрирован в хранилище NSS “/home/a513/tmp/TEST_NSS” необходимо выполнить следующую команду:

$ certutil -L -d /home/a513/tmp/TEST_NSS -h "LS11SW2021"
Enter Password or Pin for "LS11SW2021":
Certificate Nickname                                  Trust Attributes
                                                      SSL,S/MIME,JAR/XPI
LS11SW2021:TestCA_P11                                 u,u,u
LS11SW2021:clientnss from CryptoArmPKCS               u,u,u
LS11SW2021:ТестШифр                                   u,u,u
LS11SW2021:Thenderbird-60.3.0 from 32                 u,u,u
LS11SW2021:Всесильный Хабр from УЦ 12_512             u,u,u
LS11SW2021:Text4Key                                   u,u,u
LS11SW2021:KmailKleopatra от GnuPG-2001               u,u,u
LS11SW2021:setvernss from CryptoArmPKCS               u,u,u
LS11SW2021:Ф И О from УЦ 12_512                       u,u,u
LS11SW2021:Test 12 512                                u,u,u
LS11SW2021:Kleopatra от GnuPG-2001                    ,,   
$

Список сертификатов, находящихся на токене, выводится в две колонки. В первой колонке дается nicknamе сертификата, а во второй — атрибуты доверия этого сертификата.

Причем, если сертификат имеет закрытый ключ на токене, где он находится, то в этой колонке будет значение «u,u,u».

Если атрибуты не устанавливались, то в колонке будет значение “,,”.

Сертификаты с атрибутами доверия «u,u,u» (у них есть закрытый ключ) могут быть использованы для аутентификации или формирования электронной подписи.

Другие значения атрибутов доверия могут быть установлены для сертификатов, находящихся на встроенном токене «NSS Certificate DB». Но об этом позже.

Что же такое nickname сертификата в NSS?

Для внутреннего токена («NSS Certificate DB») метка токена в nickname может отсутствовать.

Если рассматривать механизмы токенов PKCS#11, то мы увидим, что операции генерации ключей, импорта сертификатов и ключей сами по себе не предусматривают установку значений атрибутов CKA_ID и CKA_LABEL. Это все перекладывается на разработчика прикладного ПО. Но, если вы используете утилиты NSS для работы с токенами, то оказывается, что они берут на себя эти хлопоты.

Для просмотра списка закрытых ключей используется следующая команда:

$certutil -K [-d <хранилиже NSS>] [-h <метка токена>]

При этом распечатывается тип ключа, CKA_ID и CKA_LABEL ключа.

Но вернемся к сертификатам. Для просмотра сертификата, находящегося на токене, в текстовом виде достаточно выполнить команду:

$certutil -L [-d <хранилище NSS>] -n <nickname сертификата>

Например:

certutil -L -d ‘/home/a513/tmp/TEST_NSS’ -n ‘NSS Certificate DB:Тестовый сертификат’

$ certutil -L -d “/home/a513/tmp/TEST_NSS” -n «NSS Certificate DB: Тестовый сертификат»

Certificate:

Data:

Version: 3 (0x2)

Serial Number: 4096 (0x1000)

Signature Algorithm: PKCS #1 SHA-256 With RSA Encryption

Issuer: «[email protected],OGRN=1111111111111,INN=222222222222,CN=Удос

товеряюший Центр,OU=Отдел Удостоверя

юший Центр,O=Удостоверяюший Центр,STR

EET=»ул. Хавровская, д. 0″,L=Хабраград,ST=М

осковская область,C=RU”

Validity:

Not Before: Tue Jul 07 08:40:14 2020

Not After: Fri Aug 06 08:40:14 2021

Subject: «[email protected],CN=Тестовый сертификат»

Subject Public Key Info:

Public Key Algorithm: PKCS #1 RSA Encryption

RSA Public Key:

Modulus:

9a:9f:6c:60:94:f7:ec:f7:94:b3:51:01:e2:1a:c5:25:

28:bb:02:77:49:52:4d:99:8a:6e:26:12:55:8f:71:34:

04:da:39:24:f9:b4:6b:d0:0a:42:27:1b:b2:d7:9b:d9:

c3:76:b0:e0:1c:7c:21:ce:79:9f:d5:2b:17:63:cb:94:

5b:d9:b2:53:ff:b9:bf:4f:3d:cf:b7:8d:8a:37:ba:02:

8c:da:d2:0d:fd:46:5b:45:1d:95:64:07:6e:fa:88:0d:

a4:bd:b3:4a:ed:99:f1:fd:73:c5:b6:05:a0:e5:ee:6b:

c3:83:5b:d0:64:05:77:6a:18:d8:c8:28:a1:d0:06:41:

23:0d:bb:87:8a:77:14:fb:6c:5d:af:db:2b:0b:11:a3:

16:1b:2b:05:18:26:a9:b5:00:4a:40:da:b3:05:aa:2a:

67:c0:18:0d:03:f7:d2:b9:ba:7c:36:f9:95:2e:56:81:

a3:09:99:5e:20:10:95:38:10:c9:c1:6f:c3:6c:a6:1b:

78:51:c6:e4:4f:11:bc:c0:22:4b:ca:59:16:f2:45:95:

0d:fd:7b:46:cf:c7:ac:1c:3d:d7:26:fc:ad:80:3e:2c:

21:93:29:32:a6:79:e2:a8:c6:e9:5e:45:34:d3:38:57:

8f:cd:95:5e:91:09:84:34:21:d2:16:29:69:75:4d:a3

Exponent: 65537 (0x10001)

Signed Extensions:

Name: Certificate Basic Constraints

Critical: True

Data: Is not a CA.

Name: Certificate Key UsageUsages: Digital SignatureKey EnciphermentKey Agreement

Name: Certificate TypeData:

Name: Extended Key UsageTLS Web Client Authentication CertificateE-Mail Protection Certificate

Name: Certificate Subject Key IDData:26:a1:b3:98:1c:fe:62:ba:23:81:96:37:3f:08:bd:70:d6:f2:b1:46

Name: Certificate Authority Key Identifier
Key ID:
0a:b6:f6:87:64:1d:8e:b3:63:08:29:9f:21:59:ad:47:
d8:ea:07:f4
Issuer:
Directory Name: «[email protected],OGRN=1111111111111,INN=22222222
2222,CN=Удостоверяюший Центр,OU=Отд
ел Удостоверяюший Центр,O=Удост
оверяюший Центр,STREET=»ул. Хавровс
кая, д. 0″,L=Хабраград,ST=Московска
я область,C=RU”
Serial Number:
00:a2:9b:22:32:3e:a7:3d:d8

Name: Certificate Subject Alt Name
RFC822 Name: «[email protected]»

Name: Certificate Issuer Alt Name
Error: Parsing extension: Certificate extension value is invalid.
Data: Sequence {
}

Signature Algorithm: PKCS #1 SHA-256 With RSA EncryptionSignature:2f:75:7e:71:9e:15:5c:97:fe:a2:e1:2a:52:39:56:55:e0:62:60:bc:5f:6d:c2:b6:ec:cd:8b:10:b3:b1:3f:e5:d6:d1:5f:a5:fa:61:c1:ce:3e:db:6a:2f:b2:13:46:8d:67:cf:18:09:61:97:01:45:bc:99:bb:0c:d6:0a:a3:03:87:0a:8e:10:3a:d5:e3:94:6d:4a:24:fa:c3:40:0b:43:c2:3b:00:56:06:c4:d2:fc:b2:7e:e9:00:e5:2f:4b:e2:3a:91:49:ce:f8:c3:60:ec:01:74:d8:1a:3b:af:e6:f6:91:db:c5:f1:d7:de:be:18:38:47:41:8a:e2:ef:80:91:10:54:41:ae:55:22:6f:d7:8c:fa:46:b6:b6:2a:ee:6a:0c:c9:03:18:af:4e:93:6c:61:f3:b4:78:0c:61:93:f1:d8:1b:00:c3:e5:29:9a:08:0a:f8:31:67:88:3d:c3:88:7a:60:c0:c4:52:94:25:56:e5:a3:df:7d:58:c5:df:9a:c7:22:7e:2c:f6:fb:2c:bf:b7:7f:c5:ca:2b:0f:8c:20:77:b9:1f:e0:62:5a:3d:d4:6f:12:ea:c8:51:67:a5:75:ad:e9:ac:9e:4e:2e:2d:34:80:e7:d8:64:f6:8f:2f:33:32:1f:8b:bc:9c:e8:77:4a:ee:7b:84:31:ec:28:e9:70Fingerprint (SHA-256):96:F4:A5:FA:6D:8A:F8:7E:A6:10:49:BD:43:34:C1:92:C6:7D:FF:63:41:8E:69:C0:AC:34:6B:CB:63:7B:56:31Fingerprint (SHA1):B6:91:9B:C6:7A:45:9C:92:FD:E7:C7:33:00:FA:91:DF:7D:5F:00:21

Mozilla-CA-Policy: false (attribute missing)
Certificate Trust Flags:
SSL Flags:
User
Email Flags:
User
Object Signing Flags:
User
$

Читайте также:  Типовой закон ЮНСИТРАЛ об электронных подписях (2001 год) - ПАРАГРАФ-WWW

С помощью команды smime

Решение для безопасного и высокозащищенного кодирования любого файла в OpenSSL и командной строке:

Для шифрования файлов вы должны иметь готовый сертификат X.509 в формате PEM.

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

openssl req -x509 -nodes -days 100000 -newkey rsa:8192 -keyout private_key.pem -out certificate.pem

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

openssl req -x509 -days 100000 -newkey rsa:8192 -keyout private_key.pem -out certificate.pem

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

openssl req -x509 -new -days 100000 -key private_key.pem -out certificate.pem

Чтобы зашифровать файл выполните:

openssl smime -encrypt -binary -aes-256-cbc -in plainfile.zip -out encrypted.zip.enc -outform DER yourSslCertificate.pem

В этой команде:

  • — ssl команда для S/MIME утилиты
  • — выбранным действием с файлом является шифрование
  • — использовать безопасный файловый процесс. Обычно входное сообщение преобразуется в «канонический» формат, как того требует спецификация S/MIME, этот переключатель отключает его. Это необходимо для всех двоичных файлов (например, изображений, звуков, ZIP-архивов).
  • — выбран шифр AES в 256 бит для шифрования (сильный). Если не указано, используется 40-битный RC2 (очень слабый).
  • — файл для шифрованиия
  • — файл для сохранения зашифрованных данных
  • — закодировать выходной файл как двоичный файл. Если не указан, файл будет закодирован в base64, а размер файла будет увеличен на 30%.
  • — имя файла вашего сертификата. Он должен быть в формате PEM.

Эта команда может очень эффективно сильно шифровать большие файлы независимо от их формата.

Известная проблема: что-то не так происходит, при попытках зашифровать огромный файл (&gt; 600 МБ). Ошибка не выводится, но зашифрованный файл будет повреждён. Всегда проверяйте каждый файл! (или используйте PGP — больше поддержки шифрования файлов с открытым ключом).


Расшифровка файла:

openssl smime -decrypt -binary -in encrypted.zip.enc -inform DER -out decrypted.zip -inkey private.key -passin pass:ВАШ-ПАРОЛЬ

В этой команде:

  • — то же самое, что и в -outform выше
  • — имя файла вашего приватного ключа. Он должен быть в формате PEM и может быть зашифрован паролем.
  • — ваш пароль для зашифрованного приватного ключа.

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

Как заполнить декларацию по форме 8

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

Раздел I. В графе «Поступления» нужно отдельно указать закупки от импортеров и от российских производителей и оптовиков. В форме 7 такого разделения нет.


Декларация по алкоголю: сведения об импорте
Сведения об импорте указываются в графе 9 раздела I декларации по форме 8

Раздел II и III. В графе «Сведения о поставщике продукции» не нужно указывать вид деятельности из лицензии.


Алкогольная декларация: сведения о производителе и поставщике
Во II и III разделах декларации по форме 8 одинаковые блоки для «Сведений о производителе» и «Сведений о поставщике»

Симметричное шифрование файлов в openssl

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

Для шифрования используется команда следующего вида:

openssl enc -ШИФР -in ДЛЯ-ШИФРОВАНИЯ -out ЗАШИФРОВАНЫЕ-ДАННЫЕ

Для расшифровки похожая команда, но с опцией -d, также ЗАШИФРОВАНЫЕ-ДАННЫЕ теперь являются входными, а на выходе РАСШИФРОВАННЫЕ-ДАННЫЕ:

openssl enc -ШИФР -d -in ЗАШИФРОВАНЫЕ-ДАННЫЕ -out РАСШИФРОВАННЫЕ-ДАННЫЕ

В качестве ШИФРА рекомендуют aes-256-cbc, а полный список шифров вы можете посмотреть командой:

openssl enc -list

Ещё настоятельно рекомендуется использовать опцию -iter ЧИСЛО. Она использует указанное ЧИСЛО итераций для пароля при получении ключа шифрования. Высокие значения увеличивают время, необходимое для взлома пароля брут-форсом зашифрованного файла.

Эта опция включает использование алгоритма PBKDF2 для получения ключа. Указывать можно высокие значения — десятки и сотни тысяч. В разделе «Как создать базу данных KeePass» при создании базы данных используется такой же алгоритм (первая версия), там для 1 секундной задержки я выставлял значение в 25 миллионов инераций.

Пример шифрования файла art.txt шифром aes-256-cbc, зашифрованные данные будут помещены в файл с именем art.txt.enc, при получении ключа шифрования используется десять миллионов итераций (на моём железе выполнение команды заняло несколько секунд):

openssl enc -aes-256-cbc -in art.txt -out art.txt.enc -iter 10000000

Введите, а затем подтвердите пароль для шифрования:

В результате будет создан зашифрованный файл art.txt.enc.

Для расшифровки файла art.txt.enc и сохранения данных в файл art-new.txt:

openssl enc -aes-256-cbc -d -in art.txt.enc -out art-new.txt -iter 10000000

Если файл успешно расшифрован, то не будет выведена никакая дополнительная информация.

В случае неудачной расшифровки будет показано примерно следующее:

bad decrypt
140381536523584:error:06065064:digital envelope routines:EVP_DecryptFinal_ex:bad decrypt:crypto/evp/evp_enc.c:583:

Возможные причины ошибки:

  • неверный пароль
  • неверный алгоритм для расшифровки
  • неправильно указано количество итераций с опцией
  • неверно указан файл для расшифровки

Обратите внимание, что для расшифровки также нужно указать опцию -iter с тем же самым значением, которое было указано при шифровании. Конечно, можно не использовать опцию -iter при шифровании (а, следовательно, и при расшифровке), но в этом случае шифрование считается ненадёжным!

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

Предыдущие команды для шифрования и расшифровки могут запускаться чуть иначе:

openssl ШИФР

Например:

openssl aes-256-cbc -in art.txt -out art.txt.enc -iter 10000000

То есть пропускается слово enc, и перед шифром убирается дефис. Обе команды равнозначны.

Зашифрованный файл представляет собой бинарные данные, которые не получится передать, например, в текстовом сообщении (в чате). Используя опцию -a (или её псевдоним -base64), можно закодировать зашифрованные данные в кодировку Base64:

openssl enc -aes-256-cbc -in art.txt -out art.txt.b64 -iter 10000000 -a

Содержимое полученного файла art.txt.b64 можно открыть любым текстовым редактором и переслать в мессенджере или в чате.

Для расшифровки также нужно указать опцию -a:

openssl enc -aes-256-cbc -d -in art.txt.b64 -out art-new.txt -iter 10000000 -a

Чтобы просто закодировать бинарный файл в кодировку base64:

openssl enc -base64 -in file.bin -out file.b64

Чтобы раскодировать этот файл:

openssl enc -base64 -d -in file.b64 -out file.bin

Чтобы зашифровать файл используя указанный ПАРОЛЬ в команде (не интерактивный режим):

openssl enc -aes128 -pbkdf2 -d -in file.aes128 -out file.txt -pass pass:ПАРОЛЬ

Зашифровать файл, затем закодировать его с помощью base64 (например, его можно отправить по почте), используя AES-256 в режиме CTR и с получением производной ключа PBKDF2:

openssl enc -aes-256-ctr -pbkdf2 -a -in file.txt -out file.aes256

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

openssl enc -aes-256-ctr -pbkdf2 -d -a -in file.aes256 -out file.txt -pass file:<ФАЙЛ-С-ПАРОЛЕМ>

Как заполнить титульный лист

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


Алкогольная декларация: титульный лист
Нижний правый угол титульного листа заполняет работник территориального подразделения Росалкогольрегулирования, который примет декларацию

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

Как и куда подавать декларацию

Росалкогольрегулирование принимает декларации только в электронном виде, в формате XML. Если магазин, в котором продается алкоголь, работает в отдаленном районе без интернета, для подачи декларации нужно добраться до места, где интернет есть, — например, в районный центр.

Чтобы подать декларацию, понадобятся:

  • квалифицированная электронная подпись;
  • криптопровайдер — программа, которая шифрует данные, например VipNet CSP или «КриптоПро CSP»;
  • программа для подписания и шифрования деклараций, например «КриптоАРМ 5», «КриптоАРМ ГОСТ», «КриптЭК»;
  • сертификат РАР — его можно скачать на сайте Росалкогольрегулирования после регистрации.
  1. Сохранить файл декларации в формате XML.
  2. В криптопровайдере добавить сертификат квалифицированной электронной подписи и сертификат Росалкогольрегулирования.
  3. Зашифровать и подписать файл. Статус файла должен быть «подписан, упакован в zip, зашифрован» — это формат.xml.sig.zip.enc.
  4. Отправить декларацию через сайт Росалкогольрегулирования или Информационную систему субъекта РФ.

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

Кто должен подавать декларацию в Росалкогольрегулирование

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

  • общепит, если в нем продают алкоголь;
  • компании, которые продают алкоголь в районах без интернета;
  • компании и ИП, которые продают слабоалкогольные напитки: пиво, сидр, пуаре, медовуху.

Компаниям, которые продают только маркированные алкогольные напитки, например водку, вино или коньяк, декларацию сдавать не нужно. Они обязаны вести поштучный учет алкоголя: сканировать каждую бутылку при приемке, а при продаже отправлять через онлайн-кассу сведения о движении товара в ЕГАИС — Единую государственную автоматизированную информационную систему. Через ЕГАИС Росалкогольрегулирования получит всю информацию об обороте маркированного алкоголя. Если компания продает и крепкий алкоголь, и, к примеру, пиво, нужно подать декларацию только по пиву.

В районах без интернета невозможно в реальном времени сделать сверку с ЕГАИС через онлайн-кассу, поэтому продавцы подают декларацию по алкоголю, даже если торгуют только маркированными напитками.

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

Файл masks. key

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

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

masks.key

Добавление шаблонизатора

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

Для меня разработка такой функциональности была непозволительной роскошью, потому что заняла бы много времени без гарантии положительного результата. Однако мне все же хотелось добавить шаблонизатор. По умолчанию в моем приложении было только два варианта печати: либо А4, либо термопринтер. На самой этикетке выводился только датаматрикс и чуть-чуть пользовательского текста.

Первый вариант наклеек для листа А4, а второй — для термопринтера

В поисках решения я подробно изучил продукты компании iText. Их библиотека iТext PDF используется в BarCodesFX для генерации PDF. У нее есть много разных функций и при этом она полностью бесплатная при использовании в опенсорс-проектах. В коде можно полностью собрать pdf-файл с форматированием, дополнительными элементами и картинками. Через нее же происходит преобразование строки в датаматрикс.

У них же обнаружил pdfHTML — библиотеку, которая преобразует HTML-страницу в PDF. Внутри нее нашлась функциональность для заполнения HTML-шаблона. Так у пользователя появилась возможность самостоятельно сверстать практически любую этикетку, а не ограничиваться предустановленными шаблонами.

В шаблоне часть тегов нужно было заменять на пользовательские значения. Например, вместо выводить картинку кода, а вместо — номер страницы. Для этого разработчику достаточно в свойствах передать свою CustomTagWorkerFactory, которая наследует от DefaultTagWorkerFactory. Таким образом, все изменения гармонично встраиваются в поток обработки HTML-кода, при необходимости заменяя дефолтные обработчики тегов на собственные, с помощью имплементации интерфейса ITagWorker .

properties.setTagWorkerFactory(new CustomTagWorkerFactory());
List<IElement> elements =
   HtmlConverter.convertToElements(new FileInputStream(htmlSource), properties);

for (IElement element : elements) {
   document.add((IBlockElement) element);
}

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

Читайте также:  криптопро 5 ключик ruboard

Итоговая кастомизированная наклейка

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

Порядок подачи декларации в ФСРАР

Для представления деклараций нужно:

  1. Зарегистрироваться в личном кабинете на сайте Росалкогольрегулирования;
  2. Установить сертифицированный криптопровайдер. Например, КриптоПро CSP (если уже установлен, повторная установка не требуется);
  3. Установить программу для подписания и шифрования деклараций «КриптоЛайн»;
  4. Cкачать сертификаты ФСРАР.

Немного статистики

Поделюсь статистикой скачиваний за все время существования проекта. Пик в 7000 скачиваний — это выброс из-за сбоя в SourceForge, когда вместо загрузки файла заново запускался таймер, и так до бесконечности. При этом прокручивался счётчик скачиваний. Остальные пики совпадают с этапами внедрения маркировки.

Пик скачивания из РБ (Республика Беларусь) в последние месяцы обусловлен тем, что программа была официально рекомендована оператором РБ для формирования pdf файлов с этикетками.

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

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

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

Предыстория проекта

С 2019 года в РФ введена обязательная маркировка ряда товаров. Более подробно об этом можно узнать на сайте Честного Знака. На момент старта маркировки было много нерешенных вопросов, которые озвучивались в профильных чатах. Малым предприятиям требовалось приобретать и настраивать достаточно дорогие программные решения. В результате многие покупали 1С со всем его богатым набором функций исключительно ради печати DataMatrix-кодов.

Мне показалось, что ситуацию вполне можно исправить, написав программу с единственной функцией — печать кодов маркировки. Собственно, с этого все и началось.

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

Тесты

Боль. Их просто нет. А для уважающего себя проекта они должны быть написаны — хотя бы юнит-тесты. Но на все не хватает времени.

И к сожалению, нет ни малейшего представления, как можно тестировать сам интерфейс. Вся надежда на проект TestFX, но пока у меня еще не дошли руки посмотреть его подробнее.

Сборка дистрибутива

Первые версии приложения поставлялись как fat-jar со всеми зависимостями, но это оказалось неудобно. Целевая аудитория — малые предприниматели, у которых уровень владения компьютером чаще всего на уровне пользователя, изредка — продвинутого пользователя. Для них оказалось весьма проблематично устанавливать Java правильной версии, а потом запускать приложение через bat-файл.

Сначала я рекомендовал сборку Java от Liberica: после инсталляции она связывает зависимости, и приложение запускается просто по двойному клику мыши. Но даже после этого при вводе запроса «barCodesFX» Google продолжал автоматом подсказывать «как запустить». К счастью, сейчас ситуация поменялась.

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

В поисках решения нашелся проект Launch4j. Он позволяет создавать exe-файл, выступающий в роли «стартера» для основного jar-приложения. При этом JRE «правильной» версии помещается в состав дистрибутива и не требует отдельной установки. Это увеличило размер дистрибутива, но в век безлимитного интернета лишние 70 Мб для удобства пользователя не кажутся критичными.

Отдельная благодарность команде Liberica, которая поставляет сборки JRE с подключенными модулями JavaFx — это сильно облегчает запуск приложения.

Сам exe-файл создается при сборке после добавления плагина в maven-проект.

<plugin>
   <groupId>com.akathist.maven.plugins.launch4j</groupId>
   <artifactId>launch4j-maven-plugin</artifactId>
   <version>2.1.2</version>
   <executions>
       <execution>
           <id>l4j-clui</id>
           <phase>compile</phase>
           <goals><goal>launch4j</goal></goals>
           <configuration>
               <headerType>gui</headerType>
               <outfile>target/BarCodesFX.exe</outfile>
               <jar>barCodesFX.jar</jar>
               <icon>src/main/resources/microQR.ico</icon>
               <downloadUrl>https://download.oracle.com/java/17/archive/jdk-17.0.1_windows-x64_bin.msi</downloadUrl>
               <errTitle>Launching error</errTitle>
               <dontWrapJar>true</dontWrapJar>
               <jre>
                   <path>%PWD%/jre-17.0.1</path>
                   <jdkPreference>preferJdk</jdkPreference>
                   <initialHeapPercent>20</initialHeapPercent>
                   <maxHeapPercent>80</maxHeapPercent>
               </jre>
               <versionInfo>
                   <fileVersion>0.0.0.0</fileVersion>
                   <txtFileVersion>${version}</txtFileVersion>
                   <fileDescription>BarCodesFX-${version}</fileDescription>
                   <copyright>Barsukov Viktor</copyright>
                   <productVersion>0.0.0.0</productVersion>
                   <txtProductVersion>${version}</txtProductVersion>
                   <productName>BarCodesFX</productName>
                   <internalName>BarCodesFX</internalName>
                   <originalFilename>BarCodesFX.exe</originalFilename>
               </versionInfo>
               <messages>
                   <startupErr>Startup error</startupErr>
                   <bundledJreErr>Bundled Jre Error</bundledJreErr>
                   <jreVersionErr>Jre Version Error</jreVersionErr>
                   <launcherErr>Launcher Error</launcherErr>
               </messages>
           </configuration>
       </execution>
   </executions>
</plugin>

Через несколько недель после компоновки дистрибутива был обнаружен задокументированный баг: если в пути к exe-файлу есть названия папок на национальных языках (то есть на любом, кроме английского), то приложение не запускается. К счастью, проект Launch4j в целом живой и этот баг пофиксили.

Далее zip-архив вручную упаковывается в SFX-архив и выкладывается в SourceForge. Теперь пользователь может спокойно скачать единственный установочный exe-файл, несколько раз нажать ОК и Далее и получить работающую программу. 

Какую отчетность нужно сдавать в Росалкогольрегулирование

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

  • форма 7 — для общепита и продавцов крепких алкогольных напитков в населенных пунктах без интернета. Ее заполняют и сдают только компании, потому что ИП лицензии на эти виды деятельности не выдают;
  • форма 8 — для продавцов слабоалкогольных напитков. Разговорно ее называют пивной декларацией. Ее заполняют и представляют как организации, так и ИП.

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

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

Генерация XML

Чтобы при работе на сайте не отмечать каждый код в длинном списке, Честный Знак добавил возможность загружать коды файлами. Например, вам нужно списать 2000 кодов и вы просто загружаете один файл, а не выискиваете все эти коды и не прокликиваете их по одному.

Однако файл должен быть в xml-формате с правильно заполненными тегами. Вручную его создавать еще сложнее, чем прокликивать коды по одному. И именно с созданием таких файлов и помогает программа.

Задача создания xml-файлов достаточно типовая, поэтому здесь особо нечего рассказывать, кроме трех интересных моментов:

Для генерации XML используется библиотека Jackson FasterXML. При сериализации списков пришлось добавить кастомный StdSerializer, иначе вместо этого…

<products_list>
    <product>
      <ki>0104600840362250215&lt;QCkpilDMTLs</ki>
    </product>
    <product>
      <ki>0104600840362250215*uJEIn4EH&gt;Rd</ki>
    </product>
</products_list>

… получали лишние теги product вокруг списка.

<products_list>
<product>
    <product>
        <ki>0104600840362250215&lt;QCkpilDMTLs</ki>
    </product>
    <product>
        <ki>0104600840362250215*uJEIn4EH&gt;Rd</ki>
    </product>
</product>
</products_list>

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

ЦРПТ ввели дополнительные правила экранирования символов в кодах маркировки в XML. По этой причине нужно было экранировать одинарные и двойные кавычки, хотя этого не требуется по стандарту (пункт 2.4).

К счастью, в библиотеке FasterXML это удалось решить добавлением кастомной EscapingWriterFactory.

public Writer createEscapingWriterFor(final Writer out, String enc) {

   return new Writer(){
       @Override
       public void write(char[] cbuf, int off, int len) throws IOException {

           String val = "";
           for (int i = off; i < len; i++) {
               val += cbuf[i];
           }
           String escapedStr =  StringEscapeUtils.escapeXml(val);
           out.write(escapedStr);
       }

       @Override
       public void flush() throws IOException {
           out.flush();
       }

       @Override
       public void close() throws IOException {
           out.close();
       }
   };
}

ЦРПТ предоставляет некорректные xsd-схемы. Схемы разделены по документам: есть документ на ввод в оборот, вывод из оборота, списание. Для каждого документа есть свой xsd-файл и при этом есть общая для всех схема — «Определенные пользовательские типы».

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

 <xs:element name="cis" type="cis_type">
   <xs:annotation>
     <xs:documentation>Код идентификации</xs:documentation>
   </xs:annotation>

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

Установка сертификатов

Используя Инструменты КриптоПро, перейдите в пункт меню “Сертификаты” и установите сертификат Федерального казначейства в “доверенные корневые центры сертификации”.

Zip enc криптопро

Затем повторите процедуру для ЭЦП сотрудника, выбрав место установки сертификата “Личное”. Подготовка завершена.

Как заполнить декларацию по форме 7

Декларация по форме 7 состоит из трех разделов.

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

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

Разберем пошагово, как заполнять первый раздел.

Блок «Сведения о продукции». Нужно внести сведения об алкогольной продукции, чтобы ее можно было идентифицировать.

Блок «Сведения о производителе». Нужно вписать данные о компании, которая произвела алкогольную продукцию или ввезла ее в Россию.

№ столбцаНазваниеКак заполнять
3Наименование производителя или импортераСокращенное название, например ООО «Вина Юга»
4ИНН производителяДля российских ИП и компаний
5КПП производителяДля российских компаний

В столбце № 6 заполняются остатки на начало квартала по каждому виду продукции от этих производителей.

Блок «Сведения о поступлениях алкогольной продукции». К поступлениям относятся закупки и возвраты от покупателей.

№ столбцаНазваниеКак заполнять
7Закупки у производителейУказать количество алкоголя в декалитрах
8Закупки у оптовиковУказать количество алкоголя в декалитрах
9ИтогоОбщая сумма закупок
10Возвраты от покупателейУказать количество алкоголя в декалитрах
11Прочие поступленияНапример, излишки, которые нашли при инвентаризации
12Перемещения внутри организацииНапример, алкоголь, который привезли из другого магазина той же компании
13ВсегоСумма всех поступлений

Блок «Сведения о расходах алкогольной продукции». Количество выбывшего алкоголя: продажи, списания, возвраты поставщикам и перемещения внутри компании.

№ столбцаНазваниеКак заполнять
14Объем розничной продажиУказать количество алкоголя в декалитрах
15Прочие расходыНапример, недостачи или продукция, которую израсходовали на проверку качества
16Возвраты поставщикуУказать количество алкоголя в декалитрах
17Перемещения внутри компанииНапример, алкоголь, который отдали в другой магазин той же компании
18ВсегоСумма всех расходов

Блок «Итоги по кварталу». Отчетный период для декларации в Росалкогольрегулирование — квартал.

№ столбцаНазваниеКак заполнять
19Остаток алкогольной продукции на конец кварталаНужно вычислить остатки — формула дальше в статье
20Остаток продукции, маркированной федеральными специальными акцизными марками, требования к которым утратили силуУказать количество импортного алкоголя, который был поставлен до 1 января 2021 года

Остаток на конец квартала рассчитывается по формуле:


Как посчитать остаток на конец квартала: формула

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

Раздел II. В этом разделе нужно внести подробную информацию о закупках — с датами поставок, номерами товарно-транспортных накладных и таможенных деклараций.

Графы 1–5 заполняются так же, как в первом разделе.

В следующих блоках более подробно расписывают информацию о поставщиках и поставках.

Блок «Сведения о поставщике продукции»

№ столбцаНазваниеКак заполнять
6Наименование производителя или импортераЕсли поставщик из России — как в ЕГРЮЛ.
Если импортер — как в договоре
7ИНН производителяДля российских производителей — ИНН.
Для компаний из ЕАЭС — учетный номер плательщика, бизнес-идентификационный номер.
Для производителей других стран ничего указывать не нужно
8КПП производителяДля организаций, у ИП его нет
9Вид деятельностиПолное название вида деятельности, указанное в лицензии поставщика. Например, «Производство и оборот этилового спирта, алкогольной и спиртосодержащей продукции»

Блок «Сведения о поставке»

№ столбцаНазваниеКак заполнять
11Дата закупкиДатой отгрузки поставщиком, указанной в ТТН
12Номер товарно-транспортной накладной (ТТН)Взять из накладной
13Номер таможенной декларацииВзять из декларации
14Объем закупленной продукцииУказан в ТТН или таможенной декларации

Декларация в Росалкогольрегулирование: заполнение второго раздела
Второй раздел декларации Росалкогольрегулирования № 7 заполняют на основе данных из товарно-транспортных накладных или таможенных деклараций

Раздел III. Это информация о возвратах поставщикам. Графы в нем точно такие, как и во втором разделе, отличается только название последней графы — «Объем возвращенной поставщикам продукции» вместо «Объема закупленной продукции». Если возвратов в отчетном периоде не было, заполнять этот раздел не нужно.


Декларация в Росалкогольрегулирование: третий раздел
Третий раздел заполняется по данным из возвратных накладных, которыми магазин подтверждает возврат продукции контрагенту
Оцените статью
ЭЦП Эксперт
Добавить комментарий