使用PKCS#11和iText使用eID签署PDF

使用PKCS#11和iText使用eID签署PDF,pdf,itext,digital-signature,Pdf,Itext,Digital Signature,遵循中的“使用智能卡和PKCS签署文档#11”主题并创建类似于提供的代码示例后,已签名的文件签名在Adobe Reader中无效,签名外观具有不可否认证书的名称(即eID所有者的名称)但在Adobe Reader的签名面板中显示: 验证时出错: 我正在使用一个Gemalto PinPad和葡萄牙语eIDpteidpkcs11.dll以及eID中间件软件,安装在C:\Windows\System32中 我试过: 空检查 手动创建证书链,作为ks.getCertificateChain(“公民

遵循中的“使用智能卡和PKCS签署文档#11”主题并创建类似于提供的代码示例后,已签名的文件签名在Adobe Reader中无效,签名外观具有不可否认证书的名称(即eID所有者的名称)但在Adobe Reader的签名面板中显示:

验证时出错:

我正在使用一个Gemalto PinPad葡萄牙语eID
pteidpkcs11.dll
以及eID中间件软件,安装在C:\Windows\System32中

我试过:

  • 空检查
  • 手动创建证书链,作为
    ks.getCertificateChain(“公民签名证书”)返回的证书[]仅具有签名证书

