当前位置: 首页 > 面试题库 >

iText7 LtvVerification.addVerification未启用LTV

张兴旺
2023-03-14
问题内容

我们正在尝试使签名签名LTV启用。我正在使用以下代码添加验证。当signature.isTsp()为false时,PDF会说Signature is not LTV enabled,尽管在其他情况下(signature.isTsp()为true),它将显示为有效。当我们打开PDF并尝试通过右键单击签名手动添加验证信息时,它将启用LTV,而不会出现任何问题。不知道我们在这里缺少什么。任何输入都将非常有帮助。

// Adds LTV-enabled information to the PDF document.
private ByteArrayOutputStream addLtv(final IOcspClient ocspClient,
                                     final ByteArrayOutputStream docStream)
        throws IOException, GeneralSecurityException {
    final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
    final InputStream signedStream = new ByteArrayInputStream(docStream.toByteArray());
    final PdfReader reader = new PdfReader(signedStream);
    final PdfDocument document =
            new PdfDocument(reader, new PdfWriter(outputStream), new StampingProperties().useAppendMode());
    final LtvVerification verification = new LtvVerification(document);
    final SignatureUtil signatureUtil = new SignatureUtil(document);

    final List<String> signatureNames = signatureUtil.getSignatureNames();
    final String sigName = signatureNames.get(signatureNames.size() - 1);
    final PdfPKCS7 signature = signatureUtil.verifySignature(sigName);

    final CrlClientOnline crl = new CrlClientOnline();
    if (!signature.isTsp()) {
        for (final String name: signatureNames) {
            addVerificationInfo(ocspClient, verification, crl, name);
        }
    } else {
        addVerificationInfo(ocspClient, verification, crl, sigName);
    }

    document.close();

    return outputStream;
}

private void addVerificationInfo(final IOcspClient ocspClient, final LtvVerification verification,
                                 final CrlClientOnline crl,
                                 final String name) throws IOException, GeneralSecurityException {
    verification.addVerification(
            name, ocspClient, crl,
            LtvVerification.CertificateOption.WHOLE_CHAIN,
            LtvVerification.Level.OCSP_CRL,
            LtvVerification.CertificateInclusion.NO);
}

问题答案:

您的代码不总是启用LTV的PDF的主要原因是,它没有添加与OCSP响应签名相关的验证信息。

它也不会添加CRL签名的验证信息。
但是,由于CRL通常由签署者证书的发行者证书签名,并且由于已经在主签名的上下文中添加了该发行者证书的验证信息,因此启用LTV通常不会因缺少CRL签名验证信息而失败。
。因此,如果您只能使用CRL,那么您的代码确实确实已经在启用LTV的PDF中了。

在此答案的上下文中(特别是其“使用自己的实用程序类的方法”一节),我AdobeLtvEnabling为iText
5 创建了一个实用程序类,以允许启用LTV的PDF,主要使用iText
5本身中的点点滴滴。与您的代码相反,它确实为OCSP响应签名(以及CRL签名)添加了验证信息。

在这里,您可以找到该类到iText 7的端口。

实用类 AdobeLtvEnabling

该实用程序类捆绑了LTV所需的代码,以在签名的PDF文档中启用签名。这些代码段大部分来自现有的iText代码。之所以没有设计此类的主要原因是该类LtvVerification所需的变量和方法是private。由于该类最初是为iText
5编写的,因此可能会在其中找到一些iText-5-ism。

public class AdobeLtvEnabling {
    /**
     * Use this constructor with a {@link PdfDocument} in append mode. Otherwise
     * the existing signatures will be damaged.
     */
    public AdobeLtvEnabling(PdfDocument pdfDocument) {
        this.pdfDocument = pdfDocument;
    }

    /**
     * Call this method to have LTV information added to the {@link PdfDocument}
     * given in the constructor.
     */
    public void enable(IOcspClient ocspClient, ICrlClient crlClient) throws OperatorException, GeneralSecurityException, IOException, StreamParsingException, OCSPException {
        SignatureUtil signatureUtil = new SignatureUtil(pdfDocument);

        List<String> names = signatureUtil.getSignatureNames();
        for (String name : names) {
            PdfPKCS7 pdfPKCS7 = signatureUtil.verifySignature(name, BouncyCastleProvider.PROVIDER_NAME);
            PdfSignature sig = signatureUtil.getSignature(name);
            List<X509Certificate> certificatesToCheck = new ArrayList<>();
            certificatesToCheck.add(pdfPKCS7.getSigningCertificate());
            while (!certificatesToCheck.isEmpty()) {
                X509Certificate certificate = certificatesToCheck.remove(0);
                addLtvForChain(certificate, ocspClient, crlClient, getSignatureHashKey(sig));
            }
        }

        outputDss();
    }

    //
    // the actual LTV enabling methods
    //
    void addLtvForChain(X509Certificate certificate, IOcspClient ocspClient, ICrlClient crlClient, PdfName key) throws GeneralSecurityException, IOException, StreamParsingException, OperatorCreationException, OCSPException {
        ValidationData validationData = new ValidationData();

        while (certificate != null) {
            System.out.println(certificate.getSubjectX500Principal().getName());
            X509Certificate issuer = getIssuerCertificate(certificate);
            validationData.certs.add(certificate.getEncoded());
            byte[] ocspResponse = ocspClient.getEncoded(certificate, issuer, null);
            if (ocspResponse != null) {
                System.out.println("  with OCSP response");
                validationData.ocsps.add(ocspResponse);
                X509Certificate ocspSigner = getOcspSignerCertificate(ocspResponse);
                if (ocspSigner != null) {
                    System.out.printf("  signed by %s\n", ocspSigner.getSubjectX500Principal().getName());
                }
                addLtvForChain(ocspSigner, ocspClient, crlClient, getOcspHashKey(ocspResponse));
            } else {
               Collection<byte[]> crl = crlClient.getEncoded(certificate, null);
               if (crl != null && !crl.isEmpty()) {
                   System.out.printf("  with %s CRLs\n", crl.size());
                   validationData.crls.addAll(crl);
                   for (byte[] crlBytes : crl) {
                       addLtvForChain(null, ocspClient, crlClient, getCrlHashKey(crlBytes));
                   }
               }
            }
            certificate = issuer;
        }

        validated.put(key, validationData);
    }

    void outputDss() throws IOException {
        PdfDictionary dss = new PdfDictionary();
        PdfDictionary vrim = new PdfDictionary();
        PdfArray ocsps = new PdfArray();
        PdfArray crls = new PdfArray();
        PdfArray certs = new PdfArray();

        PdfCatalog catalog = pdfDocument.getCatalog();
        if (pdfDocument.getPdfVersion().compareTo(PdfVersion.PDF_2_0) < 0) {
            catalog.addDeveloperExtension(PdfDeveloperExtension.ESIC_1_7_EXTENSIONLEVEL5);
            catalog.addDeveloperExtension(new PdfDeveloperExtension(PdfName.ADBE, new PdfName("1.7"), 8));
        }

        for (PdfName vkey : validated.keySet()) {
            PdfArray ocsp = new PdfArray();
            PdfArray crl = new PdfArray();
            PdfArray cert = new PdfArray();
            PdfDictionary vri = new PdfDictionary();
            for (byte[] b : validated.get(vkey).crls) {
                PdfStream ps = new PdfStream(b);
                ps.setCompressionLevel(CompressionConstants.DEFAULT_COMPRESSION);
                ps.makeIndirect(pdfDocument);
                crl.add(ps);
                crls.add(ps);
                crls.setModified();
            }
            for (byte[] b : validated.get(vkey).ocsps) {
                b = buildOCSPResponse(b);
                PdfStream ps = new PdfStream(b);
                ps.setCompressionLevel(CompressionConstants.DEFAULT_COMPRESSION);
                ps.makeIndirect(pdfDocument);
                ocsp.add(ps);
                ocsps.add(ps);
                ocsps.setModified();
            }
            for (byte[] b : validated.get(vkey).certs) {
                PdfStream ps = new PdfStream(b);
                ps.setCompressionLevel(CompressionConstants.DEFAULT_COMPRESSION);
                ps.makeIndirect(pdfDocument);
                cert.add(ps);
                certs.add(ps);
                certs.setModified();
            }
            if (ocsp.size() > 0) {
                ocsp.makeIndirect(pdfDocument);
                vri.put(PdfName.OCSP, ocsp);
            }
            if (crl.size() > 0) {
                crl.makeIndirect(pdfDocument);
                vri.put(PdfName.CRL, crl);
            }
            if (cert.size() > 0) {
                cert.makeIndirect(pdfDocument);
                vri.put(PdfName.Cert, cert);
            }
            vri.put(PdfName.TU, new PdfDate().getPdfObject());
            vri.makeIndirect(pdfDocument);
            vrim.put(vkey, vri);
        }
        vrim.makeIndirect(pdfDocument);
        vrim.setModified();
        dss.put(PdfName.VRI, vrim);
        if (ocsps.size() > 0) {
            ocsps.makeIndirect(pdfDocument);
            dss.put(PdfName.OCSPs, ocsps);
        }
        if (crls.size() > 0) {
            crls.makeIndirect(pdfDocument);
            dss.put(PdfName.CRLs, crls);
        }
        if (certs.size() > 0) {
            certs.makeIndirect(pdfDocument);
            dss.put(PdfName.Certs, certs);
        }

        dss.makeIndirect(pdfDocument);
        dss.setModified();
        catalog.put(PdfName.DSS, dss);
    }

    //
    // VRI signature hash key calculation
    //
    static PdfName getCrlHashKey(byte[] crlBytes) throws NoSuchAlgorithmException, IOException, CRLException, CertificateException {
        CertificateFactory cf = CertificateFactory.getInstance("X.509");
        X509CRL crl = (X509CRL)cf.generateCRL(new ByteArrayInputStream(crlBytes));
        byte[] signatureBytes = crl.getSignature();
        DEROctetString octetString = new DEROctetString(signatureBytes);
        byte[] octetBytes = octetString.getEncoded();
        byte[] octetHash = hashBytesSha1(octetBytes);
        PdfName octetName = new PdfName(convertToHex(octetHash));
        return octetName;
    }

    static PdfName getOcspHashKey(byte[] basicResponseBytes) throws NoSuchAlgorithmException, IOException {
        BasicOCSPResponse basicResponse = BasicOCSPResponse.getInstance(basicResponseBytes);
        byte[] signatureBytes = basicResponse.getSignature().getBytes();
        DEROctetString octetString = new DEROctetString(signatureBytes);
        byte[] octetBytes = octetString.getEncoded();
        byte[] octetHash = hashBytesSha1(octetBytes);
        PdfName octetName = new PdfName(convertToHex(octetHash));
        return octetName;
    }

    static PdfName getSignatureHashKey(PdfSignature sig) throws NoSuchAlgorithmException, IOException {
        PdfString contents = sig.getContents();
        byte[] bc = PdfEncodings.convertToBytes(contents.getValue(), null);
        if (PdfName.ETSI_RFC3161.equals(sig.getSubFilter())) {
            try (   ASN1InputStream din = new ASN1InputStream(new ByteArrayInputStream(bc)) ) {
                ASN1Primitive pkcs = din.readObject();
                bc = pkcs.getEncoded();
            }
        }
        byte[] bt = hashBytesSha1(bc);
        return new PdfName(convertToHex(bt));
    }

    static byte[] hashBytesSha1(byte[] b) throws NoSuchAlgorithmException {
        MessageDigest sh = MessageDigest.getInstance("SHA1");
        return sh.digest(b);
    }

    static String convertToHex(byte[] bytes) {
        ByteBuffer buf = new ByteBuffer();
        for (byte b : bytes) {
            buf.appendHex(b);
        }
        return PdfEncodings.convertToString(buf.toByteArray(), null).toUpperCase();
    }

    //
    // OCSP response helpers
    //
    static X509Certificate getOcspSignerCertificate(byte[] basicResponseBytes) throws CertificateException, OCSPException, OperatorCreationException {
        JcaX509CertificateConverter converter = new JcaX509CertificateConverter().setProvider(BouncyCastleProvider.PROVIDER_NAME);
        BasicOCSPResponse borRaw = BasicOCSPResponse.getInstance(basicResponseBytes);
        BasicOCSPResp bor = new BasicOCSPResp(borRaw);

        for (final X509CertificateHolder x509CertificateHolder : bor.getCerts()) {
            X509Certificate x509Certificate = converter.getCertificate(x509CertificateHolder);

            JcaContentVerifierProviderBuilder jcaContentVerifierProviderBuilder = new JcaContentVerifierProviderBuilder();
            jcaContentVerifierProviderBuilder.setProvider(BouncyCastleProvider.PROVIDER_NAME);
            final PublicKey publicKey = x509Certificate.getPublicKey();
            ContentVerifierProvider contentVerifierProvider = jcaContentVerifierProviderBuilder.build(publicKey);

            if (bor.isSignatureValid(contentVerifierProvider))
                return x509Certificate;
        }

        return null;
    }

    static byte[] buildOCSPResponse(byte[] BasicOCSPResponse) throws IOException {
        DEROctetString doctet = new DEROctetString(BasicOCSPResponse);
        ASN1EncodableVector v2 = new ASN1EncodableVector();
        v2.add(OCSPObjectIdentifiers.id_pkix_ocsp_basic);
        v2.add(doctet);
        ASN1Enumerated den = new ASN1Enumerated(0);
        ASN1EncodableVector v3 = new ASN1EncodableVector();
        v3.add(den);
        v3.add(new DERTaggedObject(true, 0, new DERSequence(v2)));            
        DERSequence seq = new DERSequence(v3);
        return seq.getEncoded();
    }

    //
    // X509 certificate related helpers
    //
    static X509Certificate getIssuerCertificate(X509Certificate certificate) throws IOException, StreamParsingException {
        String url = getCACURL(certificate);
        if (url != null && url.length() > 0) {
            HttpURLConnection con = (HttpURLConnection)new URL(url).openConnection();
            if (con.getResponseCode() / 100 != 2) {
                throw new PdfException(PdfException.InvalidHttpResponse1).setMessageParams(con.getResponseCode());
            }
            InputStream inp = (InputStream) con.getContent();
            X509CertParser parser = new X509CertParser();
            parser.engineInit(new ByteArrayInputStream(StreamUtil.inputStreamToArray(inp)));
            return (X509Certificate) parser.engineRead();
        }
        return null;
    }

    static String getCACURL(X509Certificate certificate) {
        ASN1Primitive obj;
        try {
            obj = getExtensionValue(certificate, Extension.authorityInfoAccess.getId());
            if (obj == null) {
                return null;
            }
            ASN1Sequence AccessDescriptions = (ASN1Sequence) obj;
            for (int i = 0; i < AccessDescriptions.size(); i++) {
                ASN1Sequence AccessDescription = (ASN1Sequence) AccessDescriptions.getObjectAt(i);
                if ( AccessDescription.size() != 2 ) {
                    continue;
                }
                else if (AccessDescription.getObjectAt(0) instanceof ASN1ObjectIdentifier) {
                    ASN1ObjectIdentifier id = (ASN1ObjectIdentifier)AccessDescription.getObjectAt(0);
                    if ("1.3.6.1.5.5.7.48.2".equals(id.getId())) {
                        ASN1Primitive description = (ASN1Primitive)AccessDescription.getObjectAt(1);
                        String AccessLocation =  getStringFromGeneralName(description);
                        if (AccessLocation == null) {
                            return "" ;
                        }
                        else {
                            return AccessLocation ;
                        }
                    }
                }
            }
        } catch (IOException e) {
            return null;
        }
        return null;
    }

    static ASN1Primitive getExtensionValue(X509Certificate certificate, String oid) throws IOException {
        byte[] bytes = certificate.getExtensionValue(oid);
        if (bytes == null) {
            return null;
        }
        ASN1InputStream aIn = new ASN1InputStream(new ByteArrayInputStream(bytes));
        ASN1OctetString octs = (ASN1OctetString) aIn.readObject();
        aIn = new ASN1InputStream(new ByteArrayInputStream(octs.getOctets()));
        return aIn.readObject();
    }

    static String getStringFromGeneralName(ASN1Primitive names) throws IOException {
        ASN1TaggedObject taggedObject = (ASN1TaggedObject) names ;
        return new String(ASN1OctetString.getInstance(taggedObject, false).getOctets(), "ISO-8859-1");
    }

    //
    // inner class
    //
    static class ValidationData {
        final List<byte[]> crls = new ArrayList<byte[]>();
        final List<byte[]> ocsps = new ArrayList<byte[]>();
        final List<byte[]> certs = new ArrayList<byte[]>();
    }

    //
    // member variables
    //
    final PdfDocument pdfDocument;

    final Map<PdfName,ValidationData> validated = new HashMap<PdfName,ValidationData>();
}

( AdobeLtvEnabling.java)

使用范例

您可以使用以下AdobeLtvEnabling类:

try (   PdfReader pdfReader = new PdfReader(SOURCE);
        PdfWriter pdfWriter = new PdfWriter(TARGET);
        PdfDocument pdfDocument = new PdfDocument(pdfReader, pdfWriter,
                new StampingProperties().preserveEncryption().useAppendMode())) {
    AdobeLtvEnabling adobeLtvEnabling = new AdobeLtvEnabling(pdfDocument);
    IOcspClient ocsp = new OcspClientBouncyCastle(null);
    ICrlClient crl = new CrlClientOnline();
    adobeLtvEnabling.enable(ocsp, crl);
}

( MakeLtvEnabled测试testLtvEnableSignWithoutLtv

局限性

由于此代码实质上是从引用的答案的iText 5代码移植而来的,因此它还继承了该答案中列出的限制:

该代码在一些简化的限制下有效,尤其是:

  • 签名时间戳被忽略,
  • 假定检索到的CRL是直接而完整的,
  • 假定可以使用AIA条目构建完整的证书链。

如果您不能接受这些限制,则可以相应地改进代码。



 类似资料:
  • 我安装了php7下载从repo的windows,所有工作良好。我正面临启用curl的问题。php_curl.dll文件存在于文本文件夹中,此扩展在php.ini中未注释,但此模块未显示在phpinfo中。 只有在运行php5时,我才能使用php7面对这个问题。x旋度很好用。正在寻找解决此问题的解决方案。这是php7本身的问题。是否有解决此问题的方法?

  • 我在一个gwt/GWTPhonegap/MGWT项目中工作,我需要使用GWT2.5.1和SuperDevMode。 我使用maven和下面的命令“classpath gwt:runcodeserver”,当我编译我的项目时没有发生错误,但是当我使用superDevMode进入我的webBrowser时,无法重新编译,错误消息是“this module has not have Super Dev

  • 我有一个Spring Boot应用程序,其中有一个Kafka消费者和生产者。还有一个bean来创建主题。 我的Spring Boot应用程序和Kafka都是在Kubernetes的Docker启动的。有时Spring Boot应用程序在Kafka pod启动之前就启动了,因此无法启动,因为用户无法连接(参见stacktrace)。 有没有一种方法可以让我的应用程序以弹性的方式启动?例如,消费者应该

  • 我们正在尝试启用签名LTV。我正在使用下面的代码添加验证。签名时。isTsp()为false,PDF表示签名未启用LTV,但在另一种情况下(Signature.isTsp()为true),它显示为有效。当我们打开PDF并尝试通过右键单击签名手动添加验证信息时,它将启用LTV,而不会出现任何问题。不知道我们在这里遗漏了什么。任何输入都将非常有用。

  • 在整个评估过程中使用JxBrowser,效果非常好。现在,我一启动它就突然不起作用了。我在Mac OS X 10.11上运行,但我也在Windows上测试过,仍然不起作用。我的构建路径中有两个操作系统所需的库。这就是我得到的(在Mac OS X上运行):