logo Наши способы организации безопасного удалённого доступа к рабочим местам и корпоративным ресурсам
Добро пожаловать, Гость! Чтобы использовать все возможности Вход или Регистрация.

Уведомление

Icon
Error

Опции
К последнему сообщению К первому непрочитанному
Offline TerroNet  
#1 Оставлено : 26 марта 2020 г. 10:07:54(UTC)
TerroNet

Статус: Новичок

Группы: Участники
Зарегистрирован: 26.03.2020(UTC)
Сообщений: 5
Российская Федерация

Здравствуйте!
Пытаюсь подписать, зашифровать отчет по Форме 4 через CryptoPro JCP и отправить в ФСС через их веб-сервис.
Тестовый шлюз ФСС - http://docs-test.fss.ru/
Тестовую ЭЦП получил здесь https://www.cryptopro.ru/certsrv/
Сертификат ФСС, которым шифрую подписанный отчет: fss2012Test.zip (2kb) загружен 1 раз(а).
Когда подписываю и шифрую через их программу https://fss.ru/ru/fund/download/55833/index.shtml, потом полученный файл Zashifrovannyjj cherez FSS.zip (4kb) загружен 1 раз(а). загружаю вручную через http://docs-test.fss.ru/home/upload, то выдает ошибку "-41. Нет доверия к издателю сертификата". То есть файл расшифровывается, а ЭЦП проверку не проходит. Вроде логично.
Теперь пытаюсь подписать и зашифровать файл через JCP следующим образом:
подпись файла тестовой ЭЦП
Код:
private byte[] signDoc(String keyPairAlgorithm, String keyProvider,
                         String signAlgorithm, String signProvider, String certName,
                         String signMethod, String digestMethod, File testDoc, String signDoc)
            throws Exception {

        // инициализация объекта чтения XML-документа
        final DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();

        // установка флага, определяющего игнорирование пробелов в содержимом элементов при обработке XML-документа
        dbf.setIgnoringElementContentWhitespace(true);

        // установка флага, определяющего преобразование узлов CDATA в текстовые узлы при обработке XML-документа
        dbf.setCoalescing(true);

        // установка флага, определяющего поддержку пространств имен при обработке XML-документа
        dbf.setNamespaceAware(true);

        // загрузка содержимого подписываемого документа на основе установленных флагами правил
        final DocumentBuilder documentBuilder = dbf.newDocumentBuilder();
        final Document doc;
        
        doc = documentBuilder.parse(getClass()
                .getResourceAsStream("/5205022875_2019_12.xml"));
        /* Добавление узла подписи <ds:Signature> в загруженный XML-документ */

        // инициализация объекта формирования ЭЦП в соответствии с алгоритмом ГОСТ Р 34.10-2001
        final XMLSignature sig = new XMLSignature(doc, "", signMethod);

        // получение корневого узла XML-документа
        final Element anElement = doc.getDocumentElement();

        // добавление в корневой узел XML-документа узла подписи
        anElement.appendChild(sig.getElement());

        /* Определение правил работы с XML-документом и добавление в узел подписи этих правил */

        // создание узла преобразований <ds:Transforms> обрабатываемого XML-документа
        final Transforms transforms = new Transforms(doc);

        // добавление в узел преобразований правил работы с документом
        transforms.addTransform(Transforms.TRANSFORM_ENVELOPED_SIGNATURE);
        transforms.addTransform(Transforms.TRANSFORM_C14N_WITH_COMMENTS);

        // добавление в узел подписи ссылок (узла <ds:Reference>), определяющих правила работы с
        // XML-документом (обрабатывается текущий документ с заданными в узле <ds:Transforms> правилами
        // и заданным алгоритмом хеширования)
        sig.addDocument("", transforms, digestMethod);

        /* Создание подписи всего содержимого XML-документа на основе закрытого ключа, заданных правил и алгоритмов */
        KeyStore keyStore = KeyStore.getInstance(JCP.HD_STORE_NAME, JCP.PROVIDER_NAME);
        keyStore.load(null, null);

        PrivateKey key = (PrivateKey)keyStore.getKey(containerName, containerPass == null ? null : containerPass.toCharArray());
        Certificate[] chain = keyStore.getCertificateChain(containerName);
        
        // создание внутри узла подписи узла <ds:KeyInfo> информации об открытом ключе на основе
        // сертификата
        sig.addKeyInfo((X509Certificate)chain[0]);

        // создание подписи XML-документа
        sig.sign(key);

        /* Сохранение подписанного XML-документа в файл */

        // определение потока, в который осуществляется запись подписанного XML-документа
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        // инициализация объекта копирования содержимого XML-документа в поток
        final TransformerFactory tf = TransformerFactory.newInstance();

        // создание объекта копирования содержимого XML-документа в поток
        final Transformer trans = tf.newTransformer();

        // копирование содержимого XML-документа в поток
        trans.transform(new DOMSource(doc), new StreamResult(baos));
        byte[] signedBytes = baos.toByteArray();
        baos.close();

        // шифрование ключом ФСС
        CertificateFactory cf = CertificateFactory.getInstance("X509");
        Certificate cer = cf.generateCertificate(getClass()
                .getResourceAsStream("/fss2012Test.cer"));
        return PKCS7EnvEphTransport.encryptPKCS7((X509Certificate) cer, signedBytes);
    }


шифрование подписанного файла ключом ФСС:
Код:
public static byte[] encryptPKCS7(X509Certificate recipientCert, byte[] pkcs7) throws Exception {

		final PublicKey recipientPublic = recipientCert.getPublicKey();

		// Генерирование симметричного ключа с параметрами
		// шифрования из контрольной панели.

		final KeyGenerator kg = KeyGenerator.getInstance(CMStools.SEC_KEY_ALG_NAME, CRYPT_PROVIDER_NAME);
		final ParamsInterface paramss = AlgIdSpec.getDefaultCryptParams();
		kg.init(CryptParamsSpec.getInstance(CryptParamsSpec.Rosstandart_TC26_Z));
		final SecretKey simm = kg.generateKey();
	    
		// Зашифрование текста на симметричном ключе.

		Cipher cipher = Cipher.getInstance(CIPHER_MODE, CRYPT_PROVIDER_NAME);
		cipher.init(Cipher.ENCRYPT_MODE, simm, (SecureRandom) null);

		final byte[] iv = cipher.getIV();
		final byte[] text = cipher.doFinal(pkcs7, 0, pkcs7.length);

		// Зашифрование симметричного ключа.

		final byte[] keyTransport = wrap(simm, recipientPublic);

		// Формирование CMS-сообщения.

		final ContentInfo all = new ContentInfo();
		all.contentType = new Asn1ObjectIdentifier(new OID(CMStools.STR_CMS_OID_SIGNED).value);

		final EnvelopedData cms = new EnvelopedData();

		all.content = cms;
		cms.version = new CMSVersion(0);

		cms.recipientInfos = new RecipientInfos(1);
		cms.recipientInfos.elements = new RecipientInfo[1];
		cms.recipientInfos.elements[0] = new RecipientInfo();

		final KeyTransRecipientInfo keytrans = new KeyTransRecipientInfo();
		keytrans.version = new CMSVersion(0);

		final Asn1BerEncodeBuffer ebuf = new Asn1BerEncodeBuffer();

		final AlgIdInterface algid = new AlgIdSpec(JCP.GOST_EL_KEY_OID);
		final AlgorithmIdentifier id = (AlgorithmIdentifier) algid.getDecoded();
		id.encode(ebuf);

		Asn1BerDecodeBuffer dbuf = new Asn1BerDecodeBuffer(ebuf.getMsgCopy());
		keytrans.keyEncryptionAlgorithm = new KeyEncryptionAlgorithmIdentifier();
		keytrans.keyEncryptionAlgorithm.decode(dbuf);

		ebuf.reset();
		dbuf.reset();
	    
		keytrans.rid = new RecipientIdentifier();
		final IssuerAndSerialNumber issuer = new IssuerAndSerialNumber();

		final X500Principal issuerName = recipientCert.getIssuerX500Principal();
		dbuf = new Asn1BerDecodeBuffer(issuerName.getEncoded());
	    
		issuer.issuer = new Name();
		final RDNSequence rnd = new RDNSequence();
		rnd.decode(dbuf);
	    
		issuer.issuer.set_rdnSequence(rnd);
		issuer.serialNumber = new CertificateSerialNumber(recipientCert.getSerialNumber());

		keytrans.rid.set_issuerAndSerialNumber(issuer);
		dbuf.reset();

		keytrans.encryptedKey = new EncryptedKey(keyTransport);
		ebuf.reset();
	    
		cms.recipientInfos.elements[0].set_ktri(keytrans);
		cms.encryptedContentInfo = new EncryptedContentInfo();

		final OID contentType = new OID(CMStools.STR_CMS_OID_DATA);
		cms.encryptedContentInfo.contentType = new ContentType(contentType.value);

		final Gost28147_89_Parameters params = new Gost28147_89_Parameters();
		params.iv = new Gost28147_89_IV(iv);

		params.encryptionParamSet = new Gost28147_89_ParamSet(paramss.getOID().value);
		cms.encryptedContentInfo.contentEncryptionAlgorithm = new ContentEncryptionAlgorithmIdentifier(
			_Gost28147_89_EncryptionSyntaxValues.id_Gost28147_89, params);

		cms.encryptedContentInfo.encryptedContent = new EncryptedContent(text);
		all.encode(ebuf);

		return ebuf.getMsgCopy();
	}


Потом полученный массив байтов отправляю через веб-сервис в ФСС, т.е. SOAP конверт сам не формирую:
Код:
GatewayService gatewayService = new GatewayService();
IGatewayService service = gatewayService.getBasicHttpBindingIGatewayService();
UploadResult result = service.sendFile(encryptedBytes, "E_" + fileName + ".ef4");

В результате возвращается ошибка "-10. He удалось расшифровать".
Т.е. до проверки ЭЦП даже не доходит.
Если таким же образом отправляю файл, полученный через программу ФСС, выдает ошибку -41 также, как и через веб-интерфейс.
Файл, который получается после шифрования в JCP: Posle shifrovanija v JCP.zip (5kb) загружен 1 раз(а).
Подскажите, как правильно зашифровать ключом ФСС подписанный файл?
Offline two_oceans  
#2 Оставлено : 26 марта 2020 г. 13:22:10(UTC)
two_oceans

Статус: Эксперт

Группы: Участники
Зарегистрирован: 05.03.2015(UTC)
Сообщений: 929
Российская Федерация
Откуда: Иркутская область

Сказал(а) «Спасибо»: 58 раз
Поблагодарили: 204 раз в 192 постах
Похоже на примерно верное до комментария о формировании cms. SOAP конечно не сами формируете, но на мой взгляд слишком много свойств устанавливаете для cms, так что это почти неотличимо от самостоятельного формирования.
Offline TerroNet  
#3 Оставлено : 26 марта 2020 г. 13:42:21(UTC)
TerroNet

Статус: Новичок

Группы: Участники
Зарегистрирован: 26.03.2020(UTC)
Сообщений: 5
Российская Федерация

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


Код шифрования взят из примера CMS_samples/PKCS7EnvEphTransport.java.
Если сравнить файлы после шифрования программой ФСС и через JCP, то в том, который сформирован через JCP, данных больше.
Попробовал убрать поочередно свойства для cms - всегда выходит NullPointerException. Они все обязательные.

Отредактировано пользователем 26 марта 2020 г. 16:12:01(UTC)  | Причина: Не указана

Offline TerroNet  
#4 Оставлено : 26 марта 2020 г. 17:09:36(UTC)
TerroNet

Статус: Новичок

Группы: Участники
Зарегистрирован: 26.03.2020(UTC)
Сообщений: 5
Российская Федерация

Вот еще в чем нашел различия:
слева - зашифровано через программу ФСС, справа - через JCP
2.JPG (322kb) загружен 3 раз(а).
1.JPG (224kb) загружен 10 раз(а).
Сертификат ФСС по ГОСТ 2012.
Я не совсем понимаю, получается, JCP шифрует по ГОСТ2001 сертификатом 2012? Или это симметричный ключ генерируется по ГОСТ2001?

Отредактировано пользователем 26 марта 2020 г. 17:12:24(UTC)  | Причина: Не указана

Offline two_oceans  
#5 Оставлено : 27 марта 2020 г. 5:42:36(UTC)
two_oceans

Статус: Эксперт

Группы: Участники
Зарегистрирован: 05.03.2015(UTC)
Сообщений: 929
Российская Федерация
Откуда: Иркутская область

Сказал(а) «Спасибо»: 58 раз
Поблагодарили: 204 раз в 192 постах
Что-то скрин ни разу не похож на формат SOAP, это чистый CMS. Собственно, с точки зрения обмена с ФСС на этом можно поставить точку (насколько помню ФСС принимает и подписанные и зашифрованные файлы в SOAP - про это тут есть темы практически для всех сред и языков), но для общих сведений можно и попробовать разобраться.

Слева идентификатор зашифрованного файла, а справа - подписанного. Что Вы хотите получить? Впрочем, для подписанного файл немного странный.

Насчет гост-2001 из скрина не до конца понятно, но похоже алгоритм из какого-то сертификата. Сгенерированный ключ явно ниже выделенного красным, у него параметры TK26 Z, есть соль и сам (зашифрованный?) ключ. Если Вами используется с Вашей стороны тестовый сертификат, то возможно он выдан в тестовом УЦ, который подписывает сертификаты ключом УЦ гост-2001 и виден именно этот алгоритм ключа УЦ.

Отредактировано пользователем 27 марта 2020 г. 5:43:16(UTC)  | Причина: Не указана

Offline TerroNet  
#6 Оставлено : 27 марта 2020 г. 8:54:10(UTC)
TerroNet

Статус: Новичок

Группы: Участники
Зарегистрирован: 26.03.2020(UTC)
Сообщений: 5
Российская Федерация

Я хочу, чтобы на выходе из JCP зашифрованный файл имел такой же вид, как зашифрованный с помощью программы ФСС. Потому что последний ФСС может расшифровать и доходит до проверки ЭЦП. А сейчас останавливается уже на расшифровании.
Поэтому мне надо привести файл на скрине справа к такому же виду, как слева.
Это да, чистый формат CMS. То есть я беру отчет в xml, подписываю его ЭЦП, потом шифрую сам файл. SOAP формируется в классах, сгенерированных из wsdl описания.
Я поправил параметры при шифровании:
Код:
public static byte[] encryptPKCS7(X509Certificate recipientCert, byte[] pkcs7) throws Exception {

		final PublicKey recipientPublic = recipientCert.getPublicKey();

		// Генерирование симметричного ключа с параметрами
		// шифрования из контрольной панели.

		final KeyGenerator kg = KeyGenerator.getInstance(CMStools.SEC_KEY_ALG_NAME, CRYPT_PROVIDER_NAME);
		kg.init(CryptParamsSpec.getInstance(CryptParamsSpec.Rosstandart_TC26_Z));
		final SecretKey simm = kg.generateKey();

		// Зашифрование текста на симметричном ключе.

		Cipher cipher = Cipher.getInstance(CIPHER_MODE, CRYPT_PROVIDER_NAME);
		cipher.init(Cipher.ENCRYPT_MODE, simm, (SecureRandom) null);

		final byte[] iv = cipher.getIV();
		final byte[] text = cipher.doFinal(pkcs7, 0, pkcs7.length);

		// Зашифрование симметричного ключа.

		final byte[] keyTransport = wrap(simm, recipientPublic);

		// Формирование CMS-сообщения.

		final ContentInfo all = new ContentInfo();
		all.contentType = new Asn1ObjectIdentifier(new OID(CMStools.STR_CMS_OID_ENVELOPED).value);

		final EnvelopedData cms = new EnvelopedData();

		all.content = cms;
		cms.version = new CMSVersion(0);

		cms.recipientInfos = new RecipientInfos(1);
		cms.recipientInfos.elements = new RecipientInfo[1];
		cms.recipientInfos.elements[0] = new RecipientInfo();

		final KeyTransRecipientInfo keytrans = new KeyTransRecipientInfo();
		keytrans.version = new CMSVersion(0);

		final Asn1BerEncodeBuffer ebuf = new Asn1BerEncodeBuffer();

		final AlgIdInterface algid = new AlgIdSpec(JCP.GOST_PARAMS_SIG_2012_256_KEY_OID);
		final AlgorithmIdentifier id = (AlgorithmIdentifier) algid.getDecoded();
		id.encode(ebuf);

		Asn1BerDecodeBuffer dbuf = new Asn1BerDecodeBuffer(ebuf.getMsgCopy());
		keytrans.keyEncryptionAlgorithm = new KeyEncryptionAlgorithmIdentifier();
		keytrans.keyEncryptionAlgorithm.decode(dbuf);

		ebuf.reset();
		dbuf.reset();

		keytrans.rid = new RecipientIdentifier();
		final IssuerAndSerialNumber issuer = new IssuerAndSerialNumber();

		final X500Principal issuerName = recipientCert.getIssuerX500Principal();
		dbuf = new Asn1BerDecodeBuffer(issuerName.getEncoded());

		issuer.issuer = new Name();
		final RDNSequence rnd = new RDNSequence();
		rnd.decode(dbuf);

		issuer.issuer.set_rdnSequence(rnd);
		issuer.serialNumber = new CertificateSerialNumber(recipientCert.getSerialNumber());

		keytrans.rid.set_issuerAndSerialNumber(issuer);
		dbuf.reset();

		keytrans.encryptedKey = new EncryptedKey(keyTransport);
		ebuf.reset();

		cms.recipientInfos.elements[0].set_ktri(keytrans);
		cms.encryptedContentInfo = new EncryptedContentInfo();

		final OID contentType = new OID(CMStools.STR_CMS_OID_DATA);
		cms.encryptedContentInfo.contentType = new ContentType(contentType.value);

		final Gost28147_89_Parameters params = new Gost28147_89_Parameters();
		params.iv = new Gost28147_89_IV(iv);

		params.encryptionParamSet = new Gost28147_89_ParamSet(CryptParamsSpec.getInstance(CryptParamsSpec.VERBAO).getOID().value);
		cms.encryptedContentInfo.contentEncryptionAlgorithm = new ContentEncryptionAlgorithmIdentifier(
			_Gost28147_89_EncryptionSyntaxValues.id_Gost28147_89, params);

		cms.encryptedContentInfo.encryptedContent = new EncryptedContent(text);
		all.encode(ebuf);
		return ebuf.getMsgCopy();
	}

