Как подписать файл .jar с использованием схемы подписи XMSS (PQC) с JarSigner - PullRequest
0 голосов
/ 26 ноября 2018

Я пытаюсь использовать JarSigner для подписи файлов .jar с помощью XMSS Подписей.С помощью JCA / JCE Постквантовой криптографии Поставщик из BouncyCastle можно программно генерировать пары ключей XMSS и XMSSMT (пример) .Насколько мне известно, для использования JarSigner крайне важно предоставить KeyStore и псевдоним записи, которую вы хотите подписать своим кодом: jarsigner -keystore myKeystore -storetype JKS -storepass password -keypass password myjarfile.jar keystoreEntryAlias Запись KeyStore содержит Public / Secret KeyPair и ассоциированную X.Сертификат 509.

«Обычный» способ подписания файла Jar с помощью JarSigner заключается в следующем:

  1. Используйте keytool для генерации Public / Secret KeyPair и сертификата, а затем сохраняйте ихв хранилище ключей (keytool -genkeypair -alias keystoreEntryAlias -keyalg RSA -sigalg SHA256withRSA -dname CN=MyCompanyName -storetype JKS -keypass password -keystore mykeystore.jks -storepass password)
  2. Используйте JarSigner для подписи .jar, используя SecretKey, сохраненный в mykeystore.jks, с псевдонимом keysotreEntryAlias ​​(jarsigner -keystore mykeystore.jks -storetype jks -storepass passeword -keypass password myjarfile.jar keystoreEntryAlias)

, чтобыПодписать мой файл ключом XMSS У меня теоретически есть две возможности:

  1. Использовать BCPQC для программного создания пар ключей XMSS, сохранять их в mykeystore и использовать jarsigner -keystore mykeystore -alias xmss через CLI для подписи моего файла.
  2. Используйте BCPQC-провайдера с keytool, чтобы напрямую через CLI генерировать пары ключей XMSS и сохранять их в mykeystore (для keytool здесь потребуется еще 2 аргумента: -providerclass org.bouncycastle.pqc.jcajce.provider.BouncyCastlePQCProvider and -providerpath C:\Path\to\BouncyCastle\provider\bcprov-jdk15on-160.jar) Затем подпишите файл с записью хранилища ключей, используя JarSigner

К сожалению, я сталкиваюсь с проблемами с обеими возможностями:

  1. Поскольку я еще не нашел способчтобы использовать JarSigner без CLI, мне нужно поместить сгенерированные пары ключей XMSS в хранилище ключей, но для этого мне нужен сертификат, который включает в себя открытый ключ XMSS.BouncyCastle предоставляет X.509CertificateBuilder, который можно использовать для генерации необходимого сертификата, однако что-то кажется неправильным, если вы посмотрите на сгенерированный сертификат, особенно на алгоритм подписи и открытый ключ (исходный код внизу [CertificateBuilderExample], используяXMSSMT)
  2. Похоже, что keytool использует только перегрузку init (int) из BCPQCProvider, а XMSSKeyPairGeneratorSpi отклоняет это;ему нужен AlgorithmParameterSpec, в частности, XMSSParameterSpec, или вообще не инициализировать - и если я попробую последнее, он сгенерирует пару ключей, но результирующие ключи не могут быть закодированы и, следовательно, не могут быть сохранены в KeyStore.

Мой вопрос теперь будет:

Кто-нибудь знает способ подписать .jar файлы с использованием XMSS / XMSSMT с JarSigner и может предоставить более или менее подробное объяснение того, что он / она сделал правильно, чтоЯ поступил неправильно?Или, если я был неправ в том, что я упомянул выше, предоставьте исправление и укажите способ сделать это?


ОБНОВЛЕНИЕ 1 : Теперь я могу, используя другоеX509CertificateGenerator (исходный код внизу [X509CertificateGenerator]) и сведения, собранные из здесь , здесь и здесь , чтобы успешно подписать файлы JAR программно с RSA, предоставленным из BouncyCastle (исходный код для подписи внизу [RSA_JarSigner]).

Если я пытаюсь применить ту же схему, которая использовалась для подписи с RSA, для подписи с XMSS или XMSSMT, я запускаюв JarSignerException: Error in signer materials, вызванном NoSuchAlgorithmException: unrecognized algorithm name: XMSS (Исходный код для XMSS / XMSSMT внизу [SignXMSS] [SignXMSSMT].

Надеюсь, кто-то может помочь мне выяснить, в чем проблема!


ОБНОВЛЕНИЕ 2 : Кажется, проблема с сгенерированными сертификатами XMSS (или XMSSMT) связана с тем, что запись для алгоритма подписи (которая является SHA256withXMSS) передается как ASN1ObjectIdentifier, который пока неизвестен системе.Поэтому я провел некоторое исследование, чтобы выяснить, не случайно ли у BouncyCastle где-то где-то лежит Генератор сертификатов XMSS ... Бинго, здесь - это единица!

Я немного сократил код и подошелс 1 генератором и 1 верификатором (исходный код внизу [XMSSGen] [XMSSVer].Генератор дает мне те же сертификаты, которые я уже получил с другими методами (например, [X509CertificateGenerator]).Верификатор печально подсказывает мне эту ужасную ошибку: Exception in thread "main" java.security.spec.InvalidKeySpecException: java.lang.ClassCastException: org.bouncycastle.asn1.DLSequence cannot be cast to org.bouncycastle.asn1.ASN1Integer at org.bouncycastle.pqc.jcajce.provider.xmss.XMSSKeyFactorySpi.engineGeneratePrivate(Unknown Source) at java.base/java.security.KeyFactory.generatePrivate(KeyFactory.java:384) at BCXMSSCertificateVerifyer.verifyCertificate(BCXMSSCertificateVerifyer.java:32) at BCXMSSCertificateTester.main(BCXMSSCertificateTester.java:23)

Может быть, у кого-то есть идея, откуда она взялась / как ее исправить.Чтобы увидеть, может ли BC самостоятельно работать со своими собственными созданными сертификатами XMSS.

Редактировать: Одна проблема с верификатором: PrivateKey privKey = keyFactory.generatePrivate(new PKCS8EncodedKeySpec(keyBytes)); Зачем нам нужен закрытый ключ для проверкиСертификат?Не имеет смысла - просто удалите его, и оно будет работать ^^ (или, по крайней мере, должно)


ОБНОВЛЕНИЕ 3 : мне наконец-то удалось заставить верификатор работать правильно, поэтому яВ настоящее время я могу производить и проверять сертификаты XMSS.Я также могу хранить пары ключей XMSS с сертификатом, содержащим PublicKey, в хранилище ключей.Хотя я до сих пор не могу подписать какой-либо файл .jar с помощью.

Теперь приходит кое-что немного странное: я могу подписывать вещи с помощью пары ключей BC XMSS (конечно, я это сделал для этого)) если я сохраню их (или, по крайней мере, PrivateKey, так как он должен подписывать материал), а затем перезагрузлю его, чтобы снова подписать материал, он не будет работать.Ни в том случае, если я храню их в хранилище ключей и извлекаю их, ни в том случае, если я сохраняю ключ в виде закодированных байтов в файл и загружаю их снова.(Если вас интересует код, просто прокомментируйте, и я опубликую его здесь)

Я предлагаю следующее: поскольку схема подписи XMSS требует сохранения состояния (состояния уже использованного OTS),это состояние каким-то образом не может быть извлечено из PrivateKey, поскольку оно загружается снова (из хранилища ключей или файла не имеет значения) и, следовательно, не может использоваться для подписи чего-либо с помощью.


[CertificateBuilderExample]

import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x509.BasicConstraints;
import org.bouncycastle.asn1.x509.Extension;
import org.bouncycastle.asn1.x509.KeyUsage;
import org.bouncycastle.cert.X509v3CertificateBuilder;
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
import org.bouncycastle.pqc.jcajce.provider.BouncyCastlePQCProvider;
import org.bouncycastle.pqc.jcajce.spec.XMSSMTParameterSpec;

import java.io.FileOutputStream;
import java.io.IOException;
import java.math.BigInteger;
import java.security.*;
import java.security.cert.CertificateEncodingException;
import java.security.cert.X509Certificate;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.Random;


public class App {

    public static void main(String[] args) throws Exception {
        Security.addProvider(new BouncyCastlePQCProvider());

        SimpleDateFormat sdf = new SimpleDateFormat("dd-M-yyyy hh:mm:ss");
        String datefrom = "12-08-2018 10:20:56";
        String dateuntil = "12-05-2020 10:20:56";
        Date from = sdf.parse(datefrom);
        Date until = sdf.parse(dateuntil);

        // Create self signed Root CA certificate
        KeyPair rootCAKeyPair = generateKeyPair();
        X509v3CertificateBuilder builder = new JcaX509v3CertificateBuilder(
                new X500Name("CN=rootCA"), // issuer authority
                BigInteger.valueOf(new Random().nextInt()), //serial number of certificate
                from, // start of validity
                until, //end of certificate validity
                new X500Name("CN=rootCA"), // subject name of certificate
                rootCAKeyPair.getPublic()); // public key of certificate
        // key usage restrictions
        builder.addExtension(Extension.keyUsage, true, new KeyUsage(KeyUsage.keyCertSign));
        builder.addExtension(Extension.basicConstraints, false, new BasicConstraints(true));
        X509Certificate rootCA = new JcaX509CertificateConverter().getCertificate(builder
                .build(new JcaContentSignerBuilder("SHA256withXMSSMT").setProvider("BCPQC").
                        build(rootCAKeyPair.getPrivate()))); // private key of signing authority , here it is self signed
        saveToFile(rootCA, "rootCA.cer");

    }

    private static KeyPair generateKeyPair() throws NoSuchAlgorithmException, NoSuchProviderException, InvalidAlgorithmParameterException {
        KeyPairGenerator kpGen = KeyPairGenerator.getInstance("XMSSMT", "BCPQC");
        kpGen.initialize(new XMSSMTParameterSpec(20, 10, XMSSMTParameterSpec.SHA256), new SecureRandom());
        KeyPair kp = kpGen.generateKeyPair();
        System.out.print("Public key:" + Arrays.toString(kp.getPublic().getEncoded()));
        return kp;
    }

    private static void saveToFile(X509Certificate certificate, String filePath) throws IOException, CertificateEncodingException {
        FileOutputStream fileOutputStream = new FileOutputStream(filePath);
        fileOutputStream.write(certificate.getEncoded());
        fileOutputStream.flush();
        fileOutputStream.close();
    }

}

[X509CertificateGenerator]

public X509Certificate generateCertificate(String dn, KeyPair keyPair, int validity, String sigAlgName) throws GeneralSecurityException, IOException {
        PrivateKey privateKey = keyPair.getPrivate();

        X509CertInfo info = new X509CertInfo();

        Date from = new Date();
        Date to = new Date(from.getTime() + validity * 1000L * 24L * 60L * 60L);

        CertificateValidity interval = new CertificateValidity(from, to);
        BigInteger serialNumber = new BigInteger(64, new SecureRandom());
        X500Name owner = new X500Name(dn);
        AlgorithmId sigAlgId = new AlgorithmId(AlgorithmId.md5WithRSAEncryption_oid);

        info.set(X509CertInfo.VALIDITY, interval);
        info.set(X509CertInfo.SERIAL_NUMBER, new CertificateSerialNumber(serialNumber));
        info.set(X509CertInfo.SUBJECT, owner);
        info.set(X509CertInfo.ISSUER, owner);
        info.set(X509CertInfo.KEY, new CertificateX509Key(keyPair.getPublic()));
        info.set(X509CertInfo.VERSION, new CertificateVersion(CertificateVersion.V3));
        info.set(X509CertInfo.ALGORITHM_ID, new CertificateAlgorithmId(sigAlgId));

        // Sign the cert to identify the algorithm that's used.
        X509CertImpl certificate = new X509CertImpl(info);
        certificate.sign(privateKey, sigAlgName);

        // Update the algorith, and resign.
        sigAlgId = (AlgorithmId) certificate.get(X509CertImpl.SIG_ALG);
        info.set(CertificateAlgorithmId.NAME + "." + CertificateAlgorithmId.ALGORITHM, sigAlgId);
        certificate = new X509CertImpl(info);
        certificate.sign(privateKey, sigAlgName);

        return certificate;
    }

[RSA_JarSigner]

public class JarSignerTest {

    public static void main(String[] args) throws Exception{
        JarSignerTest jst = new JarSignerTest();
        jst.SignRSA();
    }

    public void SignRSA() throws Exception{
        Security.addProvider(new BouncyCastleProvider());
        File inputFile = new File("C:\\Path\\to\\jar\\toSign\\jarfile.jar"),
                outputfile = new File("C:\\Path\\to\\signedJar\\jarfile.jar");
        X509CertificateGen x509certgen = new X509CertificateGen();
        KeyPairGenerator kpGen = KeyPairGenerator.getInstance("RSA", "BC");
        kpGen.initialize(1024, new SecureRandom());
        KeyPair keyPair = kpGen.generateKeyPair();
        Certificate[] chain = {x509certgen.generateCertificate("cn=Unknown", keyPair, 356, "SHA256withRSA")};
        List<? extends Certificate> foo = Arrays.asList(chain);
        CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
        CertPath certPath = certificateFactory.generateCertPath(foo);
        JarSigner signer = new JarSigner.Builder(keyPair.getPrivate(), certPath)
                .digestAlgorithm("SHA-256")
                .signatureAlgorithm("SHA256withRSA")
                .build();
        try (ZipFile in = new ZipFile(inputFile);
             FileOutputStream out = new FileOutputStream(outputfile)){
            signer.sign(in, out);
        }
    }
}

[SignXMSS]

public void SignXMSS() throws Exception{
    Security.addProvider(new BouncyCastlePQCProvider());
    File inputFile = new File("C:\\Path\\to\\jar\\toSign\\jarfile.jar"),
            outputfile = new File("C:\\Path\\to\\signedJar\\jarfile.jar");
    X509CertificateGen x509certgen = new X509CertificateGen();
    KeyPairGenerator kpGen = KeyPairGenerator.getInstance("XMSS", "BCPQC");
    kpGen.initialize(new XMSSParameterSpec(10, XMSSParameterSpec.SHA256), new SecureRandom());
    KeyPair keyPair = kpGen.generateKeyPair();
    Certificate[] chain = {x509certgen.generateCertificate("cn=Unknown", keyPair, 356, "SHA256withXMSS")};
    List<? extends Certificate> foo = Arrays.asList(chain);
    CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
    CertPath certPath = certificateFactory.generateCertPath(foo);
    JarSigner signer = new JarSigner.Builder(keyPair.getPrivate(), certPath)
            .digestAlgorithm("SHA-256")
            .signatureAlgorithm("SHA256withXMSS", new BouncyCastlePQCProvider())
            .build();
    try (ZipFile in = new ZipFile(inputFile);
         FileOutputStream out = new FileOutputStream(outputfile)){
        signer.sign(in, out);
    }
}

[SignXMSSMT]

public void SignXMSSMT() throws Exception{
    Security.addProvider(new BouncyCastlePQCProvider());
    File inputFile = new File("C:\\Path\\to\\jar\\toSign\\jarfile.jar"),
            outputfile = new File("C:\\Path\\to\\signedJar\\jarfile.jar");
    X509CertificateGen x509certgen = new X509CertificateGen();
    KeyPairGenerator kpGen = KeyPairGenerator.getInstance("XMSSMT", "BCPQC");
    kpGen.initialize(new XMSSMTParameterSpec(20, 10, XMSSMTParameterSpec.SHA256), new SecureRandom());
    KeyPair keyPair = kpGen.generateKeyPair();
    Certificate[] chain = {x509certgen.generateCertificate("cn=Unknown", keyPair, 356, "SHA256withXMSSMT")};
    List<? extends Certificate> foo = Arrays.asList(chain);
    CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
    CertPath certPath = certificateFactory.generateCertPath(foo);
    JarSigner signer = new JarSigner.Builder(keyPair.getPrivate(), certPath)
            .digestAlgorithm("SHA-256")
            .signatureAlgorithm("SHA256withXMSSMT")
            .build();
    try (ZipFile in = new ZipFile(inputFile);
         FileOutputStream out = new FileOutputStream(outputfile)){
        signer.sign(in, out);
    }
}

[XMSSGen]

public class BCXMSSCertificateGenerator {

    public static X509Certificate generateCertificate(PrivateKey privKey, PublicKey pubKey, int duration, boolean isSelfSigned) throws Exception {
        Provider BC = new BouncyCastleProvider();

        //
        // distinguished name table.
        //
        X500NameBuilder builder = createStdBuilder();

        //
        // create the certificate - version 3
        //
        ContentSigner sigGen = new JcaContentSignerBuilder("SHA256withXMSS").setProvider("BCPQC").build(privKey);
        X509v3CertificateBuilder certGen = new JcaX509v3CertificateBuilder(new X500Name("cn=Java"), BigInteger.valueOf(1), new Date(System.currentTimeMillis() - 50000), new Date((long)(System.currentTimeMillis() + duration*8.65*Math.pow(10,7))), builder.build(), pubKey);

        X509Certificate cert = new JcaX509CertificateConverter().setProvider(BC).getCertificate(certGen.build(sigGen));

        cert.checkValidity(new Date());
        if (isSelfSigned) {
            //
            // check verifies in general
            //
            cert.verify(pubKey);

            //
            // check verifies with contained key
            //
            cert.verify(cert.getPublicKey());
        }


        ByteArrayInputStream bIn = new ByteArrayInputStream(cert.getEncoded());
        CertificateFactory fact = CertificateFactory.getInstance("X.509", BC);

        return (X509Certificate) fact.generateCertificate(bIn);
    }

    private static X500NameBuilder createStdBuilder() {
        X500NameBuilder builder = new X500NameBuilder(RFC4519Style.INSTANCE);

        builder.addRDN(RFC4519Style.c, "AU");
        builder.addRDN(RFC4519Style.o, "The Legion of the Bouncy Castle");
        builder.addRDN(RFC4519Style.l, "Melbourne");
        builder.addRDN(RFC4519Style.st, "Victoria");
        builder.addRDN(PKCSObjectIdentifiers.pkcs_9_at_emailAddress, "feedback-crypto@bouncycastle.org");

        return builder;
    }
}

[XMSSVer]

public class BCXMSSCertificateVerifyer {
    public static boolean verifyCertificate(byte[] certBytes, String sigAlgorithm, byte[] keyBytes) throws Exception{
        ByteArrayInputStream bIn;

        bIn = new ByteArrayInputStream(certBytes);

        CertificateFactory fact = CertificateFactory.getInstance("X.509", "BC");

        Certificate cert = fact.generateCertificate(bIn);

        PublicKey k = cert.getPublicKey();

        X509CertificateHolder certHldr = new X509CertificateHolder(certBytes);

        certHldr.isSignatureValid(new JcaContentVerifierProviderBuilder().setProvider("BCPQC").build(k));
        System.out.println(cert);

        KeyFactory keyFactory = KeyFactory.getInstance(k.getAlgorithm(), "BCPQC");

        PrivateKey privKey = keyFactory.generatePrivate(new PKCS8EncodedKeySpec(keyBytes));   // ERROR at this line
        /*_______________________________________________________________________________________________________________*\

        Exception in thread "main" java.security.spec.InvalidKeySpecException: java.lang.ClassCastException: org.bouncycastle.asn1.DLSequence cannot be cast to org.bouncycastle.asn1.ASN1Integer
            at org.bouncycastle.pqc.jcajce.provider.xmss.XMSSKeyFactorySpi.engineGeneratePrivate(Unknown Source)
            at java.base/java.security.KeyFactory.generatePrivate(KeyFactory.java:384)
            at BCXMSSCertificateVerifyer.verifyCertificate(BCXMSSCertificateVerifyer.java:32)
            at BCXMSSCertificateTester.main(BCXMSSCertificateTester.java:23)
        _________________________________________________________________________________________________________________
      /*                                                                                                                 */

        Signature signer = Signature.getInstance(sigAlgorithm, "BCPQC");

        signer.initSign(privKey);

        signer.update(certBytes);

        byte[] sig = signer.sign();

        signer.initVerify(cert);

        signer.update(certBytes);

        signer.verify(sig);
        return true;
    }
}

1 Ответ

0 голосов
/ 14 декабря 2018

TL; DR: Подписание файлов .jar с помощью схем подписи PQC, таких как XMSS, предоставляемых BC с использованием встроенного JarSigner *1004*, невозможно.Однако можно написать один JarSigner, который затем может.

Хотя Oracle предоставляет JCA / JCE готовую интеграцию для поставщика Crypto, такого как BC, эти зарегистрированные поставщики не используются из JarSigner ни для подписания, ни для проверки,Рассматривая JarSigner: поддерживаемые алгоритмы жестко запрограммированы и поэтому не могут быть расширены.

Единственный способ использовать JarSigner с поставщиком BC - полностью перестроить его.Однако это не рекомендуется.

Для тех, кто не боится перехватить исходный код jdk, ваш проект должен "переопределить" следующие классы из jdk:

  • AlgorithmId
  • Атрибуты
  • ContentInfo
  • EndEntityChecker
  • HttpTimestamper
  • InvalidJarIndexError
  • Jarentry
  • JarException
  • JarIndex
  • JarInputStream
  • JarOutputStream
  • JarSigner
  • JarVerifier
  • JavaUtilJarAccess
  • JavaUtilZipFileAccess
  • Main (jdk.jartool.sun.security.tools.jarsigner)
  • Манифест
  • МанифестEntryVerifier
  • PKCS7
  • PKIXValidator
  • Ресурсы
  • Resources_ja
  • Resources_zh_CN
  • SharedSecrets
  • SignatureFileVerifier
  • SignerInfo
  • SimpleValidator
  • TimestampedSigner
  • Timestamper
  • TimestampToken
  • TSResponse
  • Validator
  • VersionedStream

для копирования и вставкиисходный код в ваш проект и удалите импорт всех классов, которые вы взломали, так что ваши «пользовательские» классы будут взяты вместо официальных.

Примечание. Большинство упомянутых выше классов можно найти вМодуль java.base, хотя некоторые находятся в модуле jdk.jartool (например, сам JarSigner).

После клонирования необходимой части jdk, чтобы заставить JarSigner работать, вы можете, наконец, перейти к реализации вашего BCпоставщик и поддержка схем подписи PQC, особенно XMSSMT и SPHINCS, поскольку на данный момент кажется, что XMSS имеет некоторые серьезные проблемы .

Обратите внимание, что для проверка JAR-файла со знаком JarVerifier берет расширение файла блочного файла подписи и проверяет его .RSA, .DCA или .EC.Поэтому вам нужно будет добавить .XMSS .XMSSMT .SPHINCS256 и т. Д. Вы также должны указать классу PKCS7 в методе parseSignedData использовать генератор сертификатов BC.Есть также несколько других вещей, которые нужно изменить (например, AlgorithmID), которые я не помню, но после того, как вы выполнили все необходимые шаги, ваш JarSigner может подписывать и проверять файлы .jar, используя BC в дополнение к использованию "Обычные "RSA DCA и EC подписывают и проверяют.

К сожалению, я не смогу поделиться с вами окончательным исходным кодом, хотя я постараюсь ответить на как можно больше ваших вопросов.

...