我在数字签名pdf哈希时遇到问题。这是我发现的实施和解决方案,它基于Bruno Lewagie的PDF文档数字签名。我们的想法是签署PDF哈希以避免将其带到客户端。 这是我提出的解决方案:
1。一个Applet Java,我用它来实例化一个令牌,获取它,然后访问它。
2。我用来获取我要签名的PDF的哈希的servlet java。我使用从applet获得的公钥来计算哈希值。获得此哈希后,我将其发送到applet(客户端)以使其签名。
小程序对生成的PDF哈希进行签名,然后将其返回到servlet,以插入到生成的PDF中并保存在给定目录中。 起初,它似乎可以正常工作,但在完成该过程并使用任何阅读器打开PDF后,我收到以下错误:“内部加密错误。错误代码:0x2727”。 值得一提的是,当我创建PDF并仅使用applet对其进行签名时,签名已正确验证。
代码: 与令牌交互并调用servlet - >
的Appletpublic void clientCallServlet(Certificate[] chain, PrivateKey pk) {
// Send to servlet cert
ObjectOutputStream out = new ObjectOutputStream(aConnection.getOutputStream());
out.writeObject(chain[0]);
out.flush();
out.close();
List<String> cookies = aConnection.getHeaderFields().get("Set-Cookie");
// received hash pdf from servlet
InputStream is = aConnection.getInputStream();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] data = new byte[256];
int read;
while ((read = is.read(data, 0, data.length)) != -1) {
baos.write(data, 0, read);
}
is.close();
// Sign bytes hash received
Signature sig = Signature.getInstance("SHA1withRSA");
sig.initSign(pk);
sig.update(baos.toByteArray());
data = sig.sign();
// End to sign
// send signed hash to servlet (second step)
...
...
// Write the signed bytes received from servlet in a PDF
File someFile = new File("c:/test1.pdf");
OutputStream fos = new FileOutputStream(someFile);
ByteArrayOutputStream baos1 = new ByteArrayOutputStream();
byte[] data1 = new byte[256];
int read1;
while ((read1 = retornoFirmado.read(data1)) != -1) {
baos1.write(data1, 0, read1);
}
retornoFirmado.close();
byte[] firmado = baos1.toByteArray();
fos.write(firmado);
fos.flush();
fos.close();
}
Servlet在收到证书的公钥后(由applet发送)读取PDF并计算其哈希值以将其返回给applet。
protected void doPost(HttpServletRequest request, HttpServletResponse response){
response.setContentType("application/octet-stream");
ObjectInputStream resultStream = null;
resultStream = new ObjectInputStream(request.getInputStream());
Certificate[] chain = new Certificate[1];
chain[0] = (Certificate) resultStream.readObject();
resultStream.close();
PdfReader reader = new PdfReader("c:/test.pdf");
ByteArrayOutputStream baos = new ByteArrayOutputStream();
PdfStamper stamper = PdfStamper.createSignature(reader, baos, '\0');
PdfSignatureAppearance sap = stamper.getSignatureAppearance();
sap.setReason("Test");
sap.setLocation("On a server!");
sap.setVisibleSignature(new Rectangle(36, 748, 144, 780), 1, "sig");
sap.setCertificate(chain[0]);
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 digest = new BouncyCastleDigest();
ExternalDigest externalDigest = new ExternalDigest() {
@Override
public MessageDigest getMessageDigest(String hashAlgorithm) throws GeneralSecurityException {
return DigestAlgorithms.getMessageDigest(hashAlgorithm, null);
}
};
PdfPKCS7 sgn = new PdfPKCS7(null, chain, "SHA1", null, externalDigest, false);
InputStream data = sap.getRangeStream();
byte hash[] = DigestAlgorithms.digest(data, externalDigest.getMessageDigest("RSA"));
Calendar cal = Calendar.getInstance();
byte[] sh = sgn.getAuthenticatedAttributeBytes(hash, cal, null, null, CryptoStandard.CMS);
HttpSession session = request.getSession(true);
session.setAttribute("sgn", sgn);
session.setAttribute("hash", hash);
session.setAttribute("cal", cal);
session.setAttribute("sap", sap);
session.setAttribute("baos", baos);
OutputStream os = response.getOutputStream();
os.write(sh, 0, sh.length);
os.flush();
os.close(); }
签署PDF后,再次从applet调用servlet,在生成的PDF - &gt;
中插入签名的哈希值protected void doPost(HttpServletRequest request, HttpServletResponse response){
response.setHeader("Cache-Control", "must-revalidate, post-check=0, pre-check=0");
response.setHeader("Pragma", "public");
response.setContentType("application/pdf");
HttpSession session = request.getSession(false);
PdfPKCS7 sgn = (PdfPKCS7) session.getAttribute("sgn");
byte[] hash = (byte[]) session.getAttribute("hash");
Calendar cal = (Calendar) session.getAttribute("cal");
PdfSignatureAppearance sap = (PdfSignatureAppearance) session.getAttribute("sap");
ByteArrayOutputStream os = (ByteArrayOutputStream) session.getAttribute("baos");
session.invalidate();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
InputStream is = request.getInputStream();
int read;
byte[] data = new byte[256];
while ((read = is.read(data, 0, data.length)) != -1) {
baos.write(data, 0, read);
}
sgn.setExternalDigest(baos.toByteArray(), null, "RSA");
byte[] encodedSig = sgn.getEncodedPKCS7(hash, cal, null, null, null, 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);
}
byte[] pdf = os.toByteArray();
//test save sign pdf
OutputStream out = new FileOutputStream("c:/out1.pdf");
out.write(pdf);
out.close();
OutputStream sos = response.getOutputStream();
sos.write(pdf, 0, pdf.length);
sos.flush();
sos.close();
这些是我使用的库的版本: