英文:
Digitally sign a pdf file in 2 steps with iText 7, result is corrupted file
问题
我会为您翻译代码部分:
首先,我会翻译计算哈希和签名的部分:
public static byte[] ComputeHash(string source, string temp, X509Certificate[] chains, string reason, string location, int qtySigns, int pageNumber)
{
IList<ICrlClient> crlList = new List<ICrlClient>();
crlList.Add(new CrlClientOnline(chains));
using (var reader = new PdfReader(source))
{
using (var os = new FileStream(temp, FileMode.OpenOrCreate, FileAccess.Write))
{
var signer = new PdfSigner(reader, os, new StampingProperties().UseAppendMode());
var signatureAppearance = signer.GetSignatureAppearance();
signatureAppearance
.SetReason(reason)
.SetLocation(location)
.SetPageNumber(pageNumber)
.SetRenderingMode(PdfSignatureAppearance.RenderingMode.NAME_AND_DESCRIPTION)
.SetCertificate(chains[0])
.SetPageRect(new iText.Kernel.Geom.Rectangle(36, 20, 144, 53));
var container = new EmptySignatureContainer(PdfName.Adobe_PPKLite, PdfName.Adbe_pkcs7_detached);
signer.SignExternalContainer(container, 8192);
byte[] hash = container.Hash;
return hash;
}
}
}
以下是签名容器部分的翻译:
public class EmptySignatureContainer : IExternalSignatureContainer
{
private PdfDictionary _sigDic;
public byte[] Hash;
public byte[] Sign(Stream data)
{
string hashAlgorithm = "SHA256";
try
{
this.Hash = DigestAlgorithms.Digest(data, hashAlgorithm);
}
catch (IOException e)
{
throw new GeneralSecurityException("EmptySignatureContainer signing exception", e);
}
return new byte[0];
}
public void ModifySigningDictionary(PdfDictionary signDic)
{
signDic.PutAll(_sigDic);
}
public EmptySignatureContainer(PdfName filter, PdfName subFilter)
{
_sigDic = new PdfDictionary();
_sigDic.Put(PdfName.Filter, filter);
_sigDic.Put(PdfName.SubFilter, subFilter);
}
}
接下来是计算哈希的部分:
var hash = ComputeHash(inputFile, tempFile, chain, "sign-reason", "", 1, 1);
PdfPKCS7 sgn = new PdfPKCS7(null, chain, "SHA256", false);
var authenAttrBytes = sgn.GetAuthenticatedAttributeBytes(hash, PdfSigner.CryptoStandard.CMS, null, null);
var computedHash = SHA256Managed.Create().ComputeHash(authenAttrBytes);
最后,是签名和添加签名的部分:
Sign(tempFile, outputFile, chain, authenAttrBytes, signedHash);
签名方法的翻译如下:
public static void Sign(string tempFile, string targetFile, X509Certificate[] chains, byte[] hash, byte[] signedHash)
{
using (PdfReader reader = new PdfReader(tempFile))
{
using (FileStream outStream = System.IO.File.OpenWrite(targetFile))
{
var signedContainer = new SignedSignatureContainer(hash, signedHash, chains);
var signer = new PdfSigner(reader, outStream, new StampingProperties());
signer.SignExternalContainer(signedContainer, 8192);
}
}
}
最后是签名容器的翻译:
public class SignedSignatureContainer : IExternalSignatureContainer
{
public byte[] Hash { get; set; }
public byte[] SignedHash { get; set; }
public X509Certificate[] CertChains { get; set; }
public SignedSignatureContainer(byte[] hash, byte[] signedHash, X509Certificate[] certCertChains)
{
this.Hash = hash;
this.SignedHash = signedHash;
this.CertChains = certCertChains;
}
public byte[] Sign(Stream data)
{
var sgn = new PdfPKCS7(null, CertChains, "SHA256", false);
sgn.SetExternalDigest(this.SignedHash, null, "RSA");
return sgn.GetEncodedPKCS7(this.Hash, CryptoStandard.CMS, null, null, null );
}
public void ModifySigningDictionary(PdfDictionary signDic)
{
signDic.Put(PdfName.Filter, PdfName.Adobe_PPKLite);
signDic.Put(PdfName.SubFilter, PdfName.Adbe_pkcs7_detached);
}
}
希望这有助于您理解代码。如果您有其他问题,请随时提出。
英文:
I'm trying to digitally sign a PDF file in two steps: compute hash, sign hash, and then merge the signed hash to the output file. The library that I'm using is iText 7.
However, the result is a corrupted file and cannot be opened.
What is wrong?
First I'm computing the hash and sign the input file with an empty signature container:
public static byte[] ComputeHash(string source, string temp, X509Certificate[] chains, string reason, string location, int qtySigns, int pageNumber)
{
IList<ICrlClient> crlList = new List<ICrlClient>();
crlList.Add(new CrlClientOnline(chains));
using (var reader = new PdfReader(source))
{
using (var os = new FileStream(temp, FileMode.OpenOrCreate, FileAccess.Write))
{
var signer = new PdfSigner(reader, os, new StampingProperties().UseAppendMode());
var signatureAppearance = signer.GetSignatureAppearance();
signatureAppearance
.SetReason(reason)
.SetLocation(location)
.SetPageNumber(pageNumber)
.SetRenderingMode(PdfSignatureAppearance.RenderingMode.NAME_AND_DESCRIPTION)
.SetCertificate(chains[0])
.SetPageRect(new iText.Kernel.Geom.Rectangle(36, 20, 144, 53));
var container = new EmptySignatureContainer(PdfName.Adobe_PPKLite, PdfName.Adbe_pkcs7_detached);
signer.SignExternalContainer(container, 8192);
byte[] hash = container.Hash;
return hash;
}
}
}
Here is the container:
public class EmptySignatureContainer : IExternalSignatureContainer
{
private PdfDictionary _sigDic;
public byte[] Hash;
public byte[] Sign(Stream data)
{
string hashAlgorithm = "SHA256";
try
{
this.Hash = DigestAlgorithms.Digest(data, hashAlgorithm);
}
catch (IOException e)
{
throw new GeneralSecurityException("EmptySignatureContainer signing exception", e);
}
return new byte[0];
}
public void ModifySigningDictionary(PdfDictionary signDic)
{
signDic.PutAll(_sigDic);
}
public EmptySignatureContainer(PdfName filter, PdfName subFilter)
{
_sigDic = new PdfDictionary();
_sigDic.Put(PdfName.Filter, filter);
_sigDic.Put(PdfName.SubFilter, subFilter);
}
}
After that, I get the authenticated attribute bytes and compute its hash:
var hash = ComputeHash(inputFile, tempFile, chain, "sign-reason", "", 1, 1);
PdfPKCS7 sgn = new PdfPKCS7(null, chain, "SHA256", false);
var authenAttrBytes = sgn.GetAuthenticatedAttributeBytes(hash, PdfSigner.CryptoStandard.CMS, null, null);
var computedHash = SHA256Managed.Create().ComputeHash(authenAttrBytes);`
The computedHash then will be sent to the hsm server to sign.
The signed hash then will be added back to the temporary file as following code:
Sign(tempFile, outputFile, chain, authenAttrBytes, signedHash);
And here is the Sign method:
public static void Sign(string tempFile, string targetFile, X509Certificate[] chains, byte[] hash, byte[] signedHash)
{
using (PdfReader reader = new PdfReader(tempFile))
{
using (FileStream outStream = System.IO.File.OpenWrite(targetFile))
{
var signedContainer = new SignedSignatureContainer(hash, signedHash, chains);
var signer = new PdfSigner(reader, outStream, new StampingProperties());
signer.SignExternalContainer(signedContainer, 8192);
}
}
}
The Signed container as following:
public class SignedSignatureContainer : IExternalSignatureContainer
{
public byte[] Hash { get; set; }
public byte[] SignedHash { get; set; }
public X509Certificate[] CertChains { get; set; }
public SignedSignatureContainer(byte[] hash, byte[] signedHash, X509Certificate[] certCertChains)
{
this.Hash = hash;
this.SignedHash = signedHash;
this.CertChains = certCertChains;
}
public byte[] Sign(Stream data)
{
var sgn = new PdfPKCS7(null, CertChains, "SHA256", false);
sgn.SetExternalDigest(this.SignedHash, null, "RSA");
return sgn.GetEncodedPKCS7(this.Hash, CryptoStandard.CMS, null, null, null );
}
public void ModifySigningDictionary(PdfDictionary signDic)
{
signDic.Put(PdfName.Filter, PdfName.Adobe_PPKLite);
signDic.Put(PdfName.SubFilter, PdfName.Adbe_pkcs7_detached);
}
}
答案1
得分: 0
在你的Sign
方法中,你使用了PdfSigner
方法的SignExternalContainer
:
var signedContainer = new SignedSignatureContainer(hash, signedHash, chains);
var signer = new PdfSigner(reader, outStream, new StampingProperties());
signer.SignExternalContainer(signedContainer, 8192);
但这不会填充原先准备好的签名!相反,它会创建另一个签名字段,并使用原始签名的签名来填充它。
请改用静态的PdfSigner
方法SignDeferred
:
/// <summary>Signs a PDF where space was already reserved.</summary>
/// <param name="document">the original PDF</param>
/// <param name="fieldName">the field to sign. It must be the last field</param>
/// <param name="outs">the output PDF</param>
/// <param name="externalSignatureContainer">
/// the signature container doing the actual signing. Only the
/// method ExternalSignatureContainer.sign is used
/// </param>
public static void SignDeferred(PdfDocument document, String fieldName, Stream outs,
IExternalSignatureContainer externalSignatureContainer)
英文:
In your Sign
method you use the PdfSigner
method SignExternalContainer
:
var signedContainer = new SignedSignatureContainer(hash, signedHash, chains);
var signer = new PdfSigner(reader, outStream, new StampingProperties());
signer.SignExternalContainer(signedContainer, 8192);
But that does not fill in the originally prepared signature! Instead this creates another signature field and fills it with a signature for the original one.
Use the static PdfSigner
method SignDeferred
instead:
/// <summary>Signs a PDF where space was already reserved.</summary>
/// <param name="document">the original PDF</param>
/// <param name="fieldName">the field to sign. It must be the last field</param>
/// <param name="outs">the output PDF</param>
/// <param name="externalSignatureContainer">
/// the signature container doing the actual signing. Only the
/// method ExternalSignatureContainer.sign is used
/// </param>
public static void SignDeferred(PdfDocument document, String fieldName, Stream outs,
IExternalSignatureContainer externalSignatureContainer)
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论