Проверки электронной подписи ЕСИА openId

187
15 февраля 2018, 14:48

Добрый день! Пишу интеграцию с есиа по opendid connect на java. Назрел вопрос по проверки подписи маркера доступа/идентификации. Вкратце что сказано в методических указания по поводу проверки подписи:

Маркер доступа/идентификации выглядит как HEADER.PAYLOAD.SIGNATURE в формате Base64Url. SIGNATURE представляет собой подпись в формате PKCS#7 detached signature в кодировке UTF-8 от значений первых двух частей маркера доступа (HEADER.PAYLOAD). Необходимо осуществить проверку данной электронной подписи с использованием сертификата ключа проверки электронной подписи ЕСИА.

Собственно сами вопросы:

Как сделать эту проверку подписи если есть только сама подпись строки(signature) и сама строка которую подписывали (header.payload)?

Откуда взять сертификата ключа проверки электронной подписи ЕСИА (Видел подобный вопрос на stackoverflow но ответов нет)?

Или может эта часть signature имеет в себе какие-то данные сертификата и ключа, и можно как то проверить? если да то как это сделать?

Answer 1

Я так понял что pkcs7 хранит в себе сертификат, и просто этим сертификатом проверяешь эту подпись(то есть себя ж), возможно я что-то я понял не так.. Вот код:

public boolean verify(byte[] data) {
    try {
        CMSSignedData cmsSignedData = new CMSSignedData(data);
        // получаем список сертификатов, которые содержатся в файле
        Store<X509CertificateHolder> certs = cmsSignedData.getCertificates();
        // p7s файл может содержать данные, которые были подписаны, то есть ваш pdf файл
        byte[] content = (byte[]) cmsSignedData.getSignedContent().getContent();
        // проходимся по списку подписей, обычно одна подпись, но стандарт поддерживает множество подписей
        for (SignerInformation si : cmsSignedData.getSignerInfos()) {
            // список всех атрибутов, подписанных вместе с файлом
            AttributeTable signedAttributes = si.getSignedAttributes();
            // атрибут с именем
            Attribute attrSurname = signedAttributes.get(new ASN1ObjectIdentifier("2.5.4.4"));
            // атрибут с именем 2
            Attribute attrGivenName = signedAttributes.get(new ASN1ObjectIdentifier("2.5.4.42"));
            // атрибут с датой подписи
            Attribute attrSigningTime = signedAttributes.get(new ASN1ObjectIdentifier("1.2.840.113549.1.9.5"));
            // выводим дату подписи
            System.out.println(attrSigningTime.getAttributeValues()[0].toString());
            // возможно некоторые атрибуты не были частью подписи, формат поддерживает и такие
            //AttributeTable unsignedAttributes = si.getUnsignedAttributes();
            // получаем идентификатор сертификата, которым подписаны данные
            SignerId signerId = si.getSID();
            // ищем сертификат в коллекции по идентификатору
            Collection certCollection = certs.getMatches(signerId);
            // выбираем первый сертификат, здесь не должно быть больше одного
            Iterator certIt = certCollection.iterator();
            X509CertificateHolder certHolder = (X509CertificateHolder) certIt.next();
            // создаем Verifier на основании сертификата, Verifier использует публичный ключ сертификата
            SignerInformationVerifier verifier = new JcaSimpleSignerInfoVerifierBuilder().build(certHolder);
            // проверяем подпись
            return si.verify(verifier);
        }
    } catch (CertificateException | OperatorCreationException | CMSException e) {
        logger.error(e);
    }
    return false;
}

Как я понял byte[] content может не содержать самих данных которые подписывает а может и содержать. Так же Attribute *** написаны чисто для проверки. И еще маркер состоит из header.payload.signature в формате base64url, поэтому signature надо декодировать примерно так:

 byte[] data = Base64.getUrlDecoder().decode(a);

И уже эту data надо отправлять в эту функцию public boolean verify(byte[] data). Возможно это решение не верное, я точно не знаю. Если это правильно/неправильно то напишите что все ок или подскажите как правильно.

READ ALSO
Как получить дату изменения каталога в Java

Как получить дату изменения каталога в Java

Как в Java получить FolderlastModified()? Вариант рекурсивного просмотра каталога в глубину и взятие даты последнего файла не подходит, так как каталог...

149
firebird embedded Java

firebird embedded Java

Как указать относительный путь для embedded Firebird? Вот так не работает: jdbc:firebirdsql://localhost/examfdb

165
Android Java Удалить все изображения из listView

Android Java Удалить все изображения из listView

Имеется listView, в который по клику на item добавляется imageПри втором клике на другой item изображение вновь подставляется, а с первого item'a изображение...

129