"jarsigner -tsa ..." приводит к jar, который не может быть проверен на java 6, тогда как просто "jarsigner ..." подойдет - PullRequest
0 голосов
/ 03 июля 2018

Компания, в которой я работаю, распространяет часть своего программного обеспечения в виде подписанных файлов jar Java. Существует требование, что файлы jar должны быть «проверяемыми» на Java 6. Существует механизм, который считывает записи jar и дает сбой, когда подпись неверна или отсутствует для какой-либо записи. Код (упрощенный):

import java.io.*;
import java.security.cert.Certificate;
import java.util.Enumeration;
import java.util.jar.*;

public final class JarCheckSimple {

    public static void main(String[] args) throws IOException {
        check(new File("SuperAwesome.jar"));
    }

    private static void check(File file) throws IOException {
        JarFile jar = new JarFile(file);
        Manifest manifest = jar.getManifest();
        if (manifest == null) {
            throw new SecurityException("jar not signed " + jar);
        }

        byte[] buffer = new byte[8096];
        Enumeration entries = jar.entries();
        while (entries.hasMoreElements()) {
            JarEntry e = (JarEntry) entries.nextElement();
            if (!e.isDirectory() && !e.getName().startsWith("META-INF")) {
                InputStream is = jar.getInputStream(e);
                while (is.read(buffer, 0, buffer.length) != -1) { /* to be verified by VerifierStream */ }
                is.close();

                Certificate[] certs = e.getCertificates();
                if (certs == null || certs.length == 0) { /* Works on Java 8, fails on Java 6: (certs == null) is true there */
                    throw new SecurityException("unsigned entry " + e.getName());
                }
            }
        }
    }
}

Недавно наш отдел разработки релизов попытался изменить процесс подписи, чтобы включить поддержку меток времени подписи (эта вещь: https://docs.oracle.com/javase/7/docs/technotes/guides/security/time-of-signing.html),, и с тех пор проверка выше стала терпеть неудачу на Java 6 (только) - мой вопрос в конечном счете) будет поэтому, но сначала я должен дать некоторые подробности. Release Engineering утверждает, что они используют все те же сертификаты и все, единственное изменение - они добавили "-tsa http://sha256timestamp.ws.symantec.com/sha256/timestamp" к команде jarsigner, которую они имеют.

Я установил параметр -Djava.security.debug=jar для приведенного выше кода и получил эту разницу в выводе (для файлов JAR, подписанных с отметкой времени и без нее, проверено в Java 6 и Java 8):

Java 6, без отметки времени (CERTIFICATE.RSA здесь меньше 4 КБ):

jar: beginEntry META-INF/MANIFEST.MF
jar: done with meta!
jar: nothing to verify!
jar: beginEntry META-INF/MANIFEST.MF
jar: beginEntry META-INF/CERTIFICATE.SF
jar: processEntry: processing block
jar: beginEntry META-INF/CERTIFICATE.RSA
jar: processEntry: processing block
jar: Signature Block Certificate: [
[
  Version: V3
  Subject: CN=SuperCompany Ltd, OU=IT, O=SuperCompany Ltd, L=Rwanda, C=CH
  Signature Algorithm: SHA256withRSA, OID = 1.2.840.113549.1.1.11

  Key:  Sun RSA public key, 2048 bits
  modulus: ...
  public exponent: 65537
  Validity: [From: Tue May 29 02:00:00 CEST 2018,
               To: Sun Jun 28 01:59:59 CEST 2020]
  Issuer: CN=thawte SHA256 Code Signing CA, O="thawte, Inc.", C=US
  SerialNumber: [    ...]

Certificate Extensions: 8
...

Похоже, сертификат найден и проанализирован.

Java 8, с отметкой времени (CERTIFICATE.RSA здесь превышает 7 КБ):

jar: beginEntry META-INF/MANIFEST.MF
jar: done with meta!
jar: nothing to verify!
jar: beginEntry META-INF/MANIFEST.MF
jar: beginEntry META-INF/CERTIFICATE.SF
jar: processEntry: processing block
jar: beginEntry META-INF/CERTIFICATE.RSA
jar: processEntry: processing block
jar: Unsupported signer attribute: 1.2.840.113549.1.9.16.2.47
jar:
jar: Detected signature timestamp (#885515941755655847907684100465949331954643356622) generated on Tue Jun 26 14:11:42 CEST 2018
jar:
jar: Signature Block Certificate: [
[
  Version: V3
  Subject: CN=SuperCompany Ltd, OU=IT, O=SuperCompany Ltd, L=Rwanda, C=CH
  Signature Algorithm: SHA256withRSA, OID = 1.2.840.113549.1.1.11

  Key:  Sun RSA public key, 2048 bits
  modulus: ...
  public exponent: 65537
  Validity: [From: Tue May 29 02:00:00 CEST 2018,
               To: Sun Jun 28 01:59:59 CEST 2020]
  Issuer: CN=thawte SHA256 Code Signing CA, O="thawte, Inc.", C=US
  SerialNumber: [    ...]

Certificate Extensions: 8
...

Обратите внимание на «предупреждение» о «Неподдерживаемый атрибут подписавшего: 1.2.840.113549.1.9.16.2.47»; но все равно сертификат разбирается нормально.

Теперь проблема: Java 6 с меткой времени (CERTIFICATE.RSA здесь превышает 7 КБ):

jar: beginEntry META-INF/MANIFEST.MF
jar: beginEntry META-INF/CERTIFICATE.SF
jar: processEntry: processing block
jar: beginEntry META-INF/CERTIFICATE.RSA
jar: processEntry: processing block
jar: ignoring unsupported signer attribute: 1.2.840.113549.1.9.16.2.47
jar: processEntry caught: sun.security.pkcs.ParsingException: Unable to parse the encoded bytes
jar: done with meta!
jar: nothing to verify!
...

Итак, Java не может проанализировать какой-то блок, поэтому он обрабатывает весь jar как беззнаковый, поэтому наша проверка (см. Выше) завершается неудачно: JarEntry :: getCertificates () возвращает null. Для полноты: запуск "jarsigner -verify -verbose -certs SuperAwesome.jar" печатает все нормально для jarsigner из JDK 8 и печатает для каждой записи jar, что она просто "упоминалась" (не подписана) для JDK 6.

Отладка с некоторым приложенным исходным кодом OpenJDK, я обнаружил, что именно поэтому Java 6 терпит неудачу (sun.security.pkcs.PKCS9Attribute#PKCS9Attribute(sun.security.util.DerValue)):

    ...
    // get the oid
    ObjectIdentifier oid = val[0].getOID();
    index = indexOf(oid, PKCS9_OIDS, 1);
    if (index == -1) {
        if (debug != null) {
            debug.println("ignoring unsupported signer attribute: " + oid);
        }
        throw new ParsingException("Unsupported PKCS9 attribute: " + oid);
    }
    ...

Пока Java 8 проходит нормально:

    ...
    // get the oid
    oid = val[0].getOID();
    byte[] content = val[1].toByteArray();
    DerValue[] elems = new DerInputStream(content).getSet(1);

    index = indexOf(oid, PKCS9_OIDS, 1);
    if (index == -1) {
        if (debug != null) {
            debug.println("Unsupported signer attribute: " + oid);
        }
        value = content;
        return;
    }
    ...

Кроме того, вся логика в sun.security.pkcs.PKCS9Attribute готова для this.index == -1, в отличие от версии Java 6. Я даже могу изменить этот класс, чтобы он больше походил на JDK 8, и тогда тест проходит успешно; проблема в том, что я не могу контролировать JDK пользователя.

Похоже, что моя проблема могла быть решена, если бы в сертификате не было этого oid "1.2.840.113549.1.9.16.2.47" (единственная информация, которую я мог найти на нем: id-aa- signingCertificateV2 ). Должен признаться, я ничего не знаю обо всем процессе подписания (как в принципе, так и о том, как это делается нашей Release Engineering - кажется, они намеренно скрывают это), сертификатах и ​​т. Д.

Теперь мои вопросы:

  1. Прав ли я, что самый простой способ исправить это - избавиться от этого атрибута "1.2.840.113549.1.9.16.2.47"?
  2. Можно ли сказать, имея предоставленную мною информацию, что вводит ее в итоговый сертификат, который попадает в файл jar? (Это CERTIFICATE.RSA)
  3. Если нет, не могли бы вы намекнуть, какими могут быть мои дальнейшие шаги в попытке отследить или устранить проблему? Я застрял.

Я в Windows 7, использую обновление Oracle JDK 6 45 и обновление Oracle JDK 8 20. Заранее спасибо.

...