java - 对 PDF 进行数字签名时出错

标签 java pdf itext digital-signature

我正在使用以下程序来签名 PDF(我从 http://developers.itextpdf.com/examples/security/digital-signatures-white-paper/digital-signatures-chapter-2 获得了完整的示例。

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.Security;
import java.security.cert.Certificate;

import org.bouncycastle.jce.provider.BouncyCastleProvider;

import com.itextpdf.text.DocumentException;
import com.itextpdf.text.Rectangle;
import com.itextpdf.text.pdf.PdfReader;
import com.itextpdf.text.pdf.PdfSignatureAppearance;
import com.itextpdf.text.pdf.PdfStamper;
import com.itextpdf.text.pdf.security.BouncyCastleDigest;
import com.itextpdf.text.pdf.security.DigestAlgorithms;
import com.itextpdf.text.pdf.security.ExternalDigest;
import com.itextpdf.text.pdf.security.ExternalSignature;
import com.itextpdf.text.pdf.security.MakeSignature;
import com.itextpdf.text.pdf.security.MakeSignature.CryptoStandard;
import com.itextpdf.text.pdf.security.PrivateKeySignature;

public class C2_01_SignHelloWorld {

public static final String KEYSTORE = "src/main/resources/ks";
public static final char[] PASSWORD = "password".toCharArray();
public static final String SRC      = "src/main/resources/CA_Commercial_Construction.pdf";
public static final String DEST     = "results/chapter2/CA_Commercial_Construction_signed%s.pdf";

public void sign(String src, String dest, Certificate[] chain, PrivateKey pk, String digestAlgorithm, String provider, CryptoStandard subfilter, String reason, String location)
        throws GeneralSecurityException, IOException, DocumentException {
    // Creating the reader and the stamper
    PdfReader reader = new PdfReader(src);
    FileOutputStream os = new FileOutputStream(dest);
    PdfStamper stamper = PdfStamper.createSignature(reader, os, '\0');
    // Creating the appearance
    PdfSignatureAppearance appearance = stamper.getSignatureAppearance();
    appearance.setReason(reason);
    appearance.setLocation(location);
    appearance.setVisibleSignature(new Rectangle(36, 748, 144, 780), 1, "sig");
    // Creating the signature
    ExternalDigest digest = new BouncyCastleDigest();
    ExternalSignature signature = new PrivateKeySignature(pk, digestAlgorithm, provider);
    MakeSignature.signDetached(appearance, digest, signature, chain, null, null, null, 0, subfilter);
}

public static void main(String[] args) throws GeneralSecurityException, IOException, DocumentException {
    BouncyCastleProvider provider = new BouncyCastleProvider();
    Security.addProvider(provider);
    KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
    ks.load(new FileInputStream(KEYSTORE), PASSWORD);
    String alias = (String) ks.aliases().nextElement();
    PrivateKey pk = (PrivateKey) ks.getKey(alias, PASSWORD);
    Certificate[] chain = ks.getCertificateChain(alias);
    C2_01_SignHelloWorld app = new C2_01_SignHelloWorld();
    app.sign(SRC, String.format(DEST, 1), chain, pk, DigestAlgorithms.SHA256, provider.getName(), CryptoStandard.CMS, "Signed for Testing", "Universe");
    app.sign(SRC, String.format(DEST, 2), chain, pk, DigestAlgorithms.SHA512, provider.getName(), CryptoStandard.CMS, "Test 2", "Universe");
    app.sign(SRC, String.format(DEST, 3), chain, pk, DigestAlgorithms.SHA256, provider.getName(), CryptoStandard.CADES, "Test 3", "Universe");
    //app.sign(SRC, String.format(DEST, 4), chain, pk, DigestAlgorithms.RIPEMD160, provider.getName(), CryptoStandard.CADES, "Test 4", "Ghent");
}}

我使用 keytool 设置了自己的 keystore ,但当我打开 PDF 并将证书标记为受信任时,我仍然收到错误 -“文档自签名以来已被更改或损坏”。我真的不知道这里发生了什么。

编辑:程序中没有错误。我面临的问题可以从我从上面的程序生成的这个PDF链接中看到:http://pasteboard.co/9hJX1Hv.png

最佳答案

我将 DigestAlgorithm 更改为 SHA1,并且“文档自签名以来已被更改或损坏”错误消失了。正确粘贴如下:

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.Security;
import java.security.cert.Certificate;

import org.bouncycastle.jce.provider.BouncyCastleProvider;

import com.itextpdf.text.DocumentException;
import com.itextpdf.text.Rectangle;
import com.itextpdf.text.pdf.PdfReader;
import com.itextpdf.text.pdf.PdfSignatureAppearance;
import com.itextpdf.text.pdf.PdfStamper;
import com.itextpdf.text.pdf.security.BouncyCastleDigest;
import com.itextpdf.text.pdf.security.DigestAlgorithms;
import com.itextpdf.text.pdf.security.ExternalDigest;
import com.itextpdf.text.pdf.security.ExternalSignature;
import com.itextpdf.text.pdf.security.MakeSignature;
import com.itextpdf.text.pdf.security.MakeSignature.CryptoStandard;
import com.itextpdf.text.pdf.security.PrivateKeySignature;

public class C2_01_SignHelloWorld {

public static final String KEYSTORE = "src/main/resources/ks";
public static final char[] PASSWORD = "password".toCharArray();
public static final String SRC      = "src/main/resources/CA_Commercial_Construction.pdf";
public static final String DEST     = "results/chapter2/CA_Commercial_Construction_signed%s.pdf";

public void sign(String src, String dest, Certificate[] chain, PrivateKey pk, String digestAlgorithm, String provider, CryptoStandard subfilter, String reason, String location)
        throws GeneralSecurityException, IOException, DocumentException {
    // Creating the reader and the stamper
    PdfReader reader = new PdfReader(src);
    FileOutputStream os = new FileOutputStream(dest);
    PdfStamper stamper = PdfStamper.createSignature(reader, os, '\0');
    // Creating the appearance
    PdfSignatureAppearance appearance = stamper.getSignatureAppearance();
    appearance.setReason(reason);
    appearance.setLocation(location);
    appearance.setVisibleSignature(new Rectangle(36, 748, 144, 780), 1, "sig");
    // Creating the signature
    ExternalDigest digest = new BouncyCastleDigest();
    ExternalSignature signature = new PrivateKeySignature(pk, digestAlgorithm, provider);
    MakeSignature.signDetached(appearance, digest, signature, chain, null, null, null, 0, subfilter);
}

public static void main(String[] args) throws GeneralSecurityException, IOException, DocumentException {
    BouncyCastleProvider provider = new BouncyCastleProvider();
    Security.addProvider(provider);
    KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
    ks.load(new FileInputStream(KEYSTORE), PASSWORD);
    String alias = (String) ks.aliases().nextElement();
    PrivateKey pk = (PrivateKey) ks.getKey(alias, PASSWORD);
    Certificate[] chain = ks.getCertificateChain(alias);
    C2_01_SignHelloWorld app = new C2_01_SignHelloWorld();
    app.sign(SRC, String.format(DEST, 1), chain, pk, DigestAlgorithms.SHA1, provider.getName(), CryptoStandard.CADES, "Signed for Testing", "Universe");   // only change done was in this line to resolve this problem

}}

关于java - 对 PDF 进行数字签名时出错,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36589698/

相关文章:

java - 用逗号扫描字符串

java - 使用 Netbeans 检查 jDownloader

java - Jvm JIT 和 Hotspot - 有什么区别

java - 如何将 pdf/images 存储到 HBase 表

java - 使用 Java 将 HTML 转换为具有特殊字符的 PDF

java - 使用 System.loadLibrary() 时加载 dll 错误

r - 将r脚本的所有内容导出为pdf

java - 将 PDF 附加到来自 Android 应用程序的电子邮件 - 文件大小为零

c# - iTextSharp - 无法访问已关闭的流

java - itext库Document.add(Element element)方法在写入PDF文件时磁盘已满时返回true并且不会抛出任何异常