Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/typescript/9.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 使用网络HSM和PDFBox签署PDF_Java_Pdf_Pdfbox_Bouncycastle - Fatal编程技术网

Java 使用网络HSM和PDFBox签署PDF

Java 使用网络HSM和PDFBox签署PDF,java,pdf,pdfbox,bouncycastle,Java,Pdf,Pdfbox,Bouncycastle,我试图以CreateSignature示例为起点,对其进行更改,使其与基于外部网络的HSM系统一起工作 生成的PDF文档总是抱怨“文档已被更改”。我对应该用什么来签名缺乏洞察力 以下是my CreateSignatureBase.java中sign()的实现: @Override public byte[] sign(InputStream content) throws IOException { // cannot be done private (interfac

我试图以CreateSignature示例为起点,对其进行更改,使其与基于外部网络的HSM系统一起工作

生成的PDF文档总是抱怨“文档已被更改”。我对应该用什么来签名缺乏洞察力

以下是my CreateSignatureBase.java中sign()的实现:

@Override
    public byte[] sign(InputStream content) throws IOException {

        // cannot be done private (interface)
        try {

            // Certificate chain is acquired at initialization
            List<Certificate> certList = new ArrayList<>();
            certList.addAll(Arrays.asList(certificateChain));
            Store<?> certs = new JcaCertStore(certList);
            org.bouncycastle.asn1.x509.Certificate cert = org.bouncycastle.asn1.x509.Certificate.getInstance(certificateChain[0].getEncoded());

            CMSSignedDataGenerator gen = new CMSSignedDataGenerator();

            //HSMSigner is the class that interacts with the network HSM to get the signature.
            HSMSigner signer = new HSMSigner();
            byte[] input = IOUtil.toByteArray(content);

            //SignedHash is a base64-encoded PKCS1 block. see HSMSigner.getSignature() below
            final String signedHash = signer.getSignature(input);

            ContentSigner sha1Signer = new ContentSigner() {

                @Override
                public byte[] getSignature() {
                    return Base64.getDecoder().decode(signedHash);
                }

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

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

            gen.addSignerInfoGenerator(new JcaSignerInfoGeneratorBuilder(new JcaDigestCalculatorProviderBuilder().build()).build(sha1Signer, new X509CertificateHolder(cert)));
            gen.addCertificates(certs);
            CMSProcessableInputStream msg = new CMSProcessableInputStream(content);
            CMSSignedData cmsSignedData = gen.generate(msg, true);

            byte[] result = cmsSignedData.getEncoded();

            return result;

        } catch (GeneralSecurityException | CMSException | OperatorCreationException e) {
            throw new IOException(e);
        }

    }
非常感谢您能帮我找出我做错了什么

签名pdf的副本可在以下网址找到:


提前谢谢

感谢您的详细回复,我能够更新CreateSignatureBase.sign()方法,如下所示,以获得所需的结果

CreateSignautreBase.java:sign()

@覆盖
公共字节[]符号(InputStream内容)引发IOException{
//无法完成私有(接口)
试一试{
//初始化时获取证书链
List certList=new ArrayList();
addAll(Arrays.asList(certificateChain));
存储证书=新JcaCertStore(证书列表);
org.bouncycastle.asn1.x509.Certificate cert=org.bouncycastle.asn1.x509.Certificate.getInstance(certificateChain[0].getEncoded());
CMSSignedDataGenerator gen=新的CMSSignedDataGenerator();
//HSMSigner是与网络HSM交互以获取签名的类。
HSMSigner signer=新的HSMSigner();
字节[]输入=IOUtil.toByteArray(内容);
//这是对以前代码的更新。
//创建内容的散列并将其添加到属性映射
MessageDigest=MessageDigest.getInstance(“SHA-256”);
messageDigest.update(输入);
Attribute attr=new属性(CMSAttributes.messageDigest,new DERSet)(new DEROctetString(messageDigest.digest());
ASN1EncodableVector v=新的ASN1EncodableVector();
v、 添加(attr);
ContentSigner sha1Signer=新ContentSigner(){
//这是为了确保使用正确的数据创建签名。
ByteArrayOutputStream=新建ByteArrayOutputStream();
@凌驾
公共字节[]getSignature(){
//在这里调用HSM,流是AttributeMap
返回Base64.getDecoder().decode(signer.getSignature(stream.toByteArray());
}
//可能由BouncyCastle库调用以提供内容
@凌驾
public OutputStream getOutputStream(){
回流;
}
@凌驾
公共算法标识符getAlgorithmIdentifier(){
返回新的DefaultSignatureAlgorithmIdentifierFinder().find(“SHA256WithRSANYPTION”);
}
};
//根据mkl的评论,使用AttributeTable作为输入,其中该表已经具有内容的哈希值。
SignerInfoGeneratorBuilder=new SignerInfoGeneratorBuilder(new BcDigestCalculatorProvider())
.setSignedAttributeGenerator(新的DefaultSignedAttributeTableGenerator(新的AttributeTable(v)));
gen.addSignerInfoGenerator(建造商建造(sha1Signer,新X509认证持有人(cert));
一般证书(证书);
CMSProcessableInputStream msg=新的CMSProcessableInputStream(内容);
cmssignedata cmssignedata=gen.generate(msg,true);
byte[]result=cmsSignedData.getEncoded();
返回结果;
}catch(一般安全性异常| CMSException |运算符创建异常e){
抛出新的IOException(e);
}
}

HSMSigner.getSignature()保持不变

上面的文件链接不起作用。请在代码中使用您获取的
InputStream内容
,创建其字节的PKCS#1签名,然后尝试基于此签名构建CMS签名容器。这是错误的方法,
InputStream content
中的数据不能直接签名,而是必须对其进行散列,然后它们的散列只是一种键值对映射中的一个值,而该映射(称为“签名属性”)将由HSM签名。我无法判断您的
HSMSigner().getSignature()
是否正确,这取决于该
signClient
的行为。您好@mlk,感谢您的指导,我能够根据您的建议更新我的代码并使其正常工作。我已经更新了下面的代码。嗨,你能告诉我HSM的工作情况吗。当我们发送摘要时它会返回什么。您在HSM中使用哪个库来签署摘要。我希望在不调用服务的情况下实现相同的逻辑。
public String getSignature(byte [] bytes) {


        String host = "hsmvip.corp.com";
        int port = 9000;
        SignClient signClient;
        try {
            //initialize the sign client 
            signClient = ///..;
            MessageDigest messageDigest = MessageDigest.getInstance("SHA-256");
            messageDigest.update(bytes);
            byte[] outputDigest = messageDigest.digest();

            // signature returned by the sign method is a base64-encoded PKCS1 block.
            String signature = signClient.sign(Base16.encodeAsString(outputDigest));
            signClient.close();
            return signature;
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            return null;
        }

    }
    @Override
    public byte[] sign(InputStream content) throws IOException {

        // cannot be done private (interface)
        try {

            // Certificate chain is acquired at initialization
            List<Certificate> certList = new ArrayList<>();
            certList.addAll(Arrays.asList(certificateChain));
            Store<?> certs = new JcaCertStore(certList);
            org.bouncycastle.asn1.x509.Certificate cert = org.bouncycastle.asn1.x509.Certificate.getInstance(certificateChain[0].getEncoded());

            CMSSignedDataGenerator gen = new CMSSignedDataGenerator();

            //HSMSigner is the class that interacts with the network HSM to get the signature.
            HSMSigner signer = new HSMSigner();
            byte[] input = IOUtil.toByteArray(content);

            //This is the update over previous code.
            //Create a hash of the content and add it to the attribute map
            MessageDigest messageDigest = MessageDigest.getInstance("SHA-256");
            messageDigest.update(input);
            Attribute attr = new Attribute(CMSAttributes.messageDigest, new DERSet(new DEROctetString(messageDigest.digest())));
            ASN1EncodableVector v = new ASN1EncodableVector();
            v.add(attr);

            ContentSigner sha1Signer = new ContentSigner() {
                //This is to ensure that signature is created using the right data.
                ByteArrayOutputStream stream = new ByteArrayOutputStream();

                @Override
                public byte[] getSignature() {
                    //Calling HSM here instead, the stream is the AttributeMap
                    return Base64.getDecoder().decode(signer.getSignature(stream.toByteArray()));
                }
                //Perhaps called by BouncyCastle library to provide the content
                @Override
                public OutputStream getOutputStream() {
                    return stream;
                }

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

            //As per mkl's comment, using the AttributeTable as an input where the table already has a Hashed value of the content.
            SignerInfoGeneratorBuilder builder = new SignerInfoGeneratorBuilder(new BcDigestCalculatorProvider())
                    .setSignedAttributeGenerator(new DefaultSignedAttributeTableGenerator(new AttributeTable(v)));

            gen.addSignerInfoGenerator(builder.build(sha1Signer, new X509CertificateHolder(cert)));

            gen.addCertificates(certs);
            CMSProcessableInputStream msg = new CMSProcessableInputStream(content);
            CMSSignedData cmsSignedData = gen.generate(msg, true);

            byte[] result = cmsSignedData.getEncoded();

            return result;

        } catch (GeneralSecurityException | CMSException | OperatorCreationException e) {
            throw new IOException(e);
        }

    }