提供的代码示例试图获取签名证书的
私钥,我发现它很奇怪,但我认为它只是用作参考。浏览用户在PinPad中取消进程时触发的异常堆栈跟踪,我得到了以下想法,幸运的是,解决了这个问题:

  • 创建自定义
    com.itextpdf.text.pdf.security.ExternalSignature
    实现
  • 实现一个实用程序类,该类使用
    sun.security.pkcs11.wrapper.pkcs11
    包装器,与eID pkcs11 dll(在我的示例中为pteidpkcs11.dll)交互,并提供一个签名方法,该方法接收字节[]消息,然后发送给智能卡读卡器进行签名,并返回字节[]这次行动的结果
  • 在CustomExternalSignature.sign(…)中使用实用程序类
  • 如果您正在为葡萄牙的开斋节而开发,您可以使用以下提示:

    • 对于上一个列表的第二项,我使用的是AndréBarbosa创建的开源项目中的
      PTeID4JPKCS11
      类,您只需要调用
      PTeID4JPKCS11.getInstance().sign(…)
    • 关于
      ExternalSignature
      接口所需的哈希和加密算法,哈希是SHA-1,加密是RSA

    作为替代方案,您可以使用葡萄牙语eid卡(Cartão de Cidadão)签名,只需使用上提供的java组件即可。它还可以在maven中央存储库中使用artifactid poreid

    下面是一个基于中提供的示例的示例


    我一直在使用葡萄牙公民卡为PDF文档进行数字签名,以下是我所拥有的:

    public void signCAdES(...) {
        String pkcs11Config = "name=GemPC" + "\n" + "library=C:\\WINDOWS\\SysWOW64\\pteidpkcs11.dll";
        ByteArrayInputStream configStream = new ByteArrayInputStream(pkcs11Config.getBytes());
        Provider pkcs11Provider = new sun.security.pkcs11.SunPKCS11(configStream);
    
        //provider_name: SunPKCS11-GemPC
        Security.addProvider(pkcs11Provider);
    
        javax.security.auth.callback.CallbackHandler cmdLineHdlr = new DialogCallbackHandler();
    
        KeyStore.Builder builder = KeyStore.Builder.newInstance("PKCS11", pkcs11Provider,
                new KeyStore.CallbackHandlerProtection(cmdLineHdlr));
        KeyStore ks= builder.getKeyStore();
    
        PdfReader reader = new PdfReader(src);
        FileOutputStream os = new FileOutputStream(dest);
    
        PdfStamper stamper = PdfStamper.createSignature(reader, os, '\0', new File(tempPath), true);
        PdfSignatureAppearance appearance = stamper.getSignatureAppearance();
    
        appearance.setReason(reason);
        appearance.setLocation(location);
        appearance.setCertificationLevel(level);
    
        String alias = "CITIZEN SIGNATURE CERTIFICATE";
    
        //certificates from electronic card and resources folder
        Certificate[] certs = getSignatureCertificatesChain(ks);
    
        PrivateKey pk = (PrivateKey) ks.getKey(alias, null);
    
        ExternalSignature es = new PrivateKeySignature(pk, "SHA-1", pkcs11Provider.getName());
        ExternalDigest digest = new BouncyCastleDigest();
    
        MakeSignature.signDetached(appearance, digest, es, certs, null, null, null, 0, MakeSignature.CryptoStandard.CADES);
    }
    
    我也必须构建证书链(getSignatureCertificatesChain(ks)),因为ks.getCertificateChain(“公民签名证书”)只提供一个证书,然后卡本身没有所有的证书,所以我必须在pki.cartaodecidadao.pt网站上找到丢失的证书,并将它们放在参考资料文件夹中。基本上,我使用卡和资源文件夹中的两个证书来构建链,方法是将它们与certificate.getIssuerX500Principal().getName()和certificate.getsubsecx500principal().getName()中的值链接起来(不同的卡可以有相同类型的不同证书,因为有效性可能不同,因此在同一类型中可能有004或008)

    据我所知,itext对CAdES(MakeSignature.CryptoStandard.CAdES)的支持是最新的,但您需要使用它,因为使用MakeSignature.CryptoStandard.CMS可能会导致签名不满足CAdES的所有标准(例如,缺少签名证书
    属性-请参阅)

    这段代码唯一的一个小问题是,可能缺少一些可选属性。我使用了这个工具中的验证器,生成的签名通过了验证,但它仍然发出警告,指出属性issuer serial丢失。我创建了一篇文章,希望大家都知道如何包含此处的属性:

    应该返回证书链

    在哪里可以找到在线工作工具


    建议:打电话给负责中间件的组织并寻求支持。

    我正在尝试做同样的事情。我遇到了很多问题,但我能够(至少)运行Andre Barbosa编写的代码,将itextpdf版本恢复为5.1.3。但是,我现在真的不知道该怎么做。明天我将尝试执行自定义外部签名实现!如何使用PDFSignaturePearance.setExternalDigest(摘要、数据、算法)设置自定义外部签名
    public void signCAdES(...) {
        String pkcs11Config = "name=GemPC" + "\n" + "library=C:\\WINDOWS\\SysWOW64\\pteidpkcs11.dll";
        ByteArrayInputStream configStream = new ByteArrayInputStream(pkcs11Config.getBytes());
        Provider pkcs11Provider = new sun.security.pkcs11.SunPKCS11(configStream);
    
        //provider_name: SunPKCS11-GemPC
        Security.addProvider(pkcs11Provider);
    
        javax.security.auth.callback.CallbackHandler cmdLineHdlr = new DialogCallbackHandler();
    
        KeyStore.Builder builder = KeyStore.Builder.newInstance("PKCS11", pkcs11Provider,
                new KeyStore.CallbackHandlerProtection(cmdLineHdlr));
        KeyStore ks= builder.getKeyStore();
    
        PdfReader reader = new PdfReader(src);
        FileOutputStream os = new FileOutputStream(dest);
    
        PdfStamper stamper = PdfStamper.createSignature(reader, os, '\0', new File(tempPath), true);
        PdfSignatureAppearance appearance = stamper.getSignatureAppearance();
    
        appearance.setReason(reason);
        appearance.setLocation(location);
        appearance.setCertificationLevel(level);
    
        String alias = "CITIZEN SIGNATURE CERTIFICATE";
    
        //certificates from electronic card and resources folder
        Certificate[] certs = getSignatureCertificatesChain(ks);
    
        PrivateKey pk = (PrivateKey) ks.getKey(alias, null);
    
        ExternalSignature es = new PrivateKeySignature(pk, "SHA-1", pkcs11Provider.getName());
        ExternalDigest digest = new BouncyCastleDigest();
    
        MakeSignature.signDetached(appearance, digest, es, certs, null, null, null, 0, MakeSignature.CryptoStandard.CADES);
    }
    
     ks.getCertificateChain("CITIZEN SIGNATURE CERTIFICATE")