You are viewing limited content. For full access, please sign in.

Question

Question

Signature Validation

asked on February 25, 2014

Is there a way to validate signatures using code and get the same result that the client does?

 

If I do a:

singleSig.Verify(SignatureVerificationFlags.None) // singleSig  is DocumentSignatureInfo

 

Then the code tells me some signatures are invalid but the UI says they are ok (The UI is correct is giving me the result I want based on my understanding of what constitutes a valid signature from the whitepapers). In fact all of the flag options might give a false false at some point except for “IgnoreKeyValidity” which can give a false true.

 

This is Rio 9.1.1.486 and the code validation is done in a workflow SDK script.

 

Thanks.

 

0 0

Replies

replied on February 25, 2014

Why not use the built-in Get Signatures activity?

0 0
replied on February 25, 2014

Thanks Miruna.

Mainly because I want to do this outside workflow, I was using workflow as a quick way to check out the API.

However, even using that activity I still don't get the same results as the UI. I'm assuming the token to check is "ForEachRow_Is Trusted". Maybe I'm missing something...

0 0
replied on February 26, 2014 Show version history

I would suggest, at least as an initial change, using the other Verify method on DocumentSignatureInfo since it will return a VerificationStatus flag which you can use to get more information on exactly why the program thinks the signature is invalid, as opposed to this Verify method that just gives a boolean. You can use the other method like this:

SignutareVerificationOptions param = new SignutareVerificationOptions();

// Set your flags here
// Note that these flags can be combined with the & operator
param.VerificationFlags = SignatureVerificationFlags.None;

// If you want to check the timestamp validity, you have to set the time manually
if (param.HasFlag(SignatureVerificationFlags.CheckSignatureDateForExpiration))
    param.VerificationTime = param.Timestamp.ToLocalTime();

VerificationStatus status = singleSig.Verify(param);

// This gets you a VerificationStatus that you can examine
if (status == VerificationStatus.Success)
{
    // Verification successful
}
else
{
    // Verification unsuccessful, use the VerificationStatus to determine why.
}

My suspicion is that once you try this you will get VerificationStatus.Invalid. If this is the case, it is probably because the Workflow service is running as a user that does not have access to the certificate. To verify, check:

if (singleSig.Certificate == null)
    // Your user can't find the certificate

In that case, try running it as a standalone application using the same user as the client and your results should match.

0 0
replied on February 26, 2014

Thanks Matthew.

 

I tried your code with some tweaks and here's what I found:

  • In workflow I will get either Success or Untrusted with a null cert, nothing else. In some Untrusted cases the document is modified and that should be reason, in others it should be Successful and still comes up Untrusted with a null cert, the latter are older signatures. Not sure cert access could be the issue since some verifications do come back Successful...but in any case I gave the workflow user "manage cert" privileges and that did not change anything.
  • When I tried a console app the Verify() call threw a LaserficheRepositoryException with a message "Certificate could not be loaded" using the same login as I used on the client to get good results.

Let me know you have any further thoughts on this, seems like I’m close.

 

Here's my SDK task code:

DocumentInfo di = (DocumentInfo)this.BoundEntryInfo;
foreach (DocumentSignatureInfo singleSig in di.GetSignatures())
{
    SignatureVerificationOptions param = new SignatureVerificationOptions();

    param.VerificationFlags =
        SignatureVerificationFlags.CheckSignatureDateForExpiration &
        SignatureVerificationFlags.VerifyCurrentVersion;

    if (param.VerificationFlags.HasFlag(SignatureVerificationFlags.CheckSignatureDateForExpiration))
        param.VerificationTime = singleSig.Timestamp.Date;

    VerificationStatus status = singleSig.Verify(param);

    if (status == VerificationStatus.Success)
    {
        this.WorkflowApi.TrackInformation(
            singleSig.Id + " " + status.ToString() +
            (singleSig.Certificate == null ? " cert is null" : " cert is not null"));
    }
    else
    {
        this.WorkflowApi.TrackWarning(
            singleSig.Id + " " + status.ToString() +
            (singleSig.Certificate == null ? " cert is null" : " cert is not null"));
    }
}

And here is the console code:


var sess = new Session();

try
{
    sess.Connect(new RepositoryRegistration("abc", "123"));
    sess.LogIn();

    Console.WriteLine(sess.UserName);

    DocumentInfo di = new DocumentInfo(docId, sess);

    foreach (DocumentSignatureInfo singleSig in di.GetSignatures())
    {
        SignatureVerificationOptions param = new SignatureVerificationOptions();

        param.VerificationFlags =
            SignatureVerificationFlags.CheckSignatureDateForExpiration &
            SignatureVerificationFlags.VerifyCurrentVersion;

        if (param.VerificationFlags.HasFlag(SignatureVerificationFlags.CheckSignatureDateForExpiration))
            param.VerificationTime = singleSig.Timestamp.Date;

        VerificationStatus status = singleSig.Verify(param);

        Console.WriteLine(
            singleSig.Id + " " + status.ToString() +
            (singleSig.Certificate == null ? " cert is null" : " cert is not null"));
    }
}
catch (Exception exp)
{
    Console.WriteLine(exp.ToString());
}
finally
{
    if (sess.IsConnected)
        sess.Close();
}

 

0 0
replied on February 26, 2014

After a little more digging, it looks like the issue is that you probably need to load the Laserfiche Certificate Store to match what the client is doing. Given that you have a DocumentSignatureInfo object already, the best way to do this is probably to grab the certificate store, search it for the certificate you're looking for, and put that certificate into the ExtraStore property of your SignatureVerificationOptions. Something like this:

 

// Get the LfCertificateStore, given an LfSession session
LfCertificateStore extraStore = new LfCertificateStore(session);

// Search the store for your certificate
LfX509Certificate cert = extraStore.GetCertificate(singleSig.SigningCertificateThumbprint);

// Insert into your options
// SignatureVerificationOptions param = new SignatureVerificationOptions();
param.ExtraStore.Add(cert.Certificate);

// Or, you could iterate through all certificates in extraStore and
// add each one individually.

Add this logic before your Verify call and it *should* match the client. Let me know if this works for you.

0 0
replied on February 26, 2014

Hi Matthew, the getting the cert part worked but still getting the same exception on call to Verify.

 

Here's the present code:

        static void validateRA(int docId, string server, string repo)
        {
            var sess = new Session();

            try
            {
                sess.Connect(new RepositoryRegistration(server, repo));
                sess.LogIn();
                LfCertificateStore extraStore = new LfCertificateStore(sess);
                DocumentInfo di = new DocumentInfo(docId, sess);

                foreach (DocumentSignatureInfo singleSig in di.GetSignatures())
                {
                    Console.WriteLine(singleSig.Id + " date: " + singleSig.Timestamp );
                    SignatureVerificationOptions param = new SignatureVerificationOptions();

                    //foreach (LfX509Certificate cert in extraStore.GetCertificates(singleSig.Signer))
                    //{
                    //    Console.WriteLine(" cert " + cert.ToString());
                    //    param.ExtraStore.Add(cert.Certificate);
                    //}

                    LfX509Certificate cert = extraStore.GetCertificate(singleSig.SigningCertificateThumbprint);
                    Console.WriteLine(" cert: " + cert.Certificate.ToString());
                    param.ExtraStore.Add(cert.Certificate);

                    param.VerificationFlags =
                        SignatureVerificationFlags.CheckSignatureDateForExpiration |
                        SignatureVerificationFlags.VerifyCurrentVersion;

                    if (param.VerificationFlags.HasFlag(SignatureVerificationFlags.CheckSignatureDateForExpiration))
                        param.VerificationTime = singleSig.Timestamp.Date;

                    VerificationStatus status = singleSig.Verify(param);

                    Console.WriteLine(
                        singleSig.Id + " " + status.ToString() +
                        (singleSig.Certificate == null ? " cert is null" : " cert is not null"));
                }
            }
            catch (Exception exp)
            {
                Console.WriteLine(exp.ToString());
            }
            finally
            {
                if (sess.IsConnected)
                    sess.Close();
            }
        }

Laserfiche.RepositoryAccess.LaserficheRepositoryException: Certificate could not be loaded.
   at Laserfiche.RepositoryAccess.DocumentSignatureInfo.LoadCertificate()
   at Laserfiche.RepositoryAccess.DocumentSignatureInfo.Verify(SignatureVerificationOptions param)
   at TestSignatures.ProgramTestSignatures.validateRA(Int32 docId, String server, String repo) in ...

0 0
replied on February 26, 2014

Try with the for loop instead of adding the single certificate, and change the for loop iterator to

 

LfX509Certificate cert in extraStore.GetAllCertificates()

to load all certificates in the Laserfiche store. What's probably going on is that in order to build the certificate chain you need other certificates besides the one whose thumbprint is given in the signature info. I didn't suggest this originally because it can be very expensive, but it might be the only way to go here.

0 0
replied on February 27, 2014

The good news is this successfully pulled all 13 certs in this repository...the bad news is I still get the same exception from Verify().

Looks like it's just ignoring those certs added to ExtraStore or there is some other missing link.

Also, these are all user certs and there is no countersigning, so not sure they establish a chain.

 

                    int i = 0;
                    foreach (LfX509Certificate cert in extraStore.GetAllCertificates())
                    {
                        Console.WriteLine("** CERT " + (++i).ToString() + " ** : " + cert.Certificate.ToString());
                        param.ExtraStore.Add(cert.Certificate);
                    }

                    param.VerificationFlags =
                        SignatureVerificationFlags.CheckSignatureDateForExpiration |
                        SignatureVerificationFlags.VerifyCurrentVersion;

                    if (param.VerificationFlags.HasFlag(SignatureVerificationFlags.CheckSignatureDateForExpiration))
                        param.VerificationTime = singleSig.Timestamp.Date;

                    VerificationStatus status = singleSig.Verify(param);

 

0 0
replied on February 28, 2014

Hey Robert, sorry for the delay responding. Can post the ErrorCode and Message from the LaserficheRepositoryException that you're getting?

0 0
replied on March 3, 2014

No problem.

 

Error Code: 0
Message: Certificate could not be loaded.

 

There's no inner exception present either. Let me know if I can look anywhere else for information.

0 0
replied on March 24, 2014

Hi Matthew,

 

The signature verification is working in SDK 9.1.1, however, you do in fact need to load the old certificates otherwise it appears to just work off the latest cert for each user which gives you "Untrusted" when checking older signatures.

 

In an unrelated note I believe there is a bug with DocumentInfo: IsUnderVersionControl always returns false so I had to try/catch on GetVersionHistory() which gives correct results so far.

 

0 0
replied on March 24, 2014

Robert, glad to hear that the new version fixed this issue. As for the IsUnderVersionControl property: It will always return false until you tell the DocumentInfo to populate its properties from the server using the Refresh method. If you need to access those properties, use Refresh(false) after you create the info.

0 0
You are not allowed to follow up in this post.

Sign in to reply to this post.