[英]iTextSharp - How to get PDF content for signing and then sign at a later time
我正在開發一個客戶端 - 服務器應用程序,客戶端必須使用簽名對PDF文檔進行簽名並將其上載到服務器。 由於客戶端無法將簽名嵌入到PDF中,因此它們只能讀取原始字節並以原始字節的形式生成簽名,這使得任務變得復雜。
我正在嘗試實現以下工作流程:
我發現了一些提取字節的代碼示例,用於簽名並將簽名字節嵌入到PDF中( 這是我正在使用的主要示例 )。
問題是這個示例執行一個程序中的所有步驟,它在獲取文檔哈希后立即嵌入簽名而不關閉PdfStamper
。 我需要的是在添加簽名字段並獲取sha.Hash
之后保存文檔的某種方式,然后在稍后的某個時間(當服務器收到計算的簽名時)打開文檔並將簽名值嵌入到PDF中。
您能否建議一種修改此代碼的方法,以便步驟(2)和(4)可以獨立,並且不需要PdfReader
和PdfStamper
共享實例?
自己搞清楚了。 這段代碼向我指出了正確的方向。
原來服務器上的進程必須如下:
相關的服務器代碼:
public static byte[] GetBytesToSign(string unsignedPdf, string tempPdf, string signatureFieldName)
{
using (PdfReader reader = new PdfReader(unsignedPdf))
{
using (FileStream os = File.OpenWrite(tempPdf))
{
PdfStamper stamper = PdfStamper.CreateSignature(reader, os, '\0');
PdfSignatureAppearance appearance = stamper.SignatureAppearance;
appearance.SetVisibleSignature(new Rectangle(36, 748, 144, 780), 1, signatureFieldName);
IExternalSignatureContainer external = new ExternalBlankSignatureContainer(PdfName.ADOBE_PPKMS, PdfName.ADBE_PKCS7_SHA1);
MakeSignature.SignExternalContainer(appearance, external, 8192);
return SHA1Managed.Create().ComputeHash(appearance.GetRangeStream());
}
}
}
public static void EmbedSignature(string tempPdf, string signedPdf, string signatureFieldName, byte[] signedBytes)
{
using (PdfReader reader = new PdfReader(tempPdf))
{
using (FileStream os = File.OpenWrite(signedPdf))
{
IExternalSignatureContainer external = new MyExternalSignatureContainer(signedBytes);
MakeSignature.SignDeferred(reader, signatureFieldName, os, external);
}
}
}
private class MyExternalSignatureContainer : IExternalSignatureContainer
{
private readonly byte[] signedBytes;
public MyExternalSignatureContainer(byte[] signedBytes)
{
this.signedBytes = signedBytes;
}
public byte[] Sign(Stream data)
{
return signedBytes;
}
public void ModifySigningDictionary(PdfDictionary signDic)
{
}
}
旁注 :在所有這些iText樣本中困擾我的是魔術數字的存在(如8192
這里),沒有任何評論。 這使得使用這個庫變得更加困難和煩人。
下面的答案來自我們關於數字簽名的白皮書 ,第4章,第4.3.3節使用在客戶端上創建的簽名在服務器上簽名文檔。 代碼示例在這里
所需的工作流程可視為3個主要步驟:
Presign:
必填:pdf,證書鏈
Serverside,設置簽名基礎結構,提取消息摘要並將摘要作為字節數組發送到客戶端
簽名:
必需:消息摘要為字節數組,私鑰
客戶端,將加密算法應用於消息摘要,以從散列生成簽名摘要,並將此簽名發送到服務器
代碼示例,iText5和C#:
Presign (服務器)
//hello :
//location of the pdf on the server
//or
//bytestream variable with teh pdf loaded in
//chain: certificate chain
// we create a reader and a stamper
PdfReader reader = new PdfReader(hello);
Stream baos = new MemoryStream();
PdfStamper stamper = PdfStamper.CreateSignature(reader, baos., '\0');
// we create the signature appearance
PdfSignatureAppearance sap = stamper.SignatureAppearance;
sap.Reason = "Test";
sap.Location = "On a server!";
sap.SetVisibleSignature ( new Rectangle(36, 748, 144, 780), 1, "sig");
sap.Certificate = chain[0];
// we create the signature infrastructure
PdfSignature dic = new PdfSignature(
PdfName.ADOBE_PPKLITE, PdfName.ADBE_PKCS7_DETACHED);
dic.Reason = sap.Reason;
dic.Location = sap.Location;
dic.Contact = sap.Contact;
dic.Date = new PdfDate(sap.SignDate);
sap.CryptoDictionary = dic;
Dictionary<PdfName, int> exc = new Dictionary<PdfName, int>();
exc.Add(PdfName.CONTENTS, (int)(8192 * 2 + 2));
sap.PreClose(exc);
PdfPKCS7 sgn = new PdfPKCS7(null, chain, "SHA256", false);
//Extract the bytes that need to be signed
Stream data = sap.GetRangeStream();
byte[] hash = DigestAlgorithms.Digest(data,"SHA256");
byte[] sh = sgn.getAuthenticatedAttributeBytes(hash,null, null, CryptoStandard.CMS);
//Store sgn, hash,sap and baos on the server
//...
//Send sh to client
簽名 (客戶)
// we receive a hash that needs to be signed
Stream istream = response.GetResponseStream();
MemoryStream baos = new MemoryStream();
data = new byte[0x100];
while ((read = istream.Read(data, 0, data.Length)) != 0)
baos.Write(data, 0, read);
istream.Close();
byte[] hash = baos.ToArray();
// we load our private key from the key store
Pkcs12Store store = new Pkcs12Store(new FileStream(KEYSTORE, FileMode.Open), PASSWORD);
String alias = "";
// searching for private key
foreach (string al in store.Aliases)
if (store.IsKeyEntry(al) && store.GetKey(al).Key.IsPrivate) {
alias = al;
break;
}
AsymmetricKeyEntry pk = store.GetKey(alias);
// we sign the hash received from the server
ISigner sig = SignerUtilities.GetSigner("SHA256withRSA");
sig.Init(true, pk.Key);
sig.BlockUpdate(hash, 0, hash.Length);
data = sig.GenerateSignature();
// we make a connection to the PostSign Servlet
request = (HttpWebRequest)WebRequest.Create(POST);
request.Headers.Add(HttpRequestHeader.Cookie,cookies.Split(";".ToCharArray(), 2)[0]);
request.Method = "POST";
// we upload the signed bytes
os = request.GetRequestStream();
os.Write(data, 0, data.Length);
os.Flush();
os.Close();
郵政署 ( 服務員 )
// we read the signed bytes
MemoryStream baos = new MemoryStream();
Stream InputStream iStream = req.GetInputStream();
int read;
byte[] data = new byte[256];
while ((read = iStream.read(data, 0, data.Length)) != -1) {
baos.Write(data, 0, read);
}
// we complete the PDF signing process
sgn.SetExternalDigest(baos.ToArray(), null, "RSA");
byte[] encodedSig = sgn.getEncodedPKCS7(hash, cal, null,
null, null, CryptoStandard.CMS);
byte[] paddedSig = new byte[8192];
paddedSig.
encodedSig.CopyTo(paddedSig, 0);
PdfDictionary dic2 = new PdfDictionary();
dic2.Put(PdfName.CONTENTS, new PdfString(paddedSig).SetHexWriting(true));
try
{
sap.close(dic2);
}
catch (DocumentException e)
{
throw new IOException(e);
}
我省略了大多數客戶端 - 服務器通信代碼,並專注於簽名邏輯。 我還沒有徹底測試這些片段,因為我必須將它們從java代碼轉換而且我目前沒有客戶端 - 服務器設置來測試它們,因此復制和運行需要您自擔風險。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.