private static byte[] wrap(SecretKey secretKey, Key recipientKey) throws Exception {

		// Генерация эфемерной пары.

		KeyPairGenerator kgp = KeyPairGenerator.getInstance(
			JCP.GOST_DH_2012_256_NAME,
			CRYPT_PROVIDER_NAME
		);

		// Устанавливаем нужные параметры, как у получателя.

        kgp.initialize(new X509PublicKeySpec(recipientKey.getEncoded()));

		// Генерируем эфемерную пару.

		KeyPair ephPair = kgp.generateKeyPair();

		PrivateKey privateKey = ephPair.getPrivate();
		PublicKey publicKey = ephPair.getPublic();

		byte[] syncro = new byte[8];
		SecureRandom random = SecureRandom.getInstance(JCP.CP_RANDOM, PROVIDER_NAME);
		random.nextBytes(syncro);

		IvParameterSpec iv = new IvParameterSpec(syncro);
		OID oid = CryptParamsSpec.OID_Gost28147_89_Rosstandart_TC26_Z_ParamSet;

		// Выработка ключа согласования.

		KeyAgreement ka = KeyAgreement.getInstance(privateKey.getAlgorithm(), CRYPT_PROVIDER_NAME);
		ka.init(privateKey, iv);

		ka.doPhase(recipientKey, true);
		Key dh = ka.generateSecret(CIPHER);

		//Зашифрование симметричного ключа на ключе согласования
		// отправителя.

		final Cipher cipher = Cipher.getInstance(CIPHER, CRYPT_PROVIDER_NAME);
		cipher.init(Cipher.WRAP_MODE, dh, (SecureRandom) null);

		final byte[] wrappedKey = cipher.wrap(secretKey);

		// Упаковка параметров и ключа.

		Gost28147_89_EncryptedKey encryptedKey = new Gost28147_89_EncryptedKey();
		Asn1BerDecodeBuffer decoder = new Asn1BerDecodeBuffer(wrappedKey);
		encryptedKey.decode(decoder);

		byte[] imita = encryptedKey.macKey.value;
		byte[] wrapperKeyBytes = encryptedKey.encryptedKey.value;

		// Кодирование открытого ключа в SubjectPublicKeyInfo.

		byte[] publicKeyBytes = publicKey.getEncoded();
		SubjectPublicKeyInfo publicKeyInfo = new SubjectPublicKeyInfo();

		decoder = new Asn1BerDecodeBuffer(publicKeyBytes);
		publicKeyInfo.decode(decoder);

		// Кодирование GostR3410_KeyTransport.

		GostR3410_KeyTransport keyTransport = new GostR3410_KeyTransport();
		Asn1BerEncodeBuffer encoder = new Asn1BerEncodeBuffer();

		keyTransport.sessionEncryptedKey = new Gost28147_89_EncryptedKey(wrapperKeyBytes, imita);
		keyTransport.transportParameters = new GostR3410_TransportParameters(
			new Gost28147_89_ParamSet(oid.value),
			publicKeyInfo,
			new Asn1OctetString(iv.getIV()));

		keyTransport.encode(encoder);
		return encoder.getMsgCopy();

	}

