Take the 2-minute tour ×
Information Security Stack Exchange is a question and answer site for Information security professionals. It's 100% free, no registration required.

I'm working on XAdES implementation for Java, as such I require cryptographic implementation to be able to verify certificates in the past.

Unfortunately I have problems verifying revoked certificates. Situation looks like this:

(time)
↑
| ← in 1d:    CA and user certificate notAfter date
|
| ← *now*:      user certificate validation using time from time stamp (12h ago)
| 
| ← 1h ago:   CRL publishing
|
| ← 6h ago:   user certificate revocation (reason: unknown)
| 
| ← 12h ago:  signature creation and time stamping
|
| ← 1 day ago:
|             CA notBefore Date
|             user notBefore Date

The Sun cryptographic provider (X509CRLSelector) can't use CRLs from "future", so validation using CRL published 1h ago when verification date is 12h in past fails outright. This behaviour is completely wrong for my use case as the Polish law requires a 1h grace period between signature time stamping and CRL publication before the document can be fully validated.

When using Bouncy Castle cryptographic provider, the X509CRLSelector problem is fixed, but when I try to validate the revoked certificate I get attached stack trace.

Do I understand certificate validation with revocation wrong (and the BC failure is correct behaviour) or is this CertPathBuilder behaviour wrong?

xades4j.providers.CannotBuildCertificationPathException: Certification path could not be validated.
    at xades4j.providers.impl.PKIXCertificateValidationProvider.validateWithParams(PKIXCertificateValidationProvider.java:272)
    at xades4j.providers.impl.PKIXCertificateValidationProvider.validate(PKIXCertificateValidationProvider.java:235)
    at xades4j.verification.XadesVerifierImpl.verify(XadesVerifierImpl.java:175)
    at xades4j.verification.AgedTimeStampTest.verifySignature(AgedTimeStampTest.java:339)
    at xades4j.verification.AgedTimeStampTest.testT_3v(AgedTimeStampTest.java:287)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:44)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:41)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20)
    at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:28)
    at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:31)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:73)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:46)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:180)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:41)
    at org.junit.runners.ParentRunner$1.evaluate(ParentRunner.java:173)
    at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:28)
    at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:31)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:220)
    at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50)
    at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)
Caused by: java.security.cert.CertPathBuilderException: Certification path could not be validated.
    at org.bouncycastle.jce.provider.PKIXCertPathBuilderSpi.engineBuild(PKIXCertPathBuilderSpi.java:112)
    at java.security.cert.CertPathBuilder.build(CertPathBuilder.java:238)
    at xades4j.providers.impl.PKIXCertificateValidationProvider.validateWithParams(PKIXCertificateValidationProvider.java:268)
    ... 28 more
Caused by: org.bouncycastle.jce.exception.ExtCertPathValidatorException: Certificate revocation after Thu Oct 18 08:46:09 CEST 2012, reason: unspecified
    at org.bouncycastle.jce.provider.RFC3280CertPathUtilities.processCertA(RFC3280CertPathUtilities.java:1516)
    at org.bouncycastle.jce.provider.PKIXCertPathValidatorSpi.engineValidate(PKIXCertPathValidatorSpi.java:274)
    at java.security.cert.CertPathValidator.validate(CertPathValidator.java:250)
    at org.bouncycastle.jce.provider.PKIXCertPathBuilderSpi.build(PKIXCertPathBuilderSpi.java:195)
    at org.bouncycastle.jce.provider.PKIXCertPathBuilderSpi.engineBuild(PKIXCertPathBuilderSpi.java:105)
    ... 30 more
Caused by: org.bouncycastle.jce.provider.AnnotatedException: Certificate revocation after Thu Oct 18 08:46:09 CEST 2012, reason: unspecified
    at org.bouncycastle.jce.provider.RFC3280CertPathUtilities.checkCRLs(RFC3280CertPathUtilities.java:2013)
    at org.bouncycastle.jce.provider.RFC3280CertPathUtilities.processCertA(RFC3280CertPathUtilities.java:1506)
    ... 34 more

