Hi,
i need to sign pdf with timestamp and ocsp, but as long as i add second digest in this line:
pdfPkcs7.GetEncodedPKCS7(sha.Hash, cal, tsc, ocsp);
, acrobat reader says "document altered". If the line is like this:
pdfPkcs7.GetEncodedPKCS7(null, cal, tsc, ocsp);
acrobat reader is happy, but naturaly ocsp isn't added.
may be someone notices something wrong in my code?
public X509Certificate2 GetCertificate()
{
var st = new X509Store(parameters.CertificateStore, parameters.CertificateLocation);
st.Open(OpenFlags.ReadOnly);
var col = st.Certificates;
X509Certificate2 card = null;
var sel = X509Certificate2UI.SelectFromCollection(col, parameters.ChooseCertificateTitle, parameters.ChooseCertificateMessage, X509SelectionFlag.SingleSelection);
if (sel.Count > 0)
{
var en = sel.GetEnumerator();
en.MoveNext();
card = en.Current;
}
st.Close();
return card;
}
static public byte[] SignMessage(Byte[] msg, X509Certificate2 signerCert, bool detached)
{
var contentInfo = new ContentInfo(msg);
var signedCms = new SignedCms(contentInfo, detached);
var cmsSigner = new CmsSigner(signerCert)
{
IncludeOption = X509IncludeOption.EndCertOnly
};
signedCms.ComputeSignature(cmsSigner, false);
return signedCms.Encode();
}
public void SignDetached(string inputFileName, string outputFileName)
{
var certificate = GetCertificate();
var chain = GetChain(certificate);
var pdfReader = new PdfReader(inputFileName);
var pdfStamper = PdfStamper.CreateSignature(pdfReader, new FileStream(outputFileName, FileMode.Create), '\0');
var signatureAppearance = pdfStamper.SignatureAppearance;
var pageSize = pdfReader.GetPageSizeWithRotation(1);
signatureAppearance.SetVisibleSignature(new Rectangle(pageSize.Width - 200, pageSize.Height - 20, pageSize.Width, pageSize.Height), 1, null);
signatureAppearance.SignDate = DateTime.Now;
signatureAppearance.SetCrypto(null, chain, null, null);
signatureAppearance.Reason = parameters.SignReason;
signatureAppearance.Location = parameters.SignLocation;
signatureAppearance.Acro6Layers = true;
signatureAppearance.Render = PdfSignatureAppearance.SignatureRender.NameAndDescription;
var dic = new PdfSignature(PdfName.ADOBE_PPKLITE, PdfName.ADBE_PKCS7_DETACHED)
{
Date = new PdfDate(signatureAppearance.SignDate),
Name = PdfPKCS7.GetSubjectFields(chain[0]).GetField("CN")
};
if (signatureAppearance.Reason != null)
dic.Reason = signatureAppearance.Reason;
if (signatureAppearance.Location != null)
dic.Location = signatureAppearance.Location;
signatureAppearance.CryptoDictionary = dic;
const int csize = 15000;
var exc = new Hashtable();
exc[PdfName.CONTENTS] = csize * 2 + 2;
signatureAppearance.PreClose(exc);
var sha = new SHA1CryptoServiceProvider();
var s = signatureAppearance.RangeStream;
var memoryStream = new MemoryStream();
int read;
var buffer = new byte[8192];
while ((read = s.Read(buffer, 0, 8192)) > 0)
{
memoryStream.Write(buffer, 0, read);
}
sha.ComputeHash(memoryStream.ToArray());
var pk2 = SignMessage(memoryStream.ToArray(), certificate, true);
var pdfPkcs7forDigests = new PdfPKCS7(pk2);
var digest = pdfPkcs7forDigests.Digest;
var rsaData = pdfPkcs7forDigests.RsaData;
var pdfPkcs7 = new PdfPKCS7(null, chain, null, "SHA1", true);
pdfPkcs7.SetExternalDigest(digest, rsaData, "RSA");
var tsc = new TSAClientBouncyCastle(parameters.TimeStampServerUrl, null, null);
var cal = DateTime.Now;
byte[] ocsp = null;
if (chain.Length >= 2)
{
var url = PdfPKCS7.GetOCSPURL(chain[0]);
if (!string.IsNullOrEmpty(url))
ocsp = new OcspClientBouncyCastle(chain[0], chain[1], url).GetEncoded();
}
var sh = pdfPkcs7.GetAuthenticatedAttributeBytes(sha.Hash, cal, null);
pdfPkcs7.Update(sh, 0, sh.Length);
var encodedSig = pdfPkcs7.GetEncodedPKCS7(sha.Hash, cal, tsc, ocsp);
if (csize + 2 < encodedSig.Length)
throw new Exception("Not enough space");
var outc = new byte[csize];
var dic2 = new PdfDictionary();
Array.Copy(encodedSig, 0, outc, 0, encodedSig.Length);
dic2.Put(PdfName.CONTENTS, new PdfString(outc).SetHexWriting(true));
signatureAppearance.Close(dic2);
}
private static X509Certificate[] GetChain(X509Certificate2 certificate2)
{
var chain = new X509Chain();
chain.Build(certificate2);
var orgChain = new List<X509Certificate>();
foreach (var cert in chain.ChainElements)
{
orgChain.Add(GetBouncyCastleCertificate(cert.Certificate));
}
return orgChain.ToArray();
}
private static X509Certificate GetBouncyCastleCertificate(X509Certificate2 certificate)
{
var certificateMemoryStream = new MemoryStream(certificate.RawData);
return new X509Certificate(
X509CertificateStructure.GetInstance(new Asn1InputStream(certificateMemoryStream).ReadObject()));
}