|
1 | 1 | package org.bouncycastle.jce.provider; |
2 | 2 |
|
3 | 3 | import java.io.ByteArrayInputStream; |
| 4 | +import java.math.BigInteger; |
| 5 | +import java.util.Date; |
4 | 6 |
|
5 | 7 | import junit.framework.TestCase; |
| 8 | +import org.bouncycastle.asn1.ASN1GeneralizedTime; |
| 9 | +import org.bouncycastle.asn1.ASN1Integer; |
| 10 | +import org.bouncycastle.asn1.ASN1Sequence; |
| 11 | +import org.bouncycastle.asn1.DERBitString; |
| 12 | +import org.bouncycastle.asn1.DEROctetString; |
| 13 | +import org.bouncycastle.asn1.DERSequence; |
| 14 | +import org.bouncycastle.asn1.nist.NISTObjectIdentifiers; |
| 15 | +import org.bouncycastle.asn1.ocsp.BasicOCSPResponse; |
| 16 | +import org.bouncycastle.asn1.ocsp.CertID; |
| 17 | +import org.bouncycastle.asn1.ocsp.CertStatus; |
| 18 | +import org.bouncycastle.asn1.ocsp.ResponderID; |
| 19 | +import org.bouncycastle.asn1.ocsp.ResponseData; |
| 20 | +import org.bouncycastle.asn1.ocsp.SingleResponse; |
| 21 | +import org.bouncycastle.asn1.x500.X500Name; |
| 22 | +import org.bouncycastle.asn1.x509.AlgorithmIdentifier; |
| 23 | +import org.bouncycastle.asn1.x509.Extensions; |
6 | 24 | import org.bouncycastle.util.Arrays; |
7 | 25 | import org.bouncycastle.util.Properties; |
8 | 26 | import org.bouncycastle.util.io.StreamOverflowException; |
@@ -90,4 +108,87 @@ public void testOverLongResponseNamesTheLimit() |
90 | 108 |
|
91 | 109 | assertTrue(Arrays.areEqual(response, OcspCache.readResponse(new ByteArrayInputStream(response), 1024))); |
92 | 110 | } |
| 111 | + /** |
| 112 | + * A cached response is only reusable while it states a validity interval covering the time |
| 113 | + * being validated for. RFC 6960 sec. 4.2.2.1: "if nextUpdate is not set, the responder is |
| 114 | + * indicating that newer revocation information is available all the time" - so there is no |
| 115 | + * interval to reuse it over, and the cache must go back to the responder. |
| 116 | + */ |
| 117 | + public void testResponseWithoutNextUpdateIsNeverCurrent() |
| 118 | + throws Exception |
| 119 | + { |
| 120 | + Date now = new Date(); |
| 121 | + CertID certID = certID(); |
| 122 | + |
| 123 | + BasicOCSPResponse withNextUpdate = response(certID, minutesFromNow(now, -5), minutesFromNow(now, 60)); |
| 124 | + assertTrue("response inside its own validity interval was not current", |
| 125 | + OcspCache.isCertIDFoundAndCurrent(withNextUpdate, now, certID)); |
| 126 | + |
| 127 | + BasicOCSPResponse expired = response(certID, minutesFromNow(now, -120), minutesFromNow(now, -60)); |
| 128 | + assertFalse("expired response was current", OcspCache.isCertIDFoundAndCurrent(expired, now, certID)); |
| 129 | + |
| 130 | + BasicOCSPResponse noNextUpdate = response(certID, minutesFromNow(now, -5), null); |
| 131 | + assertFalse("response with no nextUpdate was reused from the cache", |
| 132 | + OcspCache.isCertIDFoundAndReusable(noNextUpdate, now, certID)); |
| 133 | + |
| 134 | + // however old it is |
| 135 | + BasicOCSPResponse ancient = response(certID, minutesFromNow(now, -60 * 24 * 365), null); |
| 136 | + assertFalse("year-old response with no nextUpdate was reused from the cache", |
| 137 | + OcspCache.isCertIDFoundAndReusable(ancient, now, certID)); |
| 138 | + |
| 139 | + // but nothing is rejected by that: a responder is entitled not to state a nextUpdate, and |
| 140 | + // the response it just gave us is used for the check it arrived for |
| 141 | + assertTrue("freshly fetched response with no nextUpdate was refused", |
| 142 | + OcspCache.isCertIDFoundAndCurrent(noNextUpdate, now, certID)); |
| 143 | + |
| 144 | + // the interval is still honoured where one is stated |
| 145 | + assertTrue("response inside its validity interval was not reusable", |
| 146 | + OcspCache.isCertIDFoundAndReusable(withNextUpdate, now, certID)); |
| 147 | + assertFalse("expired response was reusable", OcspCache.isCertIDFoundAndReusable(expired, now, certID)); |
| 148 | + } |
| 149 | + |
| 150 | + /** |
| 151 | + * "Responses whose thisUpdate time is later than the local system time SHOULD be considered |
| 152 | + * unreliable" - RFC 6960 sec. 4.2.2.1. Clock skew between us and the responder is allowed for. |
| 153 | + */ |
| 154 | + public void testResponseDatedInTheFuture() |
| 155 | + throws Exception |
| 156 | + { |
| 157 | + Date now = new Date(); |
| 158 | + CertID certID = certID(); |
| 159 | + |
| 160 | + BasicOCSPResponse withinSkew = response(certID, minutesFromNow(now, 5), minutesFromNow(now, 60)); |
| 161 | + assertTrue("response inside the clock skew allowance was rejected", |
| 162 | + OcspCache.isCertIDFoundAndCurrent(withinSkew, now, certID)); |
| 163 | + |
| 164 | + BasicOCSPResponse fromTheFuture = response(certID, minutesFromNow(now, 60), minutesFromNow(now, 120)); |
| 165 | + assertFalse("response dated an hour ahead was current", |
| 166 | + OcspCache.isCertIDFoundAndCurrent(fromTheFuture, now, certID)); |
| 167 | + |
| 168 | + assertFalse("absent thisUpdate treated as future", OcspCache.isFromTheFuture(null, now)); |
| 169 | + } |
| 170 | + |
| 171 | + private static ASN1GeneralizedTime minutesFromNow(Date now, int minutes) |
| 172 | + { |
| 173 | + return new ASN1GeneralizedTime(new Date(now.getTime() + (minutes * 60 * 1000L))); |
| 174 | + } |
| 175 | + |
| 176 | + private static CertID certID() |
| 177 | + { |
| 178 | + return new CertID(new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha256), |
| 179 | + new DEROctetString(new byte[32]), new DEROctetString(new byte[32]), new ASN1Integer(BigInteger.ONE)); |
| 180 | + } |
| 181 | + |
| 182 | + private static BasicOCSPResponse response(CertID certID, ASN1GeneralizedTime thisUpdate, |
| 183 | + ASN1GeneralizedTime nextUpdate) |
| 184 | + { |
| 185 | + SingleResponse single = new SingleResponse(certID, new CertStatus(), thisUpdate, nextUpdate, |
| 186 | + (Extensions)null); |
| 187 | + |
| 188 | + ResponseData responseData = new ResponseData(new ResponderID(new X500Name("CN=Test Responder")), |
| 189 | + thisUpdate, new DERSequence(single), (Extensions)null); |
| 190 | + |
| 191 | + return new BasicOCSPResponse(responseData, new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha256), |
| 192 | + new DERBitString(new byte[]{ 1 }), (ASN1Sequence)null); |
| 193 | + } |
93 | 194 | } |
0 commit comments