Сервис ФСС на это отвечает ошибкой "-19. Файл не зашифрован или не подписан".
В файлах осталось одно отличие:
3.JPG (323kb) загружен 3 раз(а).
Я правильно понимаю, что этот параметр выставляется автоматически вот здесь:
Код:

    final AlgIdInterface algid = new AlgIdSpec(JCP.GOST_PARAMS_SIG_2012_256_KEY_OID);
	final AlgorithmIdentifier id = (AlgorithmIdentifier) algid.getDecoded();
		id.encode(ebuf);

	Asn1BerDecodeBuffer dbuf = new Asn1BerDecodeBuffer(ebuf.getMsgCopy());
	keytrans.keyEncryptionAlgorithm = new KeyEncryptionAlgorithmIdentifier();
	keytrans.keyEncryptionAlgorithm.decode(dbuf);

?
По поводу того, что согласования не было - вы имеете ввиду следующий пример из CMS_samples\CMSSignAndEncrypt.java?
Код:
// выработка ключа согласования отправителем
    final KeyAgreement senderKeyAgree =
            KeyAgreement.getInstance(agreeAlgorithm);
    senderKeyAgree.init(senderKey, new IvParameterSpec(sv), null);
    senderKeyAgree.doPhase(responderPublic, true);
    final SecretKey alisaSecret =
            senderKeyAgree.generateSecret(CMStools.SEC_KEY_ALG_NAME);

Если туда передать мой закрытый ключ из ЭЦП и открытый ключ сертификата ФСС, то выходит ошибка "Несоответствие параметров ключа".
Offline TerroNet  
#7 Оставлено : 27 марта 2020 г. 11:30:23(UTC)
TerroNet

Статус: Новичок

Группы: Участники
Зарегистрирован: 26.03.2020(UTC)
Сообщений: 5
Российская Федерация

Несоответствие в параметрах 35.х и 36.х было из-за того, что мой сертификат был только для подписи.
И ошибка в KeyAgreement вылетала тоже поэтому.
Сгенерировал новый на https://www.cryptopro.ru/certsrv/ с параметрами "Ключ обмена и подписи", теперь файлы по ГОСТам идентичны:
4.JPG (323kb) загружен 1 раз(а).
Но ФСС отвечает ошибкой "-19. Файл не зашифрован или не подписан.". И зашифрованный файл по размеру меньше.
В чем может быть причина?
RSS Лента  Atom Лента
Пользователи, просматривающие эту тему
Быстрый переход  
Вы не можете создавать новые темы в этом форуме.
Вы не можете отвечать в этом форуме.
Вы не можете удалять Ваши сообщения в этом форуме.
Вы не можете редактировать Ваши сообщения в этом форуме.
Вы не можете создавать опросы в этом форуме.
Вы не можете голосовать в этом форуме.