Java 在Client/Web服务器体系结构中实现数字签名
我正试图在web应用程序中实现数字签名,如Bruno Lowagie在白皮书中提供的示例 4.3.3使用在客户机上创建的签名在服务器上对文档进行签名 预签名-客户端向服务器请求哈希 签名后-客户端将签名的字节发送到服务器 在这个例子中,每件事都很好,但是当我们尝试在签名后打开pdf时 它在签名验证过程中给出了一个错误。验证时遇到错误:内部加密库错误。错误代码:0x2726 这是我的代码:Java 在Client/Web服务器体系结构中实现数字签名,java,pdf,servlets,itext,digital-signature,Java,Pdf,Servlets,Itext,Digital Signature,我正试图在web应用程序中实现数字签名,如Bruno Lowagie在白皮书中提供的示例 4.3.3使用在客户机上创建的签名在服务器上对文档进行签名 预签名-客户端向服务器请求哈希 签名后-客户端将签名的字节发送到服务器 在这个例子中,每件事都很好,但是当我们尝试在签名后打开pdf时 它在签名验证过程中给出了一个错误。验证时遇到错误:内部加密库错误。错误代码:0x2726 这是我的代码: Certificate[] chain = decodeX509CertChainToBase6
Certificate[] chain = decodeX509CertChainToBase64(cert);
// we create a reader and a stamper
ServletContext context = getServletContext();
String fullPath = context.getRealPath("/WEB-INF/result/hello.pdf");
PdfReader reader = new PdfReader(fullPath);
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("On a server!");
sap.setVisibleSignature(new Rectangle(72,737,400,780), 1, "sig");
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"));
Calendar cal = Calendar.getInstance();
byte[] sh = sgn.getAuthenticatedAttributeBytes(hash, cal, null, null, CryptoStandard.CMS);
// We store the objects we'll need for post signing in a session
HttpSession session = req.getSession(true);
session.setAttribute("sgn", sgn);
session.setAttribute("hash", hash);
session.setAttribute("cal", cal);
session.setAttribute("sap", sap);
session.setAttribute("baos", baos);
// we write the hash that needs to be signed to the HttpResponse output
OutputStream os = resp.getOutputStream();
os.write(sh, 0, sh.length);
os.flush();
os.close();
}
catch(Exception ex)
{
ex.printStackTrace();
}
System.out.println("end of pre sign servelet---------------");
try
{
// we get the objects we need for postsigning from the session
System.out.println("call post servelet1");
HttpSession session = req.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();
// we read the signed bytes
ByteArrayOutputStream baos = new ByteArrayOutputStream();
InputStream is = req.getInputStream();
int read;
byte[] data = new byte[256];
while ((read = is.read(data, 0, data.length)) != -1) {
baos.write(data, 0, read);
}
// we complete the PDF signing process
sgn.setExternalDigest(baos.toByteArray(), null, "RSA");
byte[] encodedSig = sgn.getEncodedPKCS7(hash, cal, null, null, null, CryptoStandard.CADES);
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));
sap.close(dic2);
// we write the signed document to the HttpResponse output stream
// let's write the file in memory to a file anyway
ServletContext context = getServletContext();
String fullPath = context.getRealPath("/WEB-INF/result/sign.pdf");
byte[] pdf = os.toByteArray();
OutputStream sos = resp.getOutputStream();
sos.write(pdf, 0, pdf.length);
sos.flush();
sos.close();
/*OutputStream sos = new FileOutputStream(fullPath);
os.writeTo(sos);
sos.flush();
sos.close();*/
}
catch(Exception ex)
{
ex.printStackTrace();
}
System.out.println("call post servelet2");
客户:
KeyStore eks = loadKeyStoreFromSmartCard("abc@123");
// Check if X.509 certification chain is available
Certificate[] certChain = new X509Certificate[1];
certChain[0] = getcert_eToken(null, eks);
String strCertificate = encodeX509CertChainToBase64(certChain);
ByteArrayOutputStream byteStream = new ByteArrayOutputStream(8192);
PrintWriter out = new PrintWriter(byteStream, true);
String postData = "certChain=" + strCertificate;
try {
HttpURLConnection connection = null;
URL dataURL = null;
dataURL = new URL("http://localhost:8085/Digital-Server/PreSignservlet");
connection = (HttpURLConnection) dataURL.openConnection();
connection.setRequestProperty("User-Agent",
"Mozilla/4.0 (compatible; MSIE 6.0; Windows 2000)");
connection.setFollowRedirects(true);
connection.setDoOutput(true);
connection.setDoInput(true);
connection.setUseCaches(false);
connection.setAllowUserInteraction(false);
connection.setRequestProperty("Content-Type",
"application/x-www-form-urlencoded");
connection.setRequestProperty("Content-Language", "en-US");
connection.setRequestProperty("Cookie", cookie);
connection.connect();
out.print(postData);
out.flush();
out.close();
byteStream.writeTo(connection.getOutputStream());
InputStream in = connection.getInputStream();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int read;
byte[] data = new byte[256];
while ((read = in.read(data)) != -1) {
baos.write(data, 0, read);
}
byte[] hash = baos.toByteArray();
PrivateKey privateKey = getprivate_eToken(null, eks);
// we sign the bytes received from the server
Signature sig = Signature.getInstance("SHA256withRSA");
sig.initSign(privateKey);
sig.update(hash);
data = sig.sign();
// --------------------------------------------
connection.disconnect();
in.close();
//Calling Post Sign Servelet
dataURL = new URL("http://localhost:8085/Digital-Server/PostSignservlet");
connection = (HttpURLConnection) dataURL.openConnection();
connection.setRequestProperty("User-Agent",
"Mozilla/4.0 (compatible; MSIE 6.0; Windows 2000)");
connection.setFollowRedirects(true);
connection.setDoOutput(true);
connection.setDoInput(true);
connection.setUseCaches(false);
connection.setAllowUserInteraction(false);
connection.setRequestProperty("Content-Type",
"application/x-www-form-urlencoded");
connection.setRequestProperty("Content-Language", "en-US");
connection.setRequestProperty("Cookie", cookie);
connection.connect();
out.flush();
out.close();
byteStream.writeTo(connection.getOutputStream());
byteStream.write(data);
in = connection.getInputStream();
OutputStream outputStream = new FileOutputStream(
"D:\\Digital Signature\\Digital-Server\\WebContent\\WEB-INF\\result\\jaihanuman.pdf");
// int read = 0;
byte[] bytes = new byte[8192];
while ((read = in.read(bytes)) != -1) {
outputStream.write(bytes, 0, read);
}
System.out.println("Done!");
预设计servlet:
Certificate[] chain = decodeX509CertChainToBase64(cert);
// we create a reader and a stamper
ServletContext context = getServletContext();
String fullPath = context.getRealPath("/WEB-INF/result/hello.pdf");
PdfReader reader = new PdfReader(fullPath);
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("On a server!");
sap.setVisibleSignature(new Rectangle(72,737,400,780), 1, "sig");
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"));
Calendar cal = Calendar.getInstance();
byte[] sh = sgn.getAuthenticatedAttributeBytes(hash, cal, null, null, CryptoStandard.CMS);
// We store the objects we'll need for post signing in a session
HttpSession session = req.getSession(true);
session.setAttribute("sgn", sgn);
session.setAttribute("hash", hash);
session.setAttribute("cal", cal);
session.setAttribute("sap", sap);
session.setAttribute("baos", baos);
// we write the hash that needs to be signed to the HttpResponse output
OutputStream os = resp.getOutputStream();
os.write(sh, 0, sh.length);
os.flush();
os.close();
}
catch(Exception ex)
{
ex.printStackTrace();
}
System.out.println("end of pre sign servelet---------------");
try
{
// we get the objects we need for postsigning from the session
System.out.println("call post servelet1");
HttpSession session = req.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();
// we read the signed bytes
ByteArrayOutputStream baos = new ByteArrayOutputStream();
InputStream is = req.getInputStream();
int read;
byte[] data = new byte[256];
while ((read = is.read(data, 0, data.length)) != -1) {
baos.write(data, 0, read);
}
// we complete the PDF signing process
sgn.setExternalDigest(baos.toByteArray(), null, "RSA");
byte[] encodedSig = sgn.getEncodedPKCS7(hash, cal, null, null, null, CryptoStandard.CADES);
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));
sap.close(dic2);
// we write the signed document to the HttpResponse output stream
// let's write the file in memory to a file anyway
ServletContext context = getServletContext();
String fullPath = context.getRealPath("/WEB-INF/result/sign.pdf");
byte[] pdf = os.toByteArray();
OutputStream sos = resp.getOutputStream();
sos.write(pdf, 0, pdf.length);
sos.flush();
sos.close();
/*OutputStream sos = new FileOutputStream(fullPath);
os.writeTo(sos);
sos.flush();
sos.close();*/
}
catch(Exception ex)
{
ex.printStackTrace();
}
System.out.println("call post servelet2");
在这里,我做了一件额外的事情,在发送之前,我将证书链编码为base64
预先设计servlet 您的代码有些混乱: 客户端代码在
certChain
中检索仅签名者证书的证书链,base64将其编码为strCertificate
,并在其前面加上“certChain=”前缀,然后将该字符串放入postData
。然后,它打开一个到PreSignservlet
的连接,并使用中介ByteArrayOutputStream ByTestStream
(为什么不简单地将postData.getBytes()
写入连接.getOutputStream()
)以复杂的方式将数据发送到post
不幸的是,您既没有关闭输出流,也没有添加内容长度头。因此,servlet可能难以识别输入的结尾。但这似乎不是当前的问题
现在,将servlet返回的数据原样(即不解码)并将其输入签名创建。然后打开到PostSignservlet
的连接,将签名字节发送到
到目前为止,这是有道理的
但是您现在将以前发送的信息(编码证书)发送到该servlet,然后将签名添加到本地byteStream
,而不是签名数据
为什么不直接将数据中的签名写入连接.getOutputStream()
最终,您将检索servlet的输出作为结果PDF
将证书而不是实际签名写入PostSignservlet
可以解释为什么结果PDF中的CMS签名容器SignerInfo包含一个“签名”值,该值类似于“certChain=MIIDKT…”,例如,与base64编码证书类似。请共享一个由安装程序签名的pdf示例。pdf的链接是:Ok,CMS签名容器SignerInfo包含一个“签名”值,该值类似于“certChain=MIIDKT…”。这显然不是一个签名值。稍后我将查看您的代码,但显然有两个问题:1。您将证书链放在必须放置签名和2的位置。您在需要未编码数据的地方使用base64编码。谢谢您的建议。好的,我正在尝试修改您建议的代码。我想知道如何将证书从eToken发送到JavaServlet,而无需对base 64进行编码。还有其他方法吗..您知道如何传输字节数组。因此,只需发送getcert\u eToken(null,eks).getEncoded()
,并在servlet中使用CertificateFactory.getInstance(“X.509”).generateCertificate(流内)
再次获得证书
实例。thnx.gr8这是我的第一篇帖子,我从没想过我会这么早得到回复。在实施了你的建议之后。现在密码库出错了。错误代码:0x2726已消失。但现在它显示一条消息,说明自应用签名以来文档已被更改或损坏。签名pdf上未显示任何图标,甚至签名无效的交叉图标。当我看到pdf元数据时,有那么多0000000个类似的数字。那么这是什么原因呢?修改后的pdf: