Как создать подпись PKCS # 7 из дайджеста? - PullRequest
0 голосов
/ 11 октября 2018

Я хочу подписать PDF, используя PDF-дайджест.Я создал хэш, используя следующий код,

byte[] buffer = new byte[1024];
int numOfBytesRead =0;
MessageDigest md = null;
md = MessageDigest.getInstance("SHA256","BC");
while((numOfBytesRead = content.read(buffer)) != -1 ){
     md.update(buffer, 0, numOfBytesRead);
}
byte[] digest = md.digest();

В конце мне нужно прикрепить эту подпись к моему pdf.Я нашел одно решение Создать подпись pkcs7 из дайджеста файла , но алгоритм, использованный в ссылке: SHA256 с RSA .Мой приватный ключ генерируется по алгоритму EC , и мне нужно использовать SHA256 с ECDSA . Можно просто подписать хеш с помощью SHA256 с ECDSA и прикрепить подпись к pdf с помощью PDFBox ExternalSigning Интерфейс.

Ответы [ 2 ]

0 голосов
/ 11 октября 2018

В некоторых ситуациях Adobe называет сертификат подписанта недействительным, хотя, по-видимому, он действителен;в частности, в данном случае:

  • Использование ключа или значения использования расширенного ключа не подходят
  • В подписи PAdES отсутствует атрибут подписи ESS-сертификата-v2

Использование ключа или Значения расширенного использования ключа не подходят

Это основано на информации, которую ОП впервые опубликовал как ответ

Я попробовал приведенный ниже код, но в PDF-файле написано, что подпись недействительна.Можете ли вы проверить код,

[...]

Я приложил PDF. PDF-файл создан

Действительно, Adobe Reader сообщает, что подпись недействительна, но посмотрите более внимательно:

Signature panel

В нем говорится «Документ не был изменен с момента применения этой подписи» - это означает, что подпись является математически правильной!

Проблема заключается в том, что «Подписывающее лицо»«сертификат недействителен», и причину этого можно увидеть при копании в диалоговых окнах свойств подписи:

Certificate Details

Таким образом, проблема заключается в том, что ваш подписантсертификат Недействительно для использования .

Это связано с выделенным атрибутом, в то время как использование ключа Цифровая подпись в порядке, «Использование расширенного ключа» 1.3.6.1.5.5.8.2.2 (OID для IPSEC Protection ) - это не *

В соответствии с Руководством по цифровой подписи Adobe для ИТ ,Adobe Acrobat принимает только

  • одно или несколько из следующих значений использования ключа (еслиy)

    • nonRepudiation
    • signTransaction (только 11.0.09)
    • digitalSignature (11.0.10 и более поздние версии)
  • и одно или несколько из следующих значений использования расширенного ключа (если есть)

    • emailProtection
    • codeSigning
    • anyExtendedKeyUsage
    • 1.2.840.113583.1.1.5 (траст Adobe Authentic Documents)

Из-за его IPSEC Protection расширенного значения использования ключа, следовательно, ваш сертификат не считаетсядействительно для подписи документов PDF.

Это, вероятно, проблема только в устаревших сигнатурах ISO 32000-1, возможно, не в сигнатурах PAdES.

В подписи PAdES отсутствует подпись ESSАтрибут -certificate-v2

Это основано на информации, впервые опубликованной ОП как ответ

У меня естьсоздано 2 файла PDF, PDFA подписан с использованием дайджеста содержимого PDF с кодом ниже,

[...]

PDFA

PDFB создается с тем же закрытым ключом исертификат, но вместо дайджеста я использую содержимое документа pdf напрямую, что дает мне действительный подписанный pdf, код PDFB ниже,

[...]

PDFB

Я думаю, что в подписывающей части PDFA чего-то не хватает, чего я не смог понять.

Здесь главное отличие состоит не в том, что вы сами явно вычисляете хеш или не позволяете вычислить его неявно, а главное в том, что подпись в PDFB включает атрибут ESS signature-certificate-v2, а сигнатура в PDFA - нет.Этот атрибут генерируется между

//PAdES - PDF Advanced Electronic Signature

и

//PAdES-end

Поскольку комментарии уже намекают, это необходимо только для подписей PAdES, а не для устаревших ISO 32000-1. Ответ, который OP взял из оригинального кода , касался создания устаревшей подписи ISO 32000-1 (и, следовательно, работает нормально), в то время как OP создает подпись PAdES.

Наличиеатрибут спецификации подписи ESS требуется спецификацией PAdES ETSI EN 319 142-1:

e) Генераторы должны использовать либо сертификат подписи, либо атрибут подписи-сертификата v2, в зависимости от хэш-функции, в соответствии с ETSI EN 319 122-1.

(ETSIEN 319 142-1, раздел 6.3 Базовые подписи PAdES)

Он ссылается на спецификацию CAdES ETSI EN 319 122-1, которая, в свою очередь, требует

h) Требование для SPO: ESS signing-certificate.Атрибут ESS signing-certificate должен использоваться, если используется алгоритм хеширования SHA-1.

i) Требование для SPO: ESS signing-certificate-v2.Атрибут ESS signing-certificate-v2 должен использоваться, когда используются алгоритмы хэширования, отличные от SHA-1.

(ETSI EN 319 122-1, раздел 6.3 Требования к компонентам и услугам)

0 голосов
/ 11 октября 2018

Я попробовал приведенный ниже код, но PDF-файл говорит, что подпись недействительна.Можете ли вы проверить код,

System.out.println("Hash Signing started");
    List<Certificate> certList = getFormatCertificate(strCertificate);
    PrivateKey privateKey;
    CMSSignedData s = null;
    Security.addProvider(new BouncyCastleProvider());
    byte[] signature = null;
    try {
        privateKey = loadPrivateKey(strPrivatekey);
        byte[] buffer = new byte[1024];
        int numOfBytesRead =0;
        MessageDigest md = null;
        md = MessageDigest.getInstance("SHA256","BC");
        while((numOfBytesRead = content.read(buffer)) != -1 ){
            md.update(buffer, 0, numOfBytesRead);
        }
        byte[] digest = md.digest();

        // Separate signature container creation step
        JcaCertStore certs = new JcaCertStore(certList);

        CMSSignedDataGenerator gen = new CMSSignedDataGenerator();

        Attribute attr = new Attribute(CMSAttributes.messageDigest,
                new DERSet(new DEROctetString(digest)));

        ASN1EncodableVector v = new ASN1EncodableVector();

        v.add(attr);

        SignerInfoGeneratorBuilder builder = new SignerInfoGeneratorBuilder(new BcDigestCalculatorProvider())
                .setSignedAttributeGenerator(new DefaultSignedAttributeTableGenerator(new AttributeTable(v)));

        //AlgorithmIdentifier sha256withECDSA = new DefaultSignatureAlgorithmIdentifierFinder().find(signerAlgorithm);

        CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
        InputStream in = new ByteArrayInputStream(certList.get(certList.size()-1).getEncoded());
        X509Certificate cert = (X509Certificate) certFactory.generateCertificate(in);

        gen.addSignerInfoGenerator(builder.build(
                new JcaContentSignerBuilder(signerAlgorithm).build(privateKey),
                new JcaX509CertificateHolder(cert)));
        //DErse
//      gen.addSignerInfoGenerator(builder.build(
//              new JcaContentSignerBuilder(sha256withRSA,
//                      new DefaultDigestAlgorithmIdentifierFinder().find(sha256withRSA))
//                              .build(PrivateKeyFactory.createKey(privateKey.getEncoded())),
//              new JcaX509CertificateHolder(cert)));

        gen.addCertificates(certs);

        s = gen.generate(new CMSAbsentContent(), false);
        System.out.println("Hash sign completed");
        signature = s.getEncoded();// ConstructEcdsaSigValue(s.getEncoded());
    } catch (GeneralSecurityException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
        System.out.println("GeneralSecurityException ::"+e.toString());
    } catch (OperatorCreationException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
        System.out.println("OperatorCreationException ::"+e.toString());
    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
        System.out.println("IOException ::"+e.toString());
    } catch (CMSException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
        System.out.println("CMSException ::"+e.toString());
    }finally{
        return signature;
    }

Я приложил PDF. PDF-файл создан

@ Mkl / Tilman: Я создал 2 PDF-файла, PDFA подписан с использованием дайджеста содержимого PDF с кодом ниже,

System.out.println("Hash Signing started");
    List<Certificate> certList = getFormatCertificate(strCertificate);
    PrivateKey privateKey;
    CMSSignedData s = null;
    Security.addProvider(new BouncyCastleProvider());
    byte[] signature = null;
    try {
        privateKey = loadPrivateKey(strPrivatekey);

        /*byte[] buffer = new byte[1024];
        int numOfBytesRead =0;
        MessageDigest md = null;
        //md = MessageDigest.getInstance("SHA256","BC");
        md = MessageDigest.getInstance("SHA-256");
        while((numOfBytesRead = content.read(buffer)) != -1 ){
            md.update(buffer, 0, numOfBytesRead);
        }
        byte[] digest = md.digest();*/
        MessageDigest md = MessageDigest.getInstance("SHA256", "BC");
        byte[] digest = md.digest(IOUtils.toByteArray(content));

        // Separate signature container creation step
        JcaCertStore certs = new JcaCertStore(certList);

        CMSSignedDataGenerator gen = new CMSSignedDataGenerator();

        Attribute attr = new Attribute(CMSAttributes.messageDigest,
                new DERSet(new DEROctetString(digest)));

        ASN1EncodableVector v = new ASN1EncodableVector();

        v.add(attr);

        SignerInfoGeneratorBuilder builder = new SignerInfoGeneratorBuilder(new BcDigestCalculatorProvider())
                .setSignedAttributeGenerator(new DefaultSignedAttributeTableGenerator(new AttributeTable(v)));

        //AlgorithmIdentifier sha256withECDSA = new DefaultSignatureAlgorithmIdentifierFinder().find(signerAlgorithm);

        CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
        InputStream in = new ByteArrayInputStream(certList.get(certList.size()-1).getEncoded());
        X509Certificate cert = (X509Certificate) certFactory.generateCertificate(in);

        gen.addSignerInfoGenerator(builder.build(
                new JcaContentSignerBuilder(signerAlgorithm).build(privateKey),
                new JcaX509CertificateHolder(cert)));
        //DErse
//      gen.addSignerInfoGenerator(builder.build(
//              new JcaContentSignerBuilder(sha256withRSA,
//                      new DefaultDigestAlgorithmIdentifierFinder().find(sha256withRSA))
//                              .build(PrivateKeyFactory.createKey(privateKey.getEncoded())),
//              new JcaX509CertificateHolder(cert)));

        gen.addCertificates(certs);

        s = gen.generate(new CMSAbsentContent(), false);
        System.out.println("Hash sign completed");
        signature = s.getEncoded();// ConstructEcdsaSigValue(s.getEncoded());
    } catch (GeneralSecurityException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
        System.out.println("GeneralSecurityException ::"+e.toString());
    } catch (OperatorCreationException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
        System.out.println("OperatorCreationException ::"+e.toString());
    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
        System.out.println("IOException ::"+e.toString());
    } catch (CMSException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
        System.out.println("CMSException ::"+e.toString());
    }finally{
        return signature;
    }

PDFA

PDFB создается с тем же секретным ключом и сертификатом, но вместо дайджеста я использую содержимое документа pdf напрямую, что дает мне действительный подписанный pdf, код PDFB ниже,

