I am trying to implement digital signature in web application as example provided by by Bruno Lowagie in White Paper.
4.3.3 Signing a document on the server using a signature created on the client
Pre-signing— the client asks the server for a hash.
Post-signing— the client sends the signed bytes to the server.
every thing is working fine in this example but when we are try to open pdf after signing it is giving an error Error during signature verification. Error encountered while validating: Internal cryptographic library error. Error Code: 0x2726
Here is my code:
client:
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!");
presign 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---------------");
post sign servlet:
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");
Here I am doing one extra thing, I am encoding certificate chain to base64 before sending to presign servlet.