使用mssp将数字签名附加到pdf [英] Attach digital signature to pdf using mssp

查看:95
本文介绍了使用mssp将数字签名附加到pdf的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试对pdf文档进行数字签名,并且需要使用MSSP(移动签名服务提供商)将签名附加到签名面板上.我研究了一些stackoverflow问题,并做了如下操作.

I am trying to sign pdf document digitally and need to attach signature to signature panel using MSSP(mobile signature service provider). I researched some stackoverflow questions and i did things as below.

首先,我创建pdf的校验和.在生成校验和之前,将空签名添加到pdf.生成校验和后,我将其作为数据发送给签名文件到服务器.服务器给了我base64签名,我从base64签名中找到了证书链.现在,我需要将签名附加到pdf,显示给签名面板". Adobe Reader部分.

First i create checksum of pdf. Before generate the checksum add empty signature to pdf. After i generated the checksum i send that as data to sign document to the server. The server gives to me base64 signature and i found certificate chain from base64 signature. Now i need to attach signature to the pdf, show to the "Signature Panel" section of Adobe reader.

我从base64签名中提取证书链,但我不知道如何将其附加到pdf.

I extract certificate chain from base64 signature and i dont know how to attach this to pdf.

我的代码是:

此功能确实会为pdf创建空签名.

this function does create empty signature to pdf.

public static void emptySignature(String src, String dest, String fieldname) throws IOException, DocumentException, GeneralSecurityException {
    PdfReader reader = new PdfReader(src);
    FileOutputStream os = new FileOutputStream(dest);
    PdfStamper stamper = PdfStamper.createSignature(reader, os, '\0');
    PdfSignatureAppearance appearance = stamper.getSignatureAppearance();
    appearance.setVisibleSignature(new Rectangle(36, 748, 144, 780), 1, fieldname);
    ExternalSignatureContainer external = new ExternalBlankSignatureContainer(PdfName.ADOBE_PPKLITE, PdfName.ADBE_PKCS7_DETACHED);
    MakeSignature.signExternalContainer(appearance, external, 8192);
}

此函数确实获取pdf的SHA-256哈希值.

this function does get SHA-256 hash value of pdf.

public static String getHashValue(String filename) throws NoSuchAlgorithmException, IOException {
    MessageDigest md = MessageDigest.getInstance("SHA-256");
    String hex = checksum("output.pdf", md);
    System.out.println("CHECKSUM: " + hex);
    return hex;
}

private static String checksum(String filepath, MessageDigest md) throws IOException {

    try (DigestInputStream dis = new DigestInputStream(new FileInputStream(filepath), md)) {
        while (dis.read() != -1) ;
        md = dis.getMessageDigest();
    }

    StringBuilder result = new StringBuilder();
    for (byte b : md.digest()) {
        result.append(String.format("%02x", b));
    }
    return result.toString();
}

然后我将pdf的哈希值发送到服务器,并获得base 64签名值: "MIAGCSqGSIb3DQEHAqCAMIACAQExDzANBglghkgBZQMEAgEFADCABgkqhkiG9w0BBwGggAQEVEVTVAAAAACggDCCBhwwggQEoAMCAQIC ... NKodC346j0GKueTJ595rhi2NbT679XZwMaMMqEyT41pimV76Nm85eW/2yYjHt08gCNVSJGP7laR8taVAAAAAAAAA ="

then i send the hash of pdf to server and got base 64 signature value: "MIAGCSqGSIb3DQEHAqCAMIACAQExDzANBglghkgBZQMEAgEFADCABgkqhkiG9w0BBwGggAQEVEVTVAAAAACggDCCBhwwggQEoAMCAQIC ... NKodC346j0GKueTJ595rhi2NbT679XZwMaMMqEyT41pimV76Nm85eW/2yYjHt08gCNVSJGP7laR8taVAAAAAAAAA="

证书链如下所示:

我尝试了一些方法来将签名附加到pdf的签名"面板,但是它需要私钥.因此,请帮助我提供一些建议,谢谢.

i tried some methods to attach signature to pdf's Signature panel but it requires private key. So please helping me giving for some advise, Thank you.

更新1:

在我使用公共证书将签名附加到pdf之后,我收到了pdf消息签名无效";

After i attach signature to pdf using public certificate i got message in pdf "signature is invalid"

此代码是我附加签名的方式(我是从链的第一个证书生成的pem文件):

This code is how i attach signature(I generated pem file from first certificate of chain):

final String SRC = "test.pdf";
final String DEST = "signed.pdf";
final String CERT = "cert.pem";

    File initialFile = new File(CERT);
    InputStream is = new FileInputStream(initialFile);

    // We get the self-signed certificate from the client
    CertificateFactory factory = CertificateFactory.getInstance("X.509");
    Certificate[] chain = new Certificate[1];
    chain[0] = factory.generateCertificate(is);
    System.out.println("chain[0]: -----> " + chain[0]);

    // we create a reader and a stamper
    PdfReader reader = new PdfReader(SRC);
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    PdfStamper stamper = PdfStamper.createSignature(reader, baos, '\0');

    // we create the signature appearance
    PdfSignatureAppearance sap = stamper.getSignatureAppearance();
    sap.setReason("test");
    sap.setLocation("test");
    sap.setVisibleSignature(new Rectangle(36, 748, 36, 748), 1, "signature"); //invisible
    sap.setCertificate(chain[0]);

    // we create the signature infrastructure
    PdfSignature dic = new PdfSignature(PdfName.ADOBE_PPKLITE, PdfName.ADBE_PKCS7_DETACHED);
    dic.setReason(sap.getReason());
    dic.setLocation(sap.getLocation());
    dic.setContact(sap.getContact());
    dic.setDate(new PdfDate(sap.getSignDate()));
    sap.setCryptoDictionary(dic);
    HashMap<PdfName, Integer> exc = new HashMap<PdfName, Integer>();
    exc.put(PdfName.CONTENTS, new Integer(8192 * 2 + 2));
    sap.preClose(exc);
    ExternalDigest externalDigest = new ExternalDigest() {
        public MessageDigest getMessageDigest(String hashAlgorithm)
                throws GeneralSecurityException {
            return DigestAlgorithms.getMessageDigest(hashAlgorithm, null);
        }
    };
    PdfPKCS7 sgn = new PdfPKCS7(null, chain, "SHA256", null, externalDigest, false);
    InputStream data = sap.getRangeStream();
    byte hash[] = DigestAlgorithms.digest(data, externalDigest.getMessageDigest("SHA256"));


    // we get OCSP and CRL for the cert
    OCSPVerifier ocspVerifier = new OCSPVerifier(null, null);
    OcspClient ocspClient = new OcspClientBouncyCastle(ocspVerifier);
    byte[] ocsp = null;
    if (chain.length >= 2 && ocspClient != null) {
        ocsp = ocspClient.getEncoded((X509Certificate) chain[0], (X509Certificate) chain[1], null);
    }

    byte[] sh = sgn.getAuthenticatedAttributeBytes(hash, ocsp, null, MakeSignature.CryptoStandard.CMS);
    byte[] signedAttributesHash = DigestAlgorithms.digest(new ByteArrayInputStream(sh), externalDigest.getMessageDigest("SHA256"));

    ByteArrayOutputStream os = baos;

    byte[] signedHash = java.util.Base64.getDecoder().decode(base64Signature);

    // we complete the PDF signing process
    sgn.setExternalDigest(signedHash, null, "RSA");
    Collection<byte[]> crlBytes = null;
    TSAClientBouncyCastle tsaClient = null;

    byte[] encodedSig = sgn.getEncodedPKCS7(hash, tsaClient, ocsp, crlBytes, MakeSignature.CryptoStandard.CMS);
    byte[] paddedSig = new byte[8192];
    System.arraycopy(encodedSig, 0, paddedSig, 0, encodedSig.length);
    PdfDictionary dic2 = new PdfDictionary();
    dic2.put(PdfName.CONTENTS, new PdfString(paddedSig).setHexWriting(true));

    try {
        sap.close(dic2);
    } catch (DocumentException e) {
        throw new IOException(e);
    }

    FileOutputStream fos = new FileOutputStream(new File(DEST));
    os.writeTo(fos);

更新2:

public byte[] sign(byte[] message) throws GeneralSecurityException {

    MessageDigest messageDigest = MessageDigest.getInstance(getHashAlgorithm());
    byte[] messageHash  = messageDigest.digest(message);
    StringBuilder sb = new StringBuilder();
    for (int i = 0; i < messageHash.length; ++i) {
        sb.append(Integer.toHexString((messageHash[i] & 0xFF) | 0x100).substring(1, 3));
    }

    byte[] signedByte = null;
    String msisdn = "97688888888";
    Client client = null;
    try {
        client = new Client( msisdn, sb.toString());
    } catch (JSONException e) {
        e.printStackTrace();
    }
    try {
        String strResult = client.sendRequest();
        JSONObject jsonResult = new JSONObject(strResult);
        System.out.println("Response:" + jsonResult);
        String base64Signature = jsonResult.getJSONObject("MSS_SignatureResp").getJSONObject("MSS_Signature").getString("Base64Signature");

        System.out.println(base64Signature);

        signedByte = Base64.getDecoder().decode(base64Signature);
    } catch (IOException | JSONException e) {
        e.printStackTrace();
    }

    return signedByte;
}

更新3:

推荐答案

问题中的代码基于多阶段方法:

The code in the question is based on a multi-phase approach:

  • 对注入一个空字节数组作为签名容器的PDF进行签名(通过使用ExternalBlankSignatureContainer).
  • 计算要签名的摘要(此处错误地计算了上一步中整个文件的摘要,必须为整个文件计算摘要,但签名容器占位符除外).
  • 请求该文档哈希的签名容器.
  • 从第一步开始将签名容器注入文件中的占位符.
  • Sign the PDF injecting an empty byte array as signature container (by use of the ExternalBlankSignatureContainer).
  • Calculate the digest to sign (here incorrectly a digest for the whole file from the previous step is calculated, the digest must be calculated for the whole file except the signature container placeholder).
  • Request signature container for that document hash.
  • Inject signature container into the placeholder in the file from the first step.

如果签名服务器可能需要很长时间才能返回签名容器,则这种方法是适当的,但对于手头的注释,应予以澄清

If the signature server can take a long time to return a signature container, such an approach is appropriate but in the case at hand comments clarify

服务器快速响应(仅几秒钟)

The server gives response quickly(just few seconds)

在这样的用例中,应该采用单一步骤(就iText签名API调用而言):

In such a use case one should proceed in a single step approach (as far as iText signing API calls are concerned):

PdfReader reader = new PdfReader(...);
PdfStamper stamper = PdfStamper.createSignature(reader, ..., '\0');
PdfSignatureAppearance appearance = stamper.getSignatureAppearance();
appearance.setVisibleSignature(new Rectangle(36, 748, 144, 780), 1, "Signature");
ExternalSignatureContainer external = new RemoteSignatureContainer();
MakeSignature.signExternalContainer(appearance, external, 8192);

具有自定义ExternalSignatureContainer实现RemoteSignatureContainer:

class RemoteSignatureContainer implements ExternalSignatureContainer {

    @Override
    public byte[] sign(InputStream data) throws GeneralSecurityException {
        [return a CMS signature container signing the data from the InputStream argument]
    }

    @Override
    public void modifySigningDictionary(PdfDictionary signDic) {
        signDic.put(PdfName.FILTER, PdfName.ADOBE_PPKLITE);
        signDic.put(PdfName.SUBFILTER, PdfName.ADBE_PKCS7_DETACHED);
    }
}

我不知道您的API用于访问签名服务器,但是基于您的 UPDATE 2 ,我假设您RemoteSignatureContainer中的sign方法看起来像这样:

I don't know your API to access your signature server but based on your UPDATE 2 I assume the sign method in your RemoteSignatureContainer would look like this:

@Override
public byte[] sign(InputStream data) throws GeneralSecurityException {
    MessageDigest messageDigest = MessageDigest.getInstance("SHA256");
    byte[] messageHash  = messageDigest.digest(StreamUtil.inputStreamToArray(data));
    StringBuilder sb = new StringBuilder();
    for (int i = 0; i < messageHash.length; ++i) {
        sb.append(Integer.toHexString((messageHash[i] & 0xFF) | 0x100).substring(1, 3));
    }

    String msisdn = "97688888888";
    try {
        Client client = new Client(msisdn, sb.toString());

        String strResult = client.sendRequest();
        JSONObject jsonResult = new JSONObject(strResult);
        String base64Signature = jsonResult
                   .getJSONObject("MSS_SignatureResp")
                   .getJSONObject("MSS_Signature")
                   .getString("Base64Signature");

        return Base64.getDecoder().decode(base64Signature);
    } catch (IOException | JSONException e) {
        throw new GeneralSecurityException(e);
    }
}

这篇关于使用mssp将数字签名附加到pdf的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