SignatureInterface signatureInterface = new SignatureInterface() {
                    @SuppressWarnings("rawtypes")
                    @Override
                    public byte[] sign(InputStream content) throws IOException {
                        try {
                            byte[] certificateByte = null;

                            Store certs = new JcaCertStore(certificates);

                            //PAdES - PDF Advanced Electronic Signature
                            //ESS - Enhanced Security Services
                            //ASN1 - Abstract Syntax Notation One-standard interface description language for defining data structures that can be serialized and deserialized in a cross-platform way
                            // Generating certificate hash
                            MessageDigest md = MessageDigest.getInstance("SHA-256");
                            md.update(certificates.get(certificates.size()-1).getEncoded());
                            byte[] certHash = md.digest();
                            // Generating certificate hash ends
                            System.out.println("Cert hash generated");
                            //ESSCertIDv2 identifies the certificate from the hash
                            ESSCertIDv2 essCert1 =
                                    new ESSCertIDv2(new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha256), certHash);
                            ESSCertIDv2[] essCert1Arr =
                                    {
                                            essCert1
                                    };
                            SigningCertificateV2 scv2 = new SigningCertificateV2(essCert1Arr);
                            Attribute certHAttribute =
                                    new Attribute(PKCSObjectIdentifiers.id_aa_signingCertificateV2, new DERSet(scv2));
                            ASN1EncodableVector v = new ASN1EncodableVector();
                            v.add(certHAttribute);

                            AttributeTable at = new AttributeTable(v);

                            //Create a standard attribute table from the passed in parameters - certhash
                            CMSAttributeTableGenerator attrGen = new DefaultSignedAttributeTableGenerator(at){
                                protected Hashtable createStandardAttributeTable(Map parameters)
                                {
                                    Hashtable result = super.createStandardAttributeTable(parameters);
                                    result.remove(CMSAttributes.signingTime);
                                    return result;
                                }
                            };
                            //PAdES-end
                            System.out.println("CMSAttributeTableGenerator generated");
                            SignerInfoGeneratorBuilder genBuild =
                                    new SignerInfoGeneratorBuilder(new BcDigestCalculatorProvider());
                            genBuild.setSignedAttributeGenerator(attrGen);
                            //Get single certificate
                            org.spongycastle.asn1.x509.Certificate certas1 = org.spongycastle.asn1.x509.Certificate.getInstance(ASN1Primitive.fromByteArray(certificates.get(certificates.size()-1).getEncoded()));
                            // ContentSigner interface creates SHA256withECDSA signer using PvtKey
                            ContentSigner sha1Signer = new JcaContentSignerBuilder(signerAlgorithm).build(privateKey);
                            //Creates SignerInfoGenerator using X.509 cert and ContentSigner
                            SignerInfoGenerator sifGen = genBuild.build(sha1Signer, new X509CertificateHolder(certas1));

                            // CMSSignedDataGenerator generates a pkcs7-signature message 
                            CMSSignedDataGenerator gen = new CMSSignedDataGenerator();

                            gen.addCertificates(certs);
                            gen.addSignerInfoGenerator(sifGen);
                            //Creates CMS message from PDF
                            CMSProcessableInputStream msg = new CMSProcessableInputStream(content);
                            //Generate a CMS Signed Data object which can be carrying a detached CMS signature
                            //msg - content to be signed
                            CMSSignedData signedData = gen.generate(msg, false);
                            System.out.println("CMSSignedData is done");
                            return signedData.getEncoded();
                        } catch (GeneralSecurityException e) {
                            throw new IOException(e);
                        } catch (CMSException e) {
                            throw new IOException(e);
                        } catch (OperatorCreationException e) {
                            throw new IOException(e);
                        }
                }

            };
            System.out.println("CMSSignedData is done2");
            PDDocument pdDocument =  PDDocument.load(inputfile);

            System.out.println("pdDocument loaded");
            pdDocument.addSignature(signature, signatureInterface);

PDFB

Я думаю, что в подписывающей части PDFA чего-то не хватает, чего я не смог понять.

...