EldoS | Feel safer!

Software components for data protection, secure storage and transfer

Issues implementing PAdES signing of PDF documents

Also by EldoS: CallbackProcess
A component to control process creation and termination in Windows and .NET applications.
#31551
Posted: 12/01/2014 12:10:14
by Peter Johnson (Standard support level)
Joined: 10/29/2014
Posts: 10

Hello,

I'm following the How-to on Implementing PAdES signing of PDF documents using SecureBlackBox, and I'm having difficulty applying it. I'm using a certificate stored on a crypto-key from a trusted CA that links back to the Adobe Root CA. I was able to use the PDFPublicSecurityHandler to successfully certify a PDF with our secure token and everything seemed perfectly fine when we opened the PDF in Adobe Reader X, but then when someone opened the PDF in Adobe Reader XI they got that "PDF is not LTV-enabled and will expire" warning. Following a series of searches on the site, I ended up at the How-To that led to using the PDFAdvancedPublicSecurityHandler and its advanced options to meet Adobe's LTV requirements.

I followed the documentation, but I'm hitting a snag on signing; the chain validation fails. I followed further documentation on diagnosing failed chain validation to output a log of the validation actions and the results showed that I first had failed to generate a CRL retriever because I had not registered an HTTPCRLRetrieverFactory. After clearing up that issue, I am now getting a CRL error 1001 - Validation of CRL's signature failed on my certificate. Then, I'm getting OCSP errors 2002 and 2004 on my certificate. My CA's certificate is not generating any CRL or OCSP errors on validation, but it is still failing validation for reason 32.

As far as the code I'm using, it's similar to the how-to documentation. The cert storage I'm using in my handler contains my certificate and my CA certificate--I don't know how to or if I need to include the Adobe Root CA. I added my CA certificate to the CustomRevocationInfo.Certificates, but I was unsure of how to add the CRL manually. I am using an http client for the time stamp provider using a trusted time stamp authority. I have tried setting MandatoryCLRCheck and MAndatoryOCSPCheck to false, and if I set IgnoreChainValidationErrors to true the process finishes successfully but the certificate on the pdf doesn't display correctly and still does not show to be LTV-enabled.

If you have any further questions, please let me know! Any help would be greatly appreciated.
#31552
Posted: 12/01/2014 12:29:55
by Eugene Mayevski (EldoS Corp.)

Please to perform validation of your signing certificate using TElX509CertificateValidator directly. If it fails, use the how-to on https://www.eldos.com/security/articles/7545.php and https://www.eldos.com/security/articles/7639.php for diagnostics.

Maybe you need to perform additional setup of the HTTP- and LDAP-related components as described on https://www.eldos.com/security/articles/8115.php .

Error codes that you mentioned are described in the help file, which is installed on your computer and is also available on the site.


Sincerely yours
Eugene Mayevski
#31554
Posted: 12/01/2014 15:26:05
by Peter Johnson (Standard support level)
Joined: 10/29/2014
Posts: 10

Eugene,

Thank you for your quick response! I followed the how-to documents and used the TElX509CertificateValidator directly to perform validation of my certificate from the certificate store, then a second validation on the CA certificate. I don't believe I'm using any LDAP components, but I checked on the type of CRL Retriever just in case and it did say it was a TElHTTPCRLRetriever. I don't think there's an issue with the HTTP requests because of where it appears to be failing, but please let me know if I misunderstood. I used the validation events to log the process step-by-step and compared the resultant codes and I think I've narrowed down the problem. Here's what I learned:

The validation successfully retrieves and uses the CRL for my certificate from the provided URI in the certificate.

The validation works to retrieve an OCSP response for my certificate from Entrust.

The validation successfully retrieves and uses the CRL for the Entrust Validation Authority certificate.

The validation successfully retrieves and uses an OCSP response for the Entrust Validation Authority certificate.

The validation attempts to validate the Entrust CA certificate. The response is invalid, giving reason 32--Unknown CA. The OCSP response then throws an error and validation chain fails.

When validating only the Entrust CA for Adobe certificate, I get the same reason. The CA for that certificate is the Adobe Root CA, which I'm not sure is obtainable? I must be doing something wrong, because I can only imagine this being a wall that anyone hits when trying to use a certificate that points back to the Adobe Root CA. I followed the How-To's on diagnosing the issue and tried to modify the settings to get past the issue to no avail. Is there a way around this?

Thank you very much for your help thus far.
#31555
Posted: 12/02/2014 00:22:27
by Eugene Mayevski (EldoS Corp.)

If you have registered a CA certificate retriever class as well (this is done in the same way OCSP and CRL retrievers are registered) and after that you still get no CA Certificate error, this means that you should get some collection of CA and/or ROOT certificates somewhere and add them to the validation process. In other words, it's your job to find where to get Adobe Root CA certificate.

Good news is that in your case it seems that you will have problems only with Adobe certificates, so you can try to pull them out of Adobe software and carry with your application.


Sincerely yours
Eugene Mayevski
#31564
Posted: 12/03/2014 08:23:58
by Peter Johnson (Standard support level)
Joined: 10/29/2014
Posts: 10

Thanks Eugene,

I registered an HTTPCertificateRetriever but the results were the same. I was able to extract and install the Adobe Root CA with Adobe Acrobat, but now that I have it installed I'm getting more errors. It still says that it successfully retrieves and uses the CRL for my certificate from Entrust, but where it had been validating the CRL certificate it is now validating the Entrust CA for Adobe certificate instead. If I set MandatoryCRLCheck to false, it checks the Entrust CA for Adobe certificate a total of 6 times. Every time it checks against the Entrust CA for Adobe certificate, the validation now fails for the reason CA Unauthorized. However, when I check the key usage for that certificate it seems okay. I've attached my code this time in case it helps:

Code
        static void ValidateCertificate()
        {
            SBUtils.Unit.SetLicenseKey(SBUtilUnlockKey);
            SBPDF.Unit.Initialize();
            SBPDFSecurity.Unit.Initialize();
            SBHTTPCRL.Unit.RegisterHTTPCRLRetrieverFactory();
            SBLDAPCRL.Unit.RegisterLDAPCRLRetrieverFactory();
            SBHTTPOCSPClient.Unit.RegisterHTTPOCSPClientFactory();
            SBHTTPCertRetriever.Unit.RegisterHTTPCertificateRetrieverFactory();

            using (TElX509CertificateValidator validator = new TElX509CertificateValidator())
            using (TElMemoryCertStorage certStorage = new TElMemoryCertStorage())
            using (TElWinCertStorage systemStore = new TElWinCertStorage())
            {
                TElX509Certificate cert = new TElX509Certificate();
                TElX509Certificate ceCert = new TElX509Certificate();
                TElX509Certificate entrustCert = new TElX509Certificate();
                TElX509Certificate adobeCert = new TElX509Certificate();
                systemStore.SystemStores.BeginUpdate();
                try
                {
                    systemStore.SystemStores.Clear();
                    systemStore.SystemStores.Add("MY");
                    systemStore.SystemStores.Add("CA");
                    systemStore.SystemStores.Add("Root");
                }
                finally
                {
                    systemStore.SystemStores.EndUpdate();
                }
                certStorage.Clear();
                for (int i = 0; i < systemStore.Count; i++)
                {
                    cert = systemStore.get_Certificates(i);
                    if (cert.SubjectName.Organization == "OurOrgName")
                    {
                        ceCert = cert;
                    }
                    if (cert.SubjectName.CommonName == "Entrust CA for Adobe")
                    {
                        entrustCert = cert;
                    }
                    if (cert.SubjectName.CommonName == "Adobe Root CA")
                    {
                        adobeCert = cert;
                    }
                }

                certStorage.Add(ceCert, true);
                //entrustCert = CreateCertificateCopy(entrustCert); I attempted to use a solution provided to another person on the forums having an issue with this method of certification with the same issuer and SafeKey hardware, but it didn't appear to make a difference.
                //adobeCert = CreateCertificateCopy(adobeCert);
                certStorage.Add(entrustCert, false);
                certStorage.Add(adobeCert, false);
                int reason = 0;
                TSBCertificateValidity test = TSBCertificateValidity.cvInvalid;

                validator.MandatoryCRLCheck = false;
                validator.MandatoryOCSPCheck = false;
                validator.MandatoryRevocationCheck = true;
                validator.IgnoreCAKeyUsage = true;
                
                validator.OnBeforeCRLRetrieverUse += new SBCertValidator.TSBBeforeCRLRetrieverUseEvent(CertValidator_OnBeforeCRLRetrieverUse);
                validator.OnBeforeOCSPClientUse += new SBCertValidator.TSBBeforeOCSPClientUseEvent(CertValidator_OnBeforeOCSPClientUse);
                validator.OnCRLError += new SBCertValidator.TSBCertificateValidatorCRLErrorEvent(CertValidator_OnCRLError);
                validator.OnCRLNeeded += new SBCertValidator.TSBCRLNeededEvent(CertValidator_OnCRLNeeded);
                validator.OnCRLRetrieved += new SBCertValidator.TSBCRLRetrievedEvent(CertValidator_OnCRLRetrieved);
                validator.OnOCSPError += new SBCertValidator.TSBCertificateValidatorOCSPErrorEvent(CertValidator_OnOCSPError);
                validator.OnAfterCRLUse += new SBCertValidator.TSBAfterCRLUseEvent(CertValidator_OnAfterCRLUse);
                validator.OnAfterOCSPResponseUse += new SBCertValidator.TSBAfterOCSPResponseUseEvent(CertValidator_OnAfterOCSPResponseUse);
                validator.OnBeforeCertificateValidation += new SBCertValidator.TSBBeforeCertificateValidationEvent(CertValidator_OnBeforeCertificateValidation);
                validator.OnAfterCertificateValidation += new SBCertValidator.TSBAfterCertificateValidationEvent(CertValidator_OnAfterCertificateValidation);
                
                Log("Starting validation of the certificate: " + ceCert.SubjectRDN.SaveToDNString() + " / " + ceCert.IssuerRDN.SaveToDNString());
                
                validator.Validate(ceCert, ref test, ref reason);

                Console.WriteLine(test.ToString() + "  " + reason.ToString());
                Console.ReadLine();
            }
        }


I've attached the log output to this post with just our certificate and organization names changed.


[ Download ]
#31565
Posted: 12/03/2014 08:32:31
by Eugene Mayevski (EldoS Corp.)

In your code you don't use certStorage anywhere (or I don't see this). You need to call AddKnownCertificates or AddTrustedCertificates method of validator object and pass certStorage there.

AddKnownCertificates is used to add known but not yet trusted certificates.
AddTrustedCertificates is used to add trusted root certificates (if you trust them explicitly).


Sincerely yours
Eugene Mayevski
#31566
Posted: 12/03/2014 09:29:39
by Peter Johnson (Standard support level)
Joined: 10/29/2014
Posts: 10

Thank you Eugene,

That certStorage was a relic from the code I was using to attempt to apply the certificate to a signature in a PDF, but using it in the validator's AddTrustedCertificates method got me back the proper validation of the certificate's CRL. I am still stuck on the Entrust CA for Adobe validation, however. It still fails validation for reason 64, CAUnauthorized.

Checking against the error codes, it says that the "Issuer (CA) certificate was found but it's key usage fields don't allow use of this certificate for signing other certificates." However, when I check the Entrust CA for Adobe certificate's key usage fields, it says "Certificate Signing, Off-line CRL Signing, CRL Signing (06)". It also has an Enhanced Key Usage field that reads "Unknown Key Usage (1.2.840.113583.1.1.5)". Under that certificate's CA (Adobe Root CA) for key usage rights (it's hard to tell from the documentation whether the error refers to this CA or it's parent CA), it says "Certificate Signing, Off-line CRL Signing, CRL Signing (06)".

I apologize for all of the issue. I was able to successfully use the TElPDFPublicKeySecurityHandler to certify PDFs with this certificate, but my end-goal is to get that LTV-enabled tag in Adobe Reader/Acrobat XI by following your documentation using TElPDFAdvancedPublicKeySecurityHandler and I have not successfully gotten that tag so far with the certificate chain being unable to validate. I'm hoping that if I can at least get the validation to succeed, I'll be one step closer to that goal.
#31567
Posted: 12/03/2014 09:31:29
by Eugene Mayevski (EldoS Corp.)

Let's continue in HelpDesk ( https://www.eldos.com/helpdesk/ ) please. I have created a new support ticket based on your above message. You will see your (and only your) support tickets by following this URL. You will also get e-mail notifications about updates related to your support ticket.


Sincerely yours
Eugene Mayevski
#31575
Posted: 12/04/2014 10:22:13
by Peter Johnson (Standard support level)
Joined: 10/29/2014
Posts: 10

For anyone having the same issue, here is the solution we found that worked for me. This applies at least to a certificate from Entrust stored on a SafeKey USB token. First, I had to extract and install the Adobe Root CA using Adobe Acrobat. Then, this method worked for signing:

Code
        static void CertifyPDF(string filePath)
        {
            string newFilePath = Path.Combine(Path.GetDirectoryName(filePath), "TestCertified.pdf");
            File.Copy(filePath, newFilePath, true);

            //Initialize SB utilities, register necessary factories
            SBUtils.Unit.SetLicenseKey(sbUtilUnlockKey);
            SBPDF.Unit.Initialize();
            SBPDFSecurity.Unit.Initialize();
            SBHTTPCRL.Unit.RegisterHTTPCRLRetrieverFactory();
            SBLDAPCRL.Unit.RegisterLDAPCRLRetrieverFactory();
            SBLDAPCertRetriever.Unit.RegisterLDAPCertificateRetrieverFactory();
            SBHTTPCertRetriever.Unit.RegisterHTTPCertificateRetrieverFactory();
            SBHTTPOCSPClient.Unit.RegisterHTTPOCSPClientFactory();

            using (TElPDFDocument Document = new TElPDFDocument())
            {
                using (FileStream stream = new FileStream(newFilePath, FileMode.Open, FileAccess.ReadWrite))
                {
                    Document.Open(stream);

                    //If we have already applied password protection to the document, use owner password to decrypt before certifying
                    if (((TElPDFPasswordSecurityHandler)Document.EncryptionHandler) != null)
                    {
                        ((TElPDFPasswordSecurityHandler)Document.EncryptionHandler).OwnerPassword = pdfPassword;
                        Document.DecryptionMode = 2;
                        Document.Decrypt();
                    }

                    //Add new signature
                    int signatureIndex = Document.AddSignature();
                    TElPDFSignature sig = Document.get_Signatures(signatureIndex);

                    //Set signature properties
                    sig.SigningTime = DateTime.UtcNow;
                    sig.Invisible = true;
                    sig.SignatureType = SBPDF.Unit.stMDP;

                    using (TElX509CertificateValidator validator = new TElX509CertificateValidator())
                    using (TElPDFAdvancedPublicKeySecurityHandler handler = new TElPDFAdvancedPublicKeySecurityHandler())
                    using (TElHTTPSClient httpClient = new TElHTTPSClient())
                    using (TElHTTPTSPClient tspClient = new TElHTTPTSPClient())
                    using (TElMemoryCertStorage certStorage = new TElMemoryCertStorage())
                    using (TElWinCertStorage systemStore = new TElWinCertStorage())
                    using (TElX509Certificate cert = new TElX509Certificate())
                    using (TElX509Certificate ceCert = new TElX509Certificate())
                    using (TElX509Certificate entrustCert = new TElX509Certificate())
                    using (TElX509Certificate adobeCert = new TElX509Certificate())
                    {
                        //Assign signature handler
                        sig.Handler = handler;

                        //Find and assign certificates in chain
                        systemStore.SystemStores.BeginUpdate();
                        try
                        {
                            systemStore.SystemStores.Clear();
                            //End certificate location
                            systemStore.SystemStores.Add("MY");
                            //Intermediate CA certificate location
                            systemStore.SystemStores.Add("CA");
                            //Root CA certificate location
                            systemStore.SystemStores.Add("Root");
                        }
                        finally
                        {
                            systemStore.SystemStores.EndUpdate();
                        }
                                    
                        for (int i = 0; i < systemStore.Count; i++)
                        {
                            cert = systemStore.get_Certificates(i);
                            //End certificate, we find by organization name
                            if (cert.SubjectName.Organization == orgName)
                            {
                                ceCert = cert;
                            }
                            //Intermediate CA certificate
                            if (cert.SubjectName.CommonName == "Entrust CA for Adobe")
                            {
                                entrustCert = cert;
                            }
                            //Adobe Root CA certificate
                            if (cert.SubjectName.CommonName == "Adobe Root CA")
                            {
                                adobeCert = cert;
                            }
                        }

                        //Assign the http client for the time stamp authority, necessary for embedded timestamp
                        tspClient.HTTPClient = httpClient;
                        tspClient.URL = "http://adobe-tsa.entrust.net/TSS/HttpTspServer";
                        handler.TSPClient = tspClient;

                        //Add certificates in chain order - end cert, intermediate CA, root CA
                        certStorage.Clear();
                        certStorage.Add(ceCert, true);
                        certStorage.Add(entrustCert, false);
                        certStorage.Add(adobeCert, false);
                        handler.CertStorage = certStorage;
                        
                        //Necessary for Entrust CA certificate to validate properly
                        validator.IgnoreCABasicConstraints = true;
                        validator.AddTrustedCertificates(certStorage);
                        
                        //Use CertValidatorPreparedEvent to assign our validator to the handler
                        handler.OnCertValidatorPrepared += new TSBPDFCertValidatorPreparedEvent((object Sender, ref TElX509CertificateValidator CertValidator, TElX509Certificate Cert) =>
                        {
                            CertValidator = validator;
                        });

                        //Assign handler properties
                        handler.AutoCollectRevocationInfo = true;
                        handler.ForceCompleteChainValidation = true;
                        handler.IncludeRevocationInfoToAdbeAttribute = true;
                        handler.PAdESSignatureType = TSBPAdESSignatureType.pastEnhanced;
                        handler.SignatureType = SBPDFSecurity.TSBPDFPublicKeySignatureType.pstPKCS7SHA1;
                        handler.HashAlgorithm = SBConstants.__Global.SB_ALGORITHM_DGST_SHA256;
                        handler.CustomName = "Adobe.PPKLite";
                        handler.ForceCompleteChainValidation = true;
                        handler.DeepValidation = true;

                        Document.Close(true);
                    }
                }
            }
        }


Of course, we still have to manually enter the password to use the token. Hope this helps!
Also by EldoS: Callback File System
Create virtual file systems and disks, expose and manage remote data as if they were files on the local disk.

Reply

Statistics

Topic viewed 1219 times

Number of guests: 1, registered members: 0, in total hidden: 0




|

Back to top

As of July 15, 2016 EldoS Corporation will operate as a division of /n software inc. For more information, please read the announcement.

Got it!