How to build a RFC 3161 time stamp using Bouncy Castle - pdf

we need to implement our own PDF timestamping mechanism based on X509 certificate (including private key of course) and RFC 3161. I've googled and asked here on SO and proper solution would be to re-implement TSAClient class to do timestamping locally for us (without online TSA). However I didn't find any implementation of RFC 3161 except SecureBlackbox components. It should be possible with Bouncy Castle libraries but I don't know how.
Can you please point me to right direction?

It is possible to generate a RFC3161 timestamp token with Bouncycastle libraries.
First create a TimestampRequest. For your case it is only a wrapper for the digest algorithm and the digest value.
byte[] document = /* ... */
byte[] digest = MessageDigest.getInstance("SHA256").digest(document);
TimeStampRequestGenerator tsReqGen = new TimeStampRequestGenerator();
TimeStampRequest tsReq = tsReqGen.generate(CMSAlgorithm.SHA256, digest);
Then generate the token
DigestCalculator dgCalc = new JcaDigestCalculatorProviderBuilder().build();
ContentSigner signer = new JcaContentSignerBuilder().build(getPrivateKey());
SignerInfoGenerator siGen = new JcaSignerInfoGeneratorBuilder(new JcaDigestCalculatorProviderBuilder()).build(signer, getCertificate());
ASN1ObjectIdentifier policy = new ASN1ObjectIdentifier("1.2.3.4.5.6"); // Replace by your timestamping policy OID
TimeStampTokenGenerator tstGen = new TimeStampTokenGenerator(siGen, dgCalc, policy);
/* Set the parameters e.g. set the accuracy or include the signing certificate */
TimeStampToken tst = tstGen.generate(tsReq, generateSerialNumber(), new Date());
byte[] encoding = tst.getEncoded();

Related

Why is SignHash from RSACng (dotNetCore) different from BoncyCastle (running in Mono)?

I'm trying summarize: I have a self-signed certificate (created by OpenSSL) being used in a aspnetCore API to verify hash (or message digest, if you prefer) signature. I can run tests to sign the hash and run in the API and everything goes well.
Now, I'm trying do my client sign such hashs and consume the API. It's an application running on Mono. I'm tried do exactly the same thing as my tests, but Mono has a bug in X509Certificate2, when pfx certificate is protected by password. So, I replaced it the by the famous BouncyCastle. However, the results are different... Checking the pk algorithm, I can see some differences, but nothing so remarkable (at least to me).
Can you give me advices? I gonna put the codes:
Good code running on tests (dotNet Core):
Console.WriteLine("Entry text:");
var text = Console.ReadLine();
X509Certificate2 certificate = new X509Certificate2((byte[])Resource1._2dbb1721_281d_4990_836c_7e46909b8767, "1509a96c-d56b-4e17-af5b-c11f5f214c74");
SHA1Managed sha1Hasher = new SHA1Managed();
UnicodeEncoding encoding = new UnicodeEncoding();
byte[] data = encoding.GetBytes(text);
byte[] hash = sha1Hasher.ComputeHash(data);
RSA provider = (RSA)certificate.PrivateKey;
var signatureData = provider.SignHash(hash, HashAlgorithmName.SHA1, RSASignaturePadding.Pkcs1);
var signature = Convert.ToBase64String(signatureData);
Bad code, running on Mono and BouncyCastle:
SHA1Managed sha1Hasher = new SHA1Managed();
var encoding = new UnicodeEncoding();
byte[] data = encoding.GetBytes(uuid);
byte[] hash = sha1Hasher.ComputeHash(data);
var rsaParameters = DotNetUtilities.ToRSAParameters((RsaPrivateCrtKeyParameters)privateKeyParam);
var provider = RSA.Create();
provider.ImportParameters(rsaParameters);
var signatureData = provider.SignHash(hash, HashAlgorithmName.SHA1, RSASignaturePadding.Pkcs1);
var signature = Convert.ToBase64String(signatureData);
Both certificates are the same. Debugging, I could see the dotNet instantiates on provider as RSACng. The SignatureAlgorithm property is "RSA" as the KeyExchangeAlgorithm so.
In the BoncyCastle over Mono, the provider is RSACrytoServiceProvider. According some articles that I read, this shouldn't make any difference. However, SignatureAlgorithm is "http://www.w3.org/2000/09/xmldsig#rsa-sha1", while KeyExchangeAlgorithm is "RSA-PKCS1-KeyEx".
Thanks in advance
My bad: I've just realized my program wasn't using the string it is supposed might use to calculate the hash...
The Mono's version using BouncyCastle is working perfect fine.

"ERROR unspecified algorithm" when signing a PDF with itextsharp 5.5.13.2 and SHA-256

Good morning, I am trying to sign a PDF document using ItextSharp 5.5.13.2 and SHA-256 for the signature process but at the moment of signing I get the error "Specified Algorithm is Invalid". This error does not happen when I use SHA-1 with .NetFramework 4.7.2 to sign the PDF, the next method is the one I use to sign the PDF.
public void SignPDF(string PathSourceDoc, string PathTargetDoc, X509Certificate2 certificate, string pathLogo)
{
using (PdfReader reader = new PdfReader(PathSourceDoc))
using (var writer = new FileStream(PathTargetDoc, FileMode.Create, FileAccess.Write))
using (var stamper = PdfStamper.CreateSignature(reader, writer, '\0', null, true))
{
var signature = stamper.SignatureAppearance;
signature.CertificationLevel = PdfSignatureAppearance.NOT_CERTIFIED;
signature.Reason = "My Reason";
signature.Location = "My Location";
signature.SignDate = DateTime.Now;
signature.Acro6Layers = true;
PdfSignature objSignature = new PdfSignature(PdfName.ADOBE_PPKMS, PdfName.ADBE_PKCS7_SHA1);
objSignature.Date = new PdfDate(signature.SignDate);
signature.CryptoDictionary = objSignature;
var bcCert = DotNetUtilities.FromX509Certificate(certificate);
string name = CertificateInfo.GetSubjectFields(bcCert).GetField("CN");
string industry = CertificateInfo.GetSubjectFields(bcCert).GetField("O");
string position = CertificateInfo.GetSubjectFields(bcCert).GetField("T");
DateTime date = DateTime.Now;
signature.Layer2Text = "Digital Signed by: " + name + "\n" +
"Reason: " + "My Reason" + "\n" +
"Date: " + date;
signature.Layer2Font = new Font(iTextSharp.text.Font.FontFamily.TIMES_ROMAN, 8);
Image img = Image.GetInstance(pathLogo);
signature.SignatureGraphic = img;
signature.SignatureRenderingMode = PdfSignatureAppearance.RenderingMode.GRAPHIC_AND_DESCRIPTION;
Rectangle rect = new Rectangle(50, 50, 300, 110);
signature.SetVisibleSignature(rect, 1, null);
var standard = CryptoStandard.CADES;
/*I tried this way but I get an error of type Internal.Cryptography.CryptoThrowHelper.WindowsCryptographicException: 'The requested operation is not supported.'*/
X509Certificate cert = certificate;
X509Certificate2 signatureCert = new X509Certificate2(cert);
var pk = Org.BouncyCastle.Security.DotNetUtilities.GetKeyPair(signatureCert.PrivateKey).Private;// the error is generated in this line (Internal.Cryptography.CryptoThrowHelper.WindowsCryptographicException: 'The requested operation is not supported.').
IExternalSignature es = new PrivateKeySignature(pk, "SHA-256");
/****************************************************************************************************************************************************************/
X509Certificate2Signature externalSignature = new X509Certificate2Signature(certificate, DigestAlgorithms.SHA1); /*throws an exception on this line of type "System.ArgumentException: 'Unknown encryption algorithm System.Security.Cryptography.RSACng'"
* when using .NET Core 3.1. this always happens, it doesn't matter if I use sha1 or use any other algorithm, it always generates error*/
MakeSignature.SignDetached(signature, externalSignature, new[] { bcCert }, null, null, null, 0, standard);
}
}
The method receives as input parameters the path of the PDF that I need to sign, the path of the pdf that will be created when signing, the certificate and the path of the logo for displaying the image in the signature. Actually I don't know what I'm doing wrong since I've been researching related questions and it should work with SHA-1 and SHA-256 and .NetFramework.
Then I migrated the project to .NetCore 3.1 in order to try to fix the problem but instead I got a new error (It is commented in the code). My goal is to use .NetCore and to allow me to sign a pdf using sha256. I have no problem modifying the SignPdf method in order to make the program work.
Any contribution or information link is appreciated. Thanks for your help.
Psdt: This is the stacktrace is as follows...
this is the stacktrace image of the error when the project was migrated to .Net Core 3.1
Apparently the Microsoft Crypto APIs used by X509Certificate2Signature do not support SHA-256.
You use a X509Certificate2Signature instance as IExternalSignature argument of MakeSignature.SignDetached.
If the PrivateKey of your X509Certificate2 is a RSACryptoServiceProvider (which appears to be the case for your certificate), X509Certificate2Signature.Sign calls
RSACryptoServiceProvider rsa = (RSACryptoServiceProvider)certificate.PrivateKey;
return rsa.SignData(message, hashAlgorithm);
RSACryptoServiceProvider.SignData at first apparently does support "SHA256" because before forwarding execution to SignHash it must have calculated the hash value to sign.
RSACryptoServiceProvider.SignHash, though, does not support SHA-256, according to the Microsoft docs on that method:
Remarks
This method creates a digital signature that is verified using the VerifyHash method.
The valid hash algorithms are SHA1 and MD5. The algorithm identifier can be derived from the hash name by using the MapNameToOID method.
Due to collision problems with SHA1 and MD5, Microsoft recommends a security model based on SHA256 or better.
(Viewed 2021-06-24, 11:07)
Thus, you get the "Specified Algorithm is Invalid" error.
Essentially, therefore, one should not base one's IExternalSignature implementation for RSA signing on RSACryptoServiceProvider anymore.
Maybe this is why X509Certificate2Signature was not ported to iText version 7...
An alternative approach might be to check whether the PrivateKey is a RSACng first and in that case use it as a RSACng. This Cryptography Next Generation (CNG) implementation of the RSA algorithm should support better hashing algorithms...

iText 7 PDF signing with GlobalSign DSS

I'm digitally signing a PDF using iText 7 in an asp.net app. I'm using vb but I could figure out a c# answer, too. We're using GlobalSign DSS in order to have the signature not come up as UNKNOWN (AATL).
I created a pfx file from our company SSL certificate and I am able to sign the PDF files using that file but I don't understand how to use the things I get from GlobalSign to accomplish the same thing. This service requires to send a hex encoded SHA256 digest and in return I receive a hex encoded signatureValue. Besides that I also receive a PEM encoded X509 signing certificate, intermediate certificate (PEM encoded X509 certificate that represents the issuer of the signing certificate), Base64 encoded DER representation of the OCSP response for signing_certificate, a Base64 encoded DER representation of timestamp token. It includes the TSA signing certificate in SignedData.CertificatesSet.
I created the code below as best as I could with limited examples from the internet for itext 7 and my even more limited knowledge of PDF signing and certificates in general. I know now that there are some things I make that I don't even use properly such as the tsaClient and the crlList. I didn't quite understand what those were until I had to switch to the DSS. I actually still don't know what the crlList would be although I think it has something to do with a chain, although I don't quite know what a chain is in this context.
Dim outputFileStream As New FileStream(Server.MapPath(strDest), IO.FileMode.Create, IO.FileAccess.ReadWrite)
Dim signer As New PdfSigner(New PdfReader(Server.MapPath(strTemp)), outputFileStream, True)
signer.SetCertificationLevel(PdfSigner.CERTIFIED_FORM_FILLING_AND_ANNOTATIONS)
Dim appearance As PdfSignatureAppearance = signer.GetSignatureAppearance
Dim rect As New iText.Kernel.Geom.Rectangle(50, 425, 500, 200)
appearance.SetContact("Contact Name - Phone")
appearance.SetSignatureCreator("Company - New Program")
appearance.SetReason(vbCrLf + "eDoc Consent Agreement: " + CType(Session("eDocConsentDateTime"), Date).ToString + vbCrLf _
+ "Terms and Conditions Agreement: " + objInstall.AgreementSigned.ToString + vbCrLf _
+ "Identifier: " + Session("Ident"))
appearance.SetLocation(vbCrLf + strInstallationAddress)
appearance.SetReuseAppearance(False)
appearance.SetPageRect(rect)
appearance.SetPageNumber(2) 'Signature will appear on Page 2
appearance.SetRenderingMode(PdfSignatureAppearance.RenderingMode.DESCRIPTION)
signer.SetFieldName(signer.GetNewSigFieldName())
Dim cert As New X509Certificate(Server.MapPath("CompSSL.pfx"), "password")
Dim tsaUrl As String = CertificateUtil.GetTSAURL(DotNetUtilities.FromX509Certificate(cert))
tsaClient = New TSAClientBouncyCastle(tsaUrl)
Dim crlList As IList(Of ICrlClient) = New List(Of ICrlClient)()
Dim fsCertFile As New FileStream(Server.MapPath("CompSSL.pfx"), FileMode.Open, FileAccess.Read)
Dim pk12 As New Pkcs12Store(fsCertFile, "password")
fsCertFile.Dispose()
'then Iterate throught certificate entries to find the private key entry
Dim [alias] As String = Nothing
For Each tAlias As String In pk12.Aliases
If pk12.IsKeyEntry(tAlias) Then
[alias] = tAlias
Exit For
End If
Next
Dim pk = pk12.GetKey([alias]).Key
Dim myExternalSignature As IExternalSignature = New PrivateKeySignature(pk, DigestAlgorithms.SHA1)
signer.SignDetached(myExternalSignature, New Org.BouncyCastle.X509.X509Certificate() {pk12.GetCertificate([alias]).Certificate}, Nothing, Nothing, Nothing, 0, PdfSigner.CryptoStandard.CMS)
So I'd like to know how I can use the GlobalSign pieces in order to sign the PDF. It almost seems like they are giving me everything I need to plug into the SignDetached method at the very end but I specifically don't know these things:
I believe that I'm supposed to do the hash to create the digest AFTER I've added the appearance stuff but I don't know what I would pass those bytes to something like Dim hash() As Byte = s.ComputeHash(fileBytes) at that point in my code (right after signer.SetFieldName(signer.GetNewSigFieldName()) I assume)
I don't know how or if I use the two certificates I get from the DSS. I even saw an example that suggests I may put them together to get something.
I don't know if the Signature Value I get is the equivalent of the IExternalSignature. If so, I don't know how to convert it to type "Org.BouncyCastle.Crypto.ICipherParameters" or even if I'm supposed to be trying to.
I plug the timestamp token string I get back from the DSS into the line tsaClient = New TSAClientBouncyCastle(tsaUrl) in the place of "tsaUrl" and there is no error but I don't know if that's right.
I don't know what to do with the OCSP response that I get from the DSS to put it into an IOcspClient object.
If I don't even try to put in the timestamp or the OCSP response in, as apparently I don't in the .pfx version of my code, what will that mean?
What are the data type conversions I'll have to do, if any, to make the DSS responses work?
I've tried to follow the iTextsharp examples, even this one (itextsharp signing pdf with signed hash) which is exactly what I'm trying to do but it doesn't seem to give me the info I need to finish it off. Thanks for your help.
EDIT:
Thanks for the help, dsandoval. I went ahead and hash the unchanged file and that's what I send to GlobalSign:
Dim s As New SHA256Managed
Dim fileBytes() As Byte = IO.File.ReadAllBytes(Server.MapPath(strSrc))
Dim hash() As Byte = s.ComputeHash(fileBytes)
Dim calculatedHash As String = ByteArrayToString(hash)
With this too:
Private Function ByteArrayToString(ByVal arrInput() As Byte) As String
Dim sb As New System.Text.StringBuilder(arrInput.Length * 2)
For i As Integer = 0 To arrInput.Length - 1
sb.Append(arrInput(i).ToString("X2"))
Next
Return sb.ToString().ToUpper
End Function
Then I wrote the two certificates I get from GS to files and then use BouncyCastle to read them into certificates and put them into an array (signing cert first). I pick up where I am writing the second cert file from their 'certificate_path' string here:
Using sw As New StreamWriter(File.Open(strFile2, FileMode.OpenOrCreate))
sw.WriteLine(strTempCert2)
End Using
Dim fileStream2 = System.IO.File.OpenText(strFile2)
Dim pemReader2 = New Org.BouncyCastle.OpenSsl.PemReader(fileStream2)
Dim myCertificateChain() As Org.BouncyCastle.X509.X509Certificate = New Org.BouncyCastle.X509.X509Certificate() {pemReader.ReadObject, pemReader2.ReadObject}
I did the same thing you did with IExternalSignature using this to convert the string from GS:
Private Function StringToByteArray(s As String) As Byte()
' remove any spaces from, e.g. "A0 20 34 34"
s = s.Replace(" "c, "")
' make sure we have an even number of digits
If (s.Length And 1) = 1 Then
Throw New FormatException("Odd string length when even string length is required.")
End If
' calculate the length of the byte array and dim an array to that
Dim nBytes = s.Length \ 2
Dim a(nBytes - 1) As Byte
' pick out every two bytes and convert them from hex representation
For i = 0 To nBytes - 1
a(i) = Convert.ToByte(s.Substring(i * 2, 2), 16)
Next
Return a
End Function
For now I'm just including the certificate chain and the signature and not the timestamp or the OCSP to see if I can just get it signed. With the certificate chain and the implementation of IExternalSignature, I'm able to get SignDetached to run:
signer.SignDetached(myExternalSignature, myCertificateChain, Nothing, Nothing, Nothing, 0, PdfSigner.CryptoStandard.CMS)
The PDF is signed but the signature panel says "Certification by is invalid", "Certified by %s". The details say "There are errors in the formatting or information contained in this signature". When clicking on "Certificate details", nothing happens. I found another person online with this error (Error signing pdf with PHP's openssl_pkcs7_sign) and it was due to the signature container being too big. The zero in my SignDetached parameters is supposed to be "estimate the size" so I changed it to 8192 which was a magic number of 8k I found in some other examples online. That didn't help. Any suggestions at this point?
I'm using c# for my implementation. I'm not too familiar with VB but hopefully this helps. Most of the examples I've seen are using Java and there were some differences in how they implement this, so I had to work around them. The only things I had to implement for this work was the IExternalSignature, IOcspClient, and ITsaClient.
The digest that is sent to GlobalSign are the contents of the document prior to applying to the signature, so the appearance can be left out if applied with signature. The document is not modified until you apply the signature using the PdfSigner.SignDetached, I believe. To compute the digest, you need to use the SHA256 algorithm to hash the message and hexencode it. This is the digest that will be sent to GS.
You need to put both the certificates from GlobalSign to create the certificate chain. First add the signing certificate then the certificate you can retrieve from their 'certificate_path' endpoint.
I'm not sure about the conversion to that type, but with the c# library, the Sign method of IExternalSignature returns a byte[]. With my own implementation of the interface, I simply converted the SignatureValue we received from GS to a byte[].
For the timestamp, we also implemented our own ITsaClient that calls GS for the timestamp. The digest has to be calculated in the GetTimeStampToken method and sent to GS. The response is a hexencoded string that we convert to a byte[].
For the OCSP, we also implemented our own IOcspClient. Since we get the ocsp_response when we fetch the identity, this client simply uses that, rather than calling GS again. I had issues enabling LTV when returning the response by only converting it from a Base64 string to a byte[]. BouncyCastle had trouble reading the response in this format. I had to convert it to the following BouncyCastle objects for this to work properly.
public byte[] GetEncoded( X509Certificate checkCert, X509Certificate issuerCert, string url )
{
var decoded = Base64.Decode(_ocspResponse); //_ocspResponse is same one that we received when fecthing the identity
var response = new OcspResp(decoded);
var basicResponse = (BasicOcspResp)response.GetResponseObject();
return basicResponse.GetEncoded();
}
If no timestamp is attached, the signature will not be timestamped. It will display the time as something along the lines of "the time is time on the signer's computer's system clock" when you open the document in Adobe. If no ocsp response is attatched, the person validating will not have a way to validate that the certificate has/hasn't been revoked at the time of the signature.
I believe the only conversions I had to make were the one mentioned above.
Let me know if you need any more code examples or any other questions.
EDIT
Here is the IExternalSignature implementation. The hash and encryption algorithm has to match what GS will use. They are using SHA256 and RSA.
public class ExternalSignature : IExternalSignature
{
private readonly SigningIdentity _identity;
private readonly GlobalSignClient _client;
public ExternalSignature( GlobalSignClient client, SigningIdentity identity )
{
_client = client;
_identity = identity;
}
public string GetEncryptionAlgorithm() => "RSA";
public string GetHashAlgorithm() => DigestAlgorithms.SHA256;
public byte[] Sign( byte[] message )
{
// Using the System.Security.Cryptography implementation of SHA256
using (var sha = SHA256.Create())
{
// Hash the message, containing the PDF
var bytes = sha.ComputeHash(message);
// HexEconde the hashed message to send to GlobalSign
var digest = Helpers.ByteArrayToString(bytes);
// Send the digest to GlobalSign
var signature = _client.GetSignatureAsync(_identity.Id, digest).Result;
// Return the returned signature as a byte[]
return Helpers.StringToByteArray(signature);
}
}
}
To add the signature, just create an instance and pass it to SignDetached method.
var signature = new ExternalSignature(_client, signingIdentity);
...
...
//After creating the PDF signer, adding appearance, creating the OCSP and TSA clients.
signer.SignDetached(signature, certChain, null, ocsp, tsa, 0, PdfSigner.CryptoStandard.CMS);
I did the same with the OCSP and TSA clients.

Unable to verify digital signature using Apache PDFBOX

I am a newbie in using Digital Signatures. In one of the projects we are using Apache PdfBox for processing digitally signed pdf files. While we could test all features, verification of signed pdf files is something we are unable to crack. We are using BouncyCastle as the provider. Below is the code:
Get Digital Signature and Signed Content from pdf file:
byte[] signatureAsBytes = pdsignature.getContents(new FileInputStream(this.INPUT_FILE));
byte[] signedContentAsBytes = pdsignature.getSignedContent(new FileInputStream(this.INPUT_FILE));
Digital Signature Verification:
Security.addProvider(new BouncyCastleProvider());
Signature signer = Signature.getInstance("RSA","BC");
//Get PublicKey from p7b file
X509Certificate cert509=null;
File file = new File("C:\\certificate_file.p7b");
FileInputStream fis = new FileInputStream(file);
CertificateFactory cf = CertificateFactory.getInstance("X.509");
Collection c = cf.generateCertificates(fis);
Iterator it = c.iterator();
PublicKey pubkey;
while (it.hasNext())
{
cert509 = (X509Certificate) it.next();
pubkey = cert509.getPublicKey();
}
boolean VERIFIED=false;
Security.addProvider(new BouncyCastleProvider());
Signature signer = Signature.getInstance("RSA","BC");
PublicKey key=this.getPublicKey(false);
signer.initVerify(key);
List<PDSignature> allsigs = this.PDFDOC.getSignatureDictionaries();
Iterator<PDSignature> i = allsigs.iterator();
while(i.hasNext())
{
PDSignature sig = (PDSignature) i.next();
byte[] signatureAsBytes = sig.getContents(new FileInputStream(this.INPUT_FILE));
byte[] signedContentAsBytes = sig.getSignedContent(new FileInputStream(this.INPUT_FILE));
signer.update(signedContentAsBytes);
VERIFIED=signer.verify(signatureAsBytes);
}
System.out.println("Verified="+VERIFIED);
Below are relevant extracts from the certificate in p7b format - I am using BouncyCastle as security provider:
Signature Algorithm: SHA256withRSA, OID = 1.2.840.113549.1.1.11
Key: Sun RSA public key, 2048 bits
Validity: [From: Tue Aug 06 12:26:47 IST 2013,
To: Wed Aug 05 12:26:47 IST 2015]
Algorithm: [SHA256withRSA]
With the above code I am always getting response as "false". I have no idea how to fix the issue. Please help
Your prime problem is that there are multiple types of PDF signatures differing in the format of the signature container and in what actually are the signed bytes. Your BC code, on the other hand, can verify merely naked signature byte sequences which are contained in the afore-mentioned signature containers.
Interoperable signature types
As the header already says, the following list contains "interoperable signature types" which are more or less strictly defined. The PDF specification specifies a way to also include completely custom signing schemes. But let us assume we are in an interoperable situation. The the collection of signature types burns down to:
adbe.x509.rsa_sha1 defined in ISO 32000-1 section 12.8.3.2 PKCS#1 Signatures; the signature value Contents contain a DER-encoded PKCS#1 binary data object; this data object is a fairly naked signature, in case of RSA an encrypted structure containing the padded document hash and the hash algorithm.
adbe.pkcs7.sha1 defined in ISO 32000-1 section 12.8.3.3 PKCS#7 Signatures; the signature value Contents contain a DER-encoded PKCS#7 binary data object; this data object is a big container object which can also contain meta-information, e.g. it may contain certificates for building certificate chains, revocation information for certificate revocation checks, digital time stamps to fix the signing time, ... The SHA1 digest of the document’s byte range shall be encapsulated in the PKCS#7 SignedData field with ContentInfo of type Data. The digest of that SignedData shall be incorporated as the normal PKCS#7 digest.
adbe.pkcs7.detached defined in ISO 32000-1 section 12.8.3.3 PKCS#7 Signatures; the signature value Contents contain a DER-encoded PKCS#7 binary data object, see above. The original signed message digest over the document’s byte range shall be incorporated as the normal PKCS#7 SignedData field. No data shall be encapsulated in the PKCS#7 SignedData field.
ETSI.CAdES.detached defined in ETSI TS 102 778-3 and will become integrated in ISO 32000-2; the signature value Contents contain a DER-encoded SignedData object as specified in CMS; CMS signature containers are close relatives to PKCS#7 signature containers, see above. This essentially is a differently profiled and stricter defined variant of adbe.pkcs7.detached.
ETSI.RFC3161 defined in ETSI TS 102 778-4 and will become integrated in ISO 32000-2; the signature value Contents contain a TimeStampToken as specified in RFC 3161; time stamp tokens again are a close relative to PKCS#7 signature containers, see above, but they contain a special data sub-structure harboring the document hash, the time of the stamp creation, and information on the issuing time server.
I would propose studying the specifications I named and the documents referenced from there, mostly RFCs. Based on that knowledge you can easily find the appropriate BouncyCastle classes to analyze the different signature Contents.
PS (2021): Meanwhile ISO 32000-2 has been published and indeed contains specifications of ETSI.CAdES.detached and ETSI.RFC3161. Also the ETSI technical specifications TS 102 778-* for PAdES have been replaced by an actual norm, ETSI EN 319 142-*.
A working example to validate adbe.pkcs7.detached PDF signatures (the most common PDF signatures) with Apache PDFBox 1.8.16 and Bouncy Castle 1.44:
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.interactive.digitalsignature.PDSignature;
import org.bouncycastle.cms.CMSProcessable;
import org.bouncycastle.cms.CMSProcessableByteArray;
import org.bouncycastle.cms.CMSSignedData;
import org.bouncycastle.cms.SignerInformation;
import org.bouncycastle.cms.SignerInformationStore;
import java.io.File;
import java.io.FileInputStream;
import java.security.cert.CertStore;
import java.security.cert.X509Certificate;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
public class PDFBoxValidateSignature {
public static void main(String[] args) throws Exception {
File signedFile = new File("sample-signed.pdf");
// We load the signed document.
PDDocument document = PDDocument.load(signedFile);
List<PDSignature> signatureDictionaries = document.getSignatureDictionaries();
// Then we validate signatures one at the time.
for (PDSignature signatureDictionary : signatureDictionaries) {
// NOTE that this code currently supports only "adbe.pkcs7.detached", the most common signature /SubFilter anyway.
byte[] signatureContent = signatureDictionary.getContents(new FileInputStream(signedFile));
byte[] signedContent = signatureDictionary.getSignedContent(new FileInputStream(signedFile));
// Now we construct a PKCS #7 or CMS.
CMSProcessable cmsProcessableInputStream = new CMSProcessableByteArray(signedContent);
CMSSignedData cmsSignedData = new CMSSignedData(cmsProcessableInputStream, signatureContent);
SignerInformationStore signerInformationStore = cmsSignedData.getSignerInfos();
Collection signers = signerInformationStore.getSigners();
CertStore certs = cmsSignedData.getCertificatesAndCRLs("Collection", (String) null);
Iterator signersIterator = signers.iterator();
while (signersIterator.hasNext()) {
SignerInformation signerInformation = (SignerInformation) signersIterator.next();
Collection certificates = certs.getCertificates(signerInformation.getSID());
Iterator certIt = certificates.iterator();
X509Certificate signerCertificate = (X509Certificate) certIt.next();
// And here we validate the document signature.
if (signerInformation.verify(signerCertificate.getPublicKey(), (String) null)) {
System.out.println("PDF signature verification is correct.");
// IMPORTANT: Note that you should usually validate the signing certificate in this phase, e.g. trust, validity, revocation, etc. See http://www.nakov.com/blog/2009/12/01/x509-certificate-validation-in-java-build-and-verify-chain-and-verify-clr-with-bouncy-castle/.
} else {
System.out.println("PDF signature verification failed.");
}
}
}
}
}
Not sure if there is an official example for this, I've checked in the official examples for PDFBox 1.8.4 and I didn't find anything.

How to generate a CMS (cryptographic message syntax) with Mono Framework and BouncyCastle API?

I have a Certificate in PKCS#12 format and I need to generate a CMS signature (Cryptographic Message Syntax). Due that "Mono Framework" does not have a full implemented "System.Security.Cryptography" assembly, I am trying to use "Bouncy Castle API for C#".
So, using "Bouncy Castle", I need to write an alternative code to the one I had wrote on DOTNET.
The code on DOT NET is the following:
X509Certificate2 crt = new X509Certificate2();
byte[] crtBytes = [ certificate in the format PKCS12 (certificate + private key) obtained using FileStream class]
crt.Import(crtBytes, "123456", X509KeyStorageFlags.DefaultKeySet);
Encoding msgCodificado = Encoding.UTF8;
byte[] msgBytes = msgCodificado.GetBytes(xmlTRA.OuterXml); // xmlTRA.OuterXml is the data to sign
ContentInfo pkcsContentInfo = new ContentInfo(msgBytes);
SignedCms cms = new SignedCms(pkcsContentInfo);
CmsSigner firmante = new CmsSigner(crt);
firmante.IncludeOption = X509IncludeOption.EndCertOnly;
cms.ComputeSignature(firmante); // ---> throw an cryptografy exception with MONO
byte[] firma = cms.Encode();
firmaB64 = Convert.ToBase64String(firma);
Anyone knows how to write an alternative code using "Bouncy Castle API for C#"?
Org.BouncyCastle.Pkcs has classes for working with a PKCS#12 store.
Org.BouncyCastle.Cms has classes for working with CMS messages.
There are corresponding test classes in the source code that show the basics of using, e.g. Pkcs12Store(Builder) and CmsSignedData(Generator).