gpt4 book ai didi

java - 在客户端/Web 服务器架构中实现数字签名

转载 作者:行者123 更新时间:2023-11-30 11:21:31 25 4
gpt4 key购买 nike

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

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

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

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

在此示例中一切正常,但是当我们在签名后尝试打开 pdf 时它在签名验证期间出现错误错误。验证时遇到错误:内部加密库错误。错误代码: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");

这里我做了一件额外的事情,我在发送之前将证书链编码为 base64预签名servlet。

最佳答案

您的代码有些困惑:

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

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

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

到目前为止它是有道理的。

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

为什么不直接将data中的签名写到connection.getOutputStream()中呢?

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

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

关于java - 在客户端/Web 服务器架构中实现数字签名,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22189077/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com