The validation time for this stacktrace was Thu Oct 18 02:46:07 CEST 2012

share|improve this question

2 Answers 2

up vote 2 down vote accepted

I've read the standard and it looks to me like the BC is not implementing it properly.

The RFC 5280, section 6.1.3, item (a):

         (3)  At the current time, the certificate is not revoked.  This 
              may be determined by obtaining the appropriate CRL 
              (Section 6.3), by status information, or by out-of-band 
              mechanisms.

In this situation, at current time certificate is not revoked, it will be revoked on some specific date in future.

Implementation should treat certificate as revoked before its revocation date when Invalidity Date extension is present (RFC 5280, section 5.3.2).

For example, if a CA issues CRLs once a week, and some system will be decommissioned in middle of this period, then it's not in violation of RFC to put its revocationDate in the future. I'd say that this is expected behaviour by a CA when automated systems that depend on this CA download CRLs only once a week. Such use is not in violation of RFC, as it only expects the revocationDate to be later than time of publishing of preceding CRL. But such use case is not supported by BC.

What's more, this is behaviour RFC 3161 depends on. As stated in section 4 of RFC 3161:

   1. When a TSA shall not be used anymore, but the TSA private key has 
      not been compromised, the authority's certificate SHALL be 
      revoked.  When the reasonCode extension relative to the revoked 
      certificate from the TSA is present in the CRL entry extensions, 
      it SHALL be set either to unspecified (0), affiliationChanged (3), 
      superseded (4) or cessationOfOperation (5).  In that case, at any 
      future time, the tokens signed with the corresponding key will be 
      considered as invalid, but tokens generated before the revocation 
      time will remain valid.  When the reasonCode extension relative to 
      the revoked certificate from the TSA is not present in the CRL 
      entry extensions, then all the tokens that have been signed with 
      the corresponding key SHALL be considered as invalid.  For that 
      reason, it is recommended to use the reasonCode extension. 

So an implementation shouldn't treat unspecified revocation reason as a very grave situation, on the line of keyCompromise. I don't expect BC to check if a certificate being verified is a TSA certificate and treat is as valid even after its notAfterDate when appropriate CRL is present, but I think I can expect BC to treat certificate as valid before their revocationDate when I provided CRL published before certificate validity end.

share|improve this answer

I'd reccomend moving this over to SO, since it is code related - there may be a workaround; but it appears that the issue is more related to the validation of a revoked certificate post publication of a new CRL that contains that certificate. I'm not 100% on your scenario - so please correct my assumptions if needed. Also recall that you need to be able to validate the entire path depending on your implementation, and you may need to remove any CRL or CRLDP that was issued after the initial certificate.

An explanation of the CA path issue is available on SO here: http://stackoverflow.com/questions/2457795/x-509-certificate-validation-with-java-and-bouncycastle

There is also a good discussion on pretty much your exact issue that's come up a couple times on the BC forums. I don't know the version of BC you're using - but take a look at the discussion point here (effectively removing the CRL that was issued in the future)

http://bouncy-castle.1462172.n4.nabble.com/CertPath-validation-bug-in-BC-1-46-td3330300.html

The pertinent section:

BC's path validation implementation doesn't compare the passed time to Now. The client isn't protected against glitches in the CA's systems, and the library doesn't perform sanity checking of its inputs -- and because the output of the validator is a boolean, the library also doesn't return what rule it failed on or why.

Another similar thread is here: http://www.bouncycastle.org/devmailarchive/msg10835.html

Effectively: you'd need to be able to get the old CRL (where the cert was still valid) in order for BC to return as valid.

share|improve this answer
    
It is code related, but that's not the main point. What I want to know is if this use case is covered by standard. –  Hubert Kario Oct 20 '12 at 16:29

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.