The sequence of checks in getValidationState is as follows:
1.a) loop thru certs checking expiration state - to see if cert is not yet valid or expired.
1.b) check if it is timestamped, and was valid when timestamped
- result of 1) is state.timeValid, and state.certValidity
2.) (if not skip flag) - check if cert is in session store - if so return setting state.trustDecidion to GRANTED_FOR_THIS_SESSION
3.) loop thru certs looking for blacklisted cert (can throw exception to exit)
4.) see if root cert is in CA - set state.rootCAValid to false if not.
5.) validitate chain - this can also set state.rootCAValid to false or throw exception
6.) (if not skip flag) perform revocation check (can throw exception)
7.) see if it is in denied store (if so, set state.trustDecision = DENIED and return)
8.) if state.rootCAValid is false, if so , return here (setting state.trustDecidion.UNKNOWN)
9.) set state.publisher to the string extracted from the first cert in the chain.
10.) check if the cert is in the permanent trust stores, if it is, set state.trustDecidion to state.expirationDate, and return.
11.) set state.trustDecidion.UNKNOWN and return
So the problem is as follows:
we need to get to step 9, which requires 4, 5, and 8 in even in the case of step 2, where cert is already in session store.
but in this case we don't want to do other steps, 3 and 6. Step 7 really doesn't really mater, since cert cant be both accepted and denied in one session.
so answer is simple, in step 2, instead of exiting, we just set a flag (which can even be the trustDecision set to GRANTED_FOR_SESSION), that causes us to skip steps 3, 6,7, we can even move step 3 so it is after 5, and all the steps being skipped will be together.
The above steps have clause, "if not skip flag" which is referring to the existing input arg "skipSessionStoreAndRevocationCheck"
this is not actually used anymore as getValidationState is only called now from SandboxSecurity and TrustDecider itself, and in each case the arg is false.
a little investigation shows:
Problem starts in TrustDecider.validateChain, where it calls lap.storeMainPublisherAndTitle with the publisher from the ValidationState.
but publisher is only set in validationState if we get thru to point where it is known to be ca-signed.
but if the cert is already in the session store, we don't get that far.
The lap is trying to take care of this by only setting it only once in DefaultLocalApplicationsProperties.storeMainPublisherAndTitle() (it won't set unless both are null) , and clearing these fields when an applet starts.
But when there are multiple applets with the same certificate, and it is not the first applet that does live connect calls, the second applet will never verify the signature (it is already in the trusted session store), so it never sets the publisher field in the ValidationState, so verifyChain then calls storeMainPublisherAndTitle(null, null) and the result is UNKNOWN
There is not really a race condition, it will happen any there are more than one applet signed by the same cert, or when the same applet is invoked from multiple html pages, or in some cases when an app is reloaded.
then after accepting security dialog - deny the liveconnect dialog. (first time it seems to have publisher correct)
Then reload it, now the session store is still containing the cert, so the dialog will have publisher UNKNOWN