繁体   English   中英

在客户端/ Web服务器体系结构中实现数字签名

[英]Implement Digital Signature in Client /Web Server Architecture

我正在尝试在Web应用程序中实现数字签名,这是Bruno Lowagie在白皮书中提供的示例。

4.3.3使用在客户端上创建的签名在服务器上签名文档

预签名-客户端向服务器请求哈希。

后签名-客户端将已签名的字节发送到服务器。

在此示例中,一切正常,但是当我们在签名后尝试打开pdf 时,签名验证过程中出现错误Error。 验证时遇到错误:内部密码库错误。 错误代码:0x2726

这是我的代码:

客户:

  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---------------");

邮政标志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");  

在这里,我要做的另一件事是,在发送证书到预签名servlet之前,我将证书链编码为base64

您的代码有些混乱:

客户端代码在certChain检索仅签署者证书的证书链,将base64编码为strCertificate ,将其前缀为“ certChain =”并将该字符串放入postData 然后,它打开与PreSignservlet的连接,并使用中间的ByteArrayOutputStream byteStream以复杂的方式发送数据以进行发布(为什么不将postData.getBytes()简单地写入connection.getOutputStream() )。

不幸的是,您既没有关闭输出流,也没有添加内容长度标头。 因此,该servlet可能难以识别输入结束。 但这似乎不是当前的问题。

现在,您按原样获取servlet返回的数据(即不解码),并将其输入到签名创建中。 然后,打开与PostSignservlet的连接以将签名字节发送到。

到目前为止,这是有道理的。

但是,现在您可以将签名(之前已发送的信息)(编码的证书)发送给该servlet,而不是签名数据,然后byteStream签名添加到本地byteStream

为什么不简单地将签名写入dataconnection.getOutputStream()

最终,您将servlet的输出作为结果PDF检索。

您将证书而不是实际签名写入PostSignservlet将会解释为什么结果PDF中的CMS签名容器SignerInfo包含一个看起来像此“ certChain = MIIDKT ...”的“签名”值,即类似于您的base64编码证书。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM