使用网络HSM和PDFBox签名PDF

时间:2018-06-22 23:40:09

标签: pdf bouncycastle pdfbox

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

生成的PDF文档始终抱怨“文档已被更改”。我缺乏关于应该使用什么签名的见识。

以下是我的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);
        }

    }

以下是HSMSigner()。getSignature()

的实现
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;
        }

    }

任何帮助我弄清楚自己在做错什么的人,都表示赞赏。

可以在https://file.io/2tVvYO上找到签名pdf的副本

谢谢!

1 个答案:

答案 0 :(得分:1)

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

CreateSignautreBase.java:sign()

listViewUsers.Items.Clear();                
dsUsers = aUser.GetUserNamesList(int.Parse(clientId));
int rowsCount = dsUsers.Tables["UserNames"].Rows.Count;

for (int i = 0; i < rowsCount; i++)
{
    dRow = dsUsers.Tables["UserNames"].Rows[i];

    lvi = new ListViewItem("item" + i, i);
    lvi.SubItems.Add(dRow["User_ID"].ToString().Trim());                  
    lvi.SubItems.Add(dRow["User Name"].ToString().Trim());

    listViewUsers.Items.Add(lvi);
}

HSMSigner.getSignature()保持不变。