Reputation: 58
I need to dynamically create a PDF file, so I use a MemoryStream and iTextSharp for editing the PDF. But I want to digitally sign the PDF file before sending it to the browser. I've seen in a sample project that iTextSharp has a PDFSigner class, which mostly does all the work. But as I've seen it, it works like this:
PDFSigner signer = new PDFSigner(inputFileName, outputFileName, certificate, metaData);
signer.Sign(......)
But with MemoryStream, I can't provide a physical file name for input or output either. My code is below:
MemoryStream memoryStream = new MemoryStream();
var document = new Document(iTextSharp.text.PageSize.LETTER, 40, 40, 60, 40);
PdfWriter.GetInstance(document, memoryStream).CloseStream = false;
document.Open();
PopulatePDF(document, someId);
document.Close();
byte[] byteArray = memoryStream.ToArray();
memoryStream.Write(byteArray, 0, byteArray.Length);
memoryStream.Position = 0;
return new FileStreamResult(memoryStream, "application/pdf");
My question is how can I use a PDFSigner object to sign the document, giving that PDFSigner takes only strings - filenames as arguments.
Upvotes: 1
Views: 14927
Reputation: 77606
As Chris Haas explained, there is no class named PdfSigner
in iText. You may be using an unofficial version of iText, in which case you need to make sure you are applying the digital signature correctly.
You can read more about correct digital signatures in the book I wrote about digital signatures.
Basically, this is how you'd sign using a PKCS#12 keystore:
Pkcs12Store store = new Pkcs12Store(new FileStream(KEYSTORE, FileMode.Open), PASSWORD);
String alias = "";
ICollection<X509Certificate> chain = new List<X509Certificate>();
// 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);
foreach (X509CertificateEntry c in store.GetCertificateChain(alias))
chain.Add(c.Certificate);
RsaPrivateCrtKeyParameters parameters = pk.Key as RsaPrivateCrtKeyParameters;
PdfReader reader = new PdfReader(src);
FileStream os = new FileStream(dest, FileMode.Create);
PdfStamper stamper = PdfStamper.CreateSignature(reader, os, '\0');
// Creating the appearance
PdfSignatureAppearance appearance = stamper.SignatureAppearance;
appearance.Reason = "My reason for signing";
appearance.Location = "The middle of nowhere";
appearance.SetVisibleSignature(new Rectangle(36, 748, 144, 780), 1, "sig");
// Creating the signature
IExternalSignature pks = new PrivateKeySignature(parameters, DigestAlgorithms.SHA256);
MakeSignature.SignDetached(appearance, pks, chain, null, null, null, 0, CryptoStandard.CMS);
Obviously, you want to replace the null
values with implementations that get revocation info and maybe even allow for a timestamp, but that's not your question.
Your question is: can I provide a PDF that only exists in memory? The answer is yes: the src
parameter when creating the PdfReader
instance can be a path to a file on disk, but it can also be a byte[]
that is obtained from a MemoryStream
.
Upvotes: 4