Кодировать PKCS7 с помощью Bouncycastle

Мой код использует sun.security для создания блока подписи PKCS7 для подписания APK:

    private static void writeSignatureBlock(byte[] signatureBytes, X509Certificate publicKey, OutputStream out)
            throws Exception {
        SignerInfo signerInfo = new SignerInfo(new X500Name(publicKey.getIssuerX500Principal().getName()),
                publicKey.getSerialNumber(), AlgorithmId.get("SHA1"), AlgorithmId.get("RSA"), signatureBytes);

        PKCS7 pkcs7 = new PKCS7(new AlgorithmId[] { AlgorithmId.get("SHA1") },
                new ContentInfo(ContentInfo.DATA_OID, null), new X509Certificate[] { publicKey },
                new SignerInfo[] { signerInfo });

        ByteArrayOutputStream o = new ByteArrayOutputStream();
        pkcs7.encodeSignedData(o);
        byte[] expected = o.toByteArray();
    }

Я пытаюсь заменить это замком Bouncy, так как более новые версии Java не любят доступ к внутренним классам солнца.

Чтобы воспроизвести это поведение с помощью надувного замка, я попробовал этот код:

        List<java.security.cert.Certificate> certList = new ArrayList<java.security.cert.Certificate>();
        java.security.cert.Certificate certificate = publicKey;
        certList.add(certificate);
        JcaCertStore certs = new JcaCertStore(certList);
        CMSSignedDataGenerator gen = new CMSSignedDataGenerator();
        gen.addCertificates(certs);

        final byte[] signedHash = signatureBytes;

        ContentSigner nonSigner = new ContentSigner() {

            @Override
            public byte[] getSignature() {
                return signedHash;
            }

            @Override
            public OutputStream getOutputStream() {
                return new ByteArrayOutputStream();
            }

            @Override
            public AlgorithmIdentifier getAlgorithmIdentifier() {
                return new DefaultSignatureAlgorithmIdentifierFinder().find("SHA1WithRSA");
            }

        };

        org.bouncycastle.asn1.x509.Certificate cert = org.bouncycastle.asn1.x509.Certificate
                .getInstance(ASN1Primitive.fromByteArray(certificate.getEncoded()));
        JcaSignerInfoGeneratorBuilder sigb = new JcaSignerInfoGeneratorBuilder(
                new JcaDigestCalculatorProviderBuilder().build());
        sigb.setDirectSignature(true);
        gen.addSignerInfoGenerator(sigb.build(nonSigner, new X509CertificateHolder(cert)));
        CMSProcessableByteArray msg = new CMSProcessableByteArray(new byte[0]);

        CMSSignedData signedData = gen.generate(msg, false);
        byte[] bouncyCastle = signedData.getEncoded();

Сгенерированные выходы имеют разную длину и сильно отличаются:

Expected length: 1489
BouncyCastle length: 1491```

Why do these outputs differ in length (if only the content would be different I'd assume I supplied the data in a wrong format)?

Большое спасибо, вы правы во всех отношениях :) Если хотите, вы также можете опубликовать это как ответ, чтобы я мог его принять, тем временем я опубликовал свой собственный на основе вашего комментария.

user1120897 17.07.2019 08:52
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
0
1
555
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Единственное отличие состоит в том, что Bouncycastle использует несколько иную кодировку определенных строк в теме сертификата, чем классы sun.*. Bouncycastle больше соответствует RFC 3280, но RFC5280 несколько смягчил требования. Любая кодировка должна быть в порядке.

Альтернативой BouncyCastle является библиотека Osdt CERT от Oracle, доступная по адресу Maven-репозиторий. Gradle-зависимость:

// https://mvnrepository.com/artifact/com.oracle.ojdbc/osdt_core
implementation "com.oracle.ojdbc:osdt_cert:19.3.0.0" 

Библиотека Oracle IMO намного проще в использовании, чем Bouncy Castle. Создание формата PKCS7 DER из существующего формата X509 — это вопрос 3 строк кода:

import oracle.security.crypto.cert.PKCS7;
import oracle.security.crypto.cert.X509;

        byte[] certDER = <retrieve the X.509 certificate as DER encoded byte array>
        X509 x509 = new X509(certAsDER);
        PKCS7 pkcs7 = new PKCS7(x509);
        byte pkcs7DER = pkcs7.getEncoded();

Другие вопросы по теме