Ключевое слово в защите информации
КЛЮЧЕВОЕ СЛОВО
в защите информации
Получить ГОСТ TLS-сертификат для домена (SSL-сертификат)
Добро пожаловать, Гость! Чтобы использовать все возможности Вход или Регистрация.

Уведомление

Icon
Error

2 Страницы12>
Опции
К последнему сообщению К первому непрочитанному
Offline Nitratius  
#1 Оставлено : 28 февраля 2025 г. 20:34:41(UTC)
Nitratius

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

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

Добрый день! Есть сервис подписания файлов. При подписании формируется файл sig. При проверке подписываемого файла на сайтах КриптоПро либо Госуслугах выдает "Не удалось проверить подпись формата CMS. Ошибка:
[Неправильное значение хеша]. Код: [0x80091007]."

Вот сам сервис:
Код:

public class SignatureServiceDisconnected implements SignatureService {

    public static final String STR_CMS_OID_SIGNED = "1.2.840.113549.1.7.2";
    public static final String STR_CMS_OID_DATA = "1.2.840.113549.1.7.1";
    public static final String STR_CMS_OID_CONT_TYP_ATTR = "1.2.840.113549.1.9.3";
    public static final String STR_CMS_OID_DIGEST_ATTR = "1.2.840.113549.1.9.4";
    public static final String STR_CMS_OID_SIGN_TYM_ATTR = "1.2.840.113549.1.9.5";
    private final KeyStore keyStore;

    @Override
    public String signature(Container<?> container, SigningDocumentRequest signingDocument) throws Exception {
        JCPPrivateKeyEntry entry = (JCPPrivateKeyEntry) (Object) keyStore.getEntry(
                container.getAlias(),
                new KeyStore.PasswordProtection(container.getPassword().toCharArray())
        );
        PrivateKey privateKey = entry.getPrivateKey();
        Certificate certificate = entry.getCertificate();

        byte[] data = Base64.getDecoder().decode(signingDocument.document());

        byte[] hashCMSEx = createHashCMSEx(data, new PrivateKey[]{privateKey}, new Certificate[]{certificate});

        return Base64.getEncoder().encodeToString(hashCMSEx);
    }
    
    private static byte[] createHashCMSEx(byte[] data, PrivateKey[] keys, Certificate[] certs) throws Exception {
        //create hashCMS
        final ContentInfo all = new ContentInfo();
        all.contentType = new Asn1ObjectIdentifier(new OID(STR_CMS_OID_SIGNED).value);

        final SignedData cms = new SignedData();
        all.content = cms;
        cms.version = new CMSVersion(1);

        // digest
        cms.digestAlgorithms = new DigestAlgorithmIdentifiers(1);
        final DigestAlgorithmIdentifier a = new DigestAlgorithmIdentifier(new OID(JCP.GOST_DIGEST_OID).value);
        a.parameters = new Asn1Null();
        cms.digestAlgorithms.elements[0] = a;

        // Нельзя сделать подпись совмещенной, если нет данных, а
        // есть только хеш с них.
        cms.encapContentInfo = new EncapsulatedContentInfo(new Asn1ObjectIdentifier(new OID(STR_CMS_OID_DATA).value), null);

        // certificates
        final int nCerts = certs.length;
        cms.certificates = new CertificateSet(nCerts);
        cms.certificates.elements = new CertificateChoices[nCerts];

        for (int i = 0; i < cms.certificates.elements.length; i++) {
            final ru.CryptoPro.JCP.ASN.PKIX1Explicit88.Certificate certificate = new ru.CryptoPro.JCP.ASN.PKIX1Explicit88.Certificate();
            final Asn1BerDecodeBuffer decodeBuffer = new Asn1BerDecodeBuffer(certs[i].getEncoded());
            certificate.decode(decodeBuffer);
            cms.certificates.elements[i] = new CertificateChoices();
            cms.certificates.elements[i].set_certificate(certificate);
        }

        // Signature.getInstance
        final Signature signature = Signature.getInstance(JCP.GOST_SIGN_2012_256_NAME, JCP.PROVIDER_NAME);
        byte[] sign;

        // signer infos
        final int nsign = keys.length;
        cms.signerInfos = new SignerInfos(nsign);
        for (int i = 0; i < cms.signerInfos.elements.length; i++) {
            cms.signerInfos.elements[i] = new SignerInfo();
            cms.signerInfos.elements[i].version = new CMSVersion(1);
            cms.signerInfos.elements[i].sid = new SignerIdentifier();
            final byte[] encodedName = ((X509Certificate) certs[i]).getIssuerX500Principal().getEncoded();
            final Asn1BerDecodeBuffer nameBuf = new Asn1BerDecodeBuffer(encodedName);
            final Name name = new Name();
            name.decode(nameBuf);
            final CertificateSerialNumber num = new CertificateSerialNumber(((X509Certificate) certs[i]).getSerialNumber());
            cms.signerInfos.elements[i].sid.set_issuerAndSerialNumber(new IssuerAndSerialNumber(name, num));
            cms.signerInfos.elements[i].digestAlgorithm = new DigestAlgorithmIdentifier(new OID(JCP.GOST_DIGEST_OID).value);
            cms.signerInfos.elements[i].digestAlgorithm.parameters = new Asn1Null();
            cms.signerInfos.elements[i].signatureAlgorithm = new SignatureAlgorithmIdentifier(new OID(JCP.GOST_EL_KEY_OID).value);
            cms.signerInfos.elements[i].signatureAlgorithm.parameters = new Asn1Null();

            //signedAttributes
            final int kmax = 3;
            cms.signerInfos.elements[i].signedAttrs = new SignedAttributes(kmax);

            //-contentType
            int k = 0;
            cms.signerInfos.elements[i].signedAttrs.elements[k] = new Attribute(
                    new OID(STR_CMS_OID_CONT_TYP_ATTR).value,
                    new Attribute_values(1)
            );

            final Asn1Type conttype = new Asn1ObjectIdentifier(new OID(STR_CMS_OID_DATA).value);

            cms.signerInfos.elements[i].signedAttrs.elements[k].values.elements[0] = conttype;

            //-Time
            k += 1;
            cms.signerInfos.elements[i].signedAttrs.elements[k] = new Attribute(
                    new OID(STR_CMS_OID_SIGN_TYM_ATTR).value,
                    new Attribute_values(1)
            );

            final Time time = new Time();

            final Asn1UTCTime utcTime = new Asn1UTCTime();
            //текущая дата с календаря
            utcTime.setTime(Calendar.getInstance());
            time.set_utcTime(utcTime);

            cms.signerInfos.elements[i].signedAttrs.elements[k].values.elements[0] = time.getElement();

            //-message digest
            k += 1;
            cms.signerInfos.elements[i].signedAttrs.elements[k] = new Attribute(
                    new OID(STR_CMS_OID_DIGEST_ATTR).value,
                    new Attribute_values(1)
            );

            final byte[] messageDigestBlob;

            // Если вместо данных у нас хеш, то сразу его передаем, ничего не вычисляем.
            messageDigestBlob = digestm(data, JCP.GOST_DIGEST_2012_256_NAME, JCP.PROVIDER_NAME);

            final Asn1Type messageDigest = new Asn1OctetString(messageDigestBlob);
            cms.signerInfos.elements[i].signedAttrs.elements[k].values.elements[0] = messageDigest;

            // Добавление signingCertificateV2 в подписанные аттрибуты, чтобы подпись
            // стала похожа на CAdES-BES.

            //signature
            Asn1BerEncodeBuffer encBufSignedAttr = new Asn1BerEncodeBuffer();
            cms.signerInfos.elements[i].signedAttrs.encode(encBufSignedAttr);
            final byte[] hsign = encBufSignedAttr.getMsgCopy();
            signature.initSign(keys[i]);
            signature.update(hsign);
            sign = signature.sign();

            cms.signerInfos.elements[i].signature = new SignatureValue(sign);
        }
        // encode
        final Asn1BerEncodeBuffer asnBuf = new Asn1BerEncodeBuffer();
        all.encode(asnBuf, true);
        return asnBuf.getMsgCopy();
    }

    public static byte[] digestm(byte[] bytes, String digestAlgorithmName, String providerName) throws Exception {
        final ByteArrayInputStream stream = new ByteArrayInputStream(bytes);
        final java.security.MessageDigest digest = providerName != null
                ? java.security.MessageDigest.getInstance(digestAlgorithmName, providerName)
                : MessageDigest.getInstance(digestAlgorithmName);

        final DigestInputStream digestStream = new DigestInputStream(stream, digest);
        while (digestStream.available() != 0) {
            digestStream.read();
        }
        return digest.digest();
    }
}
Offline Андрей *  
#2 Оставлено : 28 февраля 2025 г. 21:06:54(UTC)
Андрей *

Статус: Сотрудник

Группы: Участники
Зарегистрирован: 26.07.2011(UTC)
Сообщений: 13,623
Мужчина
Российская Федерация

Сказал «Спасибо»: 567 раз
Поблагодарили: 2283 раз в 1784 постах
Здравствуйте.

На входе что в signingDocument.document()?
Техническую поддержку оказываем тут
Наша база знаний
Offline Nitratius  
#3 Оставлено : 28 февраля 2025 г. 21:13:10(UTC)
Nitratius

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

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

Автор: Андрей * Перейти к цитате
Здравствуйте.

На входе что в signingDocument.document()?


String с файлом в Base64. Файл формата pdf.
Offline Андрей *  
#4 Оставлено : 28 февраля 2025 г. 21:20:17(UTC)
Андрей *

Статус: Сотрудник

Группы: Участники
Зарегистрирован: 26.07.2011(UTC)
Сообщений: 13,623
Мужчина
Российская Федерация

Сказал «Спасибо»: 567 раз
Поблагодарили: 2283 раз в 1784 постах
Автор: Nitratius Перейти к цитате
Автор: Андрей * Перейти к цитате
Здравствуйте.

На входе что в signingDocument.document()?


String с файлом в Base64. Файл формата pdf.



взять любой файл (бинарный, можно pdf)
закодировать (как на входе при подписании)
вызвать только digestm для data записать в файл.hash

приложить тестовый файл и файл с хешем
или самостоятельно проверить внешними утилитами хеш от файла
Техническую поддержку оказываем тут
Наша база знаний
Offline Андрей *  
#5 Оставлено: : 28 февраля 2025 г. 21:23:08(UTC)
Андрей *

Статус: Сотрудник

Группы: Участники
Зарегистрирован: 26.07.2011(UTC)
Сообщений: 13,623
Мужчина
Российская Федерация

Сказал «Спасибо»: 567 раз
Поблагодарили: 2283 раз в 1784 постах
или ещё проще - вот файл testfile.pdf (3kb) загружен 1 раз(а).

жду от него base64 (что попадает в signingDocument.document())
Техническую поддержку оказываем тут
Наша база знаний
Offline Андрей *  
#6 Оставлено : 28 февраля 2025 г. 21:25:39(UTC)
Андрей *

Статус: Сотрудник

Группы: Участники
Зарегистрирован: 26.07.2011(UTC)
Сообщений: 13,623
Мужчина
Российская Федерация

Сказал «Спасибо»: 567 раз
Поблагодарили: 2283 раз в 1784 постах
хеш для файла выше такой должен быть в hex:
A7B27353CEA6BFB2493CEAF0F55C50E78B5BC1CFA78F5C496FD4613A8478A4EE
Техническую поддержку оказываем тут
Наша база знаний
Offline Nitratius  
#7 Оставлено : 28 февраля 2025 г. 21:33:14(UTC)
Nitratius

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

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

Base64.txt (4kb) загружен 1 раз(а).

Отредактировано пользователем 28 февраля 2025 г. 21:34:31(UTC)  | Причина: Не указана

Offline Андрей *  
#8 Оставлено : 28 февраля 2025 г. 21:36:44(UTC)
Андрей *

Статус: Сотрудник

Группы: Участники
Зарегистрирован: 26.07.2011(UTC)
Сообщений: 13,623
Мужчина
Российская Федерация

Сказал «Спасибо»: 567 раз
Поблагодарили: 2283 раз в 1784 постах
Декодировал. Хеш совпал, хорошо.


Техническую поддержку оказываем тут
Наша база знаний
Offline Nitratius  
#9 Оставлено : 28 февраля 2025 г. 22:01:54(UTC)
Nitratius

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

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

А есть какой-нибудь пример java кода подписания файла с помощью cryptopro jcp ?
Offline Евгений Афанасьев  
#10 Оставлено : 2 марта 2025 г. 22:58:44(UTC)
Евгений Афанасьев

Статус: Сотрудник

Группы: Участники
Зарегистрирован: 06.12.2008(UTC)
Сообщений: 4,014
Российская Федерация
Откуда: Крипто-Про

Сказал(а) «Спасибо»: 21 раз
Поблагодарили: 721 раз в 680 постах
Здравствуйте.
В примере фигурируют JCP.GOST_DIGEST_OID, JCP.GOST_EL_KEY_OID, что неверно, т.к. подпись создается ГОСТ 2012 (256).
thanks 1 пользователь поблагодарил Евгений Афанасьев за этот пост.
Андрей * оставлено 02.03.2025(UTC)
RSS Лента  Atom Лента
Пользователи, просматривающие эту тему
Guest
2 Страницы12>
Быстрый переход  
Вы не можете создавать новые темы в этом форуме.
Вы не можете отвечать в этом форуме.
Вы не можете удалять Ваши сообщения в этом форуме.
Вы не можете редактировать Ваши сообщения в этом форуме.
Вы не можете создавать опросы в этом форуме.
Вы не можете голосовать в этом форуме.