Что именно проверяет signer.verify (signerInformationVerifier)? - PullRequest
2 голосов
/ 16 июня 2019

Что именно проверяет signer.verify (signerInformationVerifier) ​​в приведенном ниже коде?

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

Я попробовал javadoc, google, но не смог найти ответ.

SignerInformationStore signers = cmsSignedData.getSignerInfos();
// variable "it" iterates all signers
Iterator<?> it = signers.getSigners().iterator();
while (it.hasNext){
    SignerInformation signer = (SignerInformation) it.next();
    // get all 
    CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
    InputStream in = new  ByteArrayInputStream(certificateHolder.getEncoded());
    X509Certificate cert2 = (X509Certificate) certFactory.generateCertificate(in);

    SignerInformationVerifier signerInformationVerifier = new JcaSimpleSignerInfoVerifierBuilder().build(cert2);
    if (signer.verify(signerInformationVerifier)){
        System.out.println("PDF signature verification is correct");
    } else { System.out.println ("PDF signature verification failed");}

возвращает true для моих подписанных pdf документов. Я проверил с PDF-файлом, который имел доверенную подпись от доверенного CA 1 (зарегистрированного как такового в хранилище ключей) и один от CA 2, который не был зарегистрирован как доверенный в хранилище ключей.

Любое объяснение / помощь очень ценится.

1 Ответ

2 голосов
/ 16 июня 2019

SignerInformationVerifier, полученный из JcaSimpleSignerInfoVerifierBuilder...build(cert) (который в основном охватывает ContentVerifierProvider), управляется SignerInformation.verify следующим образом:

  • , так как была использована перегрузка build(cert), а не перегрузка build(publickey), , если присутствует атрибут аутентификации / подписи (обязательно должен быть) signatureTime, он проверяет, что значение signatureTime равно в течение срока действия сертификата

  • если присутствуют аутентифицированные / подписанные атрибуты, он проверяет их, включая проверку того, что дайджест (полученного) содержимого соответствует атрибуту дайджеста, и проверяет, что подпись проверяется как подпись подписанных атрибутов под publickey в сертификате (и алгоритмах, указанных в сообщении и SignerInfo)

  • в противном случае (без подписанных атрибутов) он проверяет, что подпись проверяется как подпись (полученного) контента под открытым ключом в сертификате (и алгоритмы, указанные в сообщении и SignerInfo)

Если вместо этого вы используете перегрузку JcaSimpleSignerInfoVerifierBuilder...build(cert.getPublicKey()), единственное отличие состоит в том, что он не сравнивает подписывающее время, если оно присутствует, с периодом действия сертификата.

Может проверять атрибуты подписавшего по атрибутам сертификата (только для субъекта? Или также серийный номер, эмитент?)

Это не так. Ваш код должен; см. org.bouncycastle.cms.CMSSignedDataParser javadoc для минимального примера, который использует Store сертификатов в сообщении, чтобы найти сертификат, соответствующий 'SID' (SignerIdentification) в SignerInfo. Этот «SID» обычно представляет собой пару «Эмитент» и «Последовательный», но в CMS это может быть SubjectKeyIdentifier. Стандартный формат PKCS7 / CMS не определяет имя субъекта для подписавшего (или SubjectAltName, в этом отношении), поэтому любой метод определения этого и использования его для поиска или проверки сертификата будет нестандартным. Однако вполне возможно, что, как только вы найдете сертификат другим способом, вы можете захотеть использовать его тему (или SAN) в качестве полезной информации при обработке подписанного содержимого.

Он вообще не проверяет, является ли сертификат действительным / доверенным. Пример отмечает это как проблему.

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

Если вы хотите действительно проверить сертификат, см. Руководство программиста Java PKI (или CertPathProgGuide в некоторых старых версиях) и javadoc для связанных классов, в основном java.security.cert.CertPathValidator.

В моем понимании подпись - это шифрование с закрытым ключом хеша (дайджеста сообщения) документа pdf. Причина, по-видимому, заключается в том, что такой способ подписи намного быстрее. Затем функция signer.verify (signerInformationVerifier), скорее всего, расшифровывает дайджест подписанного хэшированного сообщения, используя открытый ключ сертификата, и сравнивает его с дайджестом хэшированного сообщения (используя хэш-функцию, определенную в сертификате) - она ​​должна дать тот же результат.

В основном нет.Подписание не является шифрованием.Для алгоритма one , RSA, существует математическая симметрия между операциями в ядре шифрования и подписи, которая первоначально соблазнила людей описывать подпись как «шифрование с помощью частного ключа», но рассматривать ее таким образом было вскорекак оказалось, приводят к уязвимостям, и используемые в настоящее время схемы шифрования и подписи существенно различаются и не могут быть взаимозаменяемыми, хотя многие люди, которые просто копируют найденные в Интернете вещи, являются копиями вещей, найденных 10 лет назад и являющимися копиямивещи, найденные кем-то еще 20 лет назад, постоянно повторяют ошибкуJava crypto не помогает, потому что он был задан еще в 1990-х годах и включал неверное представление о том, что Cipher.init для RSA принимает аргументы для «шифрования» с помощью privatekey или «дешифрования» с помощью publickey и фактически молча выполняет подпись PKCS1 иливосстановление вместо шифрования и дешифрования.Для других алгоритмов, таких как DSA, ECDSA и (теперь) EdDSA, нет никакого сходства или какой-либо связи между подписанием и шифрованием.Это обсуждается в десятках Qs на crypto.SX и security.SX, если вам интересно.

Это верно , что большинство схем подписи сейчас, и все они поддерживаются PKCS7 / CMS,гибрид: «массивные» данные сначала надежно хэшируются (также называемые «переваренными»), а затем сигнатура вычисляется с использованием (небольшого) хеша / дайджеста вместо массивных данных.Если вы имели в виду скорость подписания и проверки, это зависит от ситуации;для RSA, как это реализовано в современную эпоху (примерно с 1980 года), подписывание намного медленнее, чем проверка;для DSA и ECDSA проверка немного медленнее, чем подписывание.

Для PKCS7 / CMS, как подразумевается в приведенном выше описании, подпись обычно не берется из «содержимого» (вашего PDF-файла).Вместо этого хэш содержимого, а также некоторые другие метаданные, включены в структуру данных, которая называется authenticatedAttributes в PKCS7 и переименована в signatureAttr [ibute] в CMS, а означает, что покрывается подписью publickey (включаяхэш).Но есть опция обратной совместимости, когда подписанные атрибуты не используются, а подпись открытого ключа равна , примененной к хешу содержимого.Точно то, что подписывающее лицо и верификатор делают в обоих случаях, описано в RFC 5652 и в разделах 5.4-5.6 , хотя вам может понадобиться весь раздел 5 для контекста.Также используемый алгоритм хэширования не определен в сертификате, но в сообщении PKCS7 / CMS;однако алгоритм и размер publickey (и, следовательно, сила) определены в сертификате , если используется (что является предпочтительным, но на самом деле не требуется), и хорошей практикой является выбор хеша, который достаточно разумно соответствуетсила publickey.

Для RSA верификатор «восстанавливает» (не дешифрует) хэш из подписи и сравнивает его с вновь вычисленным хешем полученных данных - либо содержимого, либо подписанных атрибутов, как указано выше.,Технически это делается в классе провайдера JCA для RSA, вызываемом (косвенно) классами BouncyCastle, так же как совершенно разные операции проверки для DSA и ECDSA выполняются в их классах провайдера, но результат тот женасколько вы обеспокоены.

...