001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: /**
019: * @author Alexander Y. Kleymenov
020: * @version $Revision$
021: */package org.apache.harmony.security.x509;
022:
023: import java.io.IOException;
024: import java.math.BigInteger;
025: import java.util.Arrays;
026: import java.util.Date;
027: import java.util.Iterator;
028: import java.util.List;
029:
030: import javax.security.auth.x500.X500Principal;
031:
032: import org.apache.harmony.security.asn1.ASN1Explicit;
033: import org.apache.harmony.security.asn1.ASN1Integer;
034: import org.apache.harmony.security.asn1.ASN1Sequence;
035: import org.apache.harmony.security.asn1.ASN1SequenceOf;
036: import org.apache.harmony.security.asn1.ASN1Type;
037: import org.apache.harmony.security.asn1.BerInputStream;
038: import org.apache.harmony.security.x501.Name;
039:
040: /**
041: * The class encapsulates the ASN.1 DER encoding/decoding work
042: * with TBSCertList structure which is the part of X.509 CRL
043: * (as specified in RFC 3280 -
044: * Internet X.509 Public Key Infrastructure.
045: * Certificate and Certificate Revocation List (CRL) Profile.
046: * http://www.ietf.org/rfc/rfc3280.txt):
047: *
048: * <pre>
049: * TBSCertList ::= SEQUENCE {
050: * version Version OPTIONAL,
051: * -- if present, MUST be v2
052: * signature AlgorithmIdentifier,
053: * issuer Name,
054: * thisUpdate Time,
055: * nextUpdate Time OPTIONAL,
056: * revokedCertificates SEQUENCE OF SEQUENCE {
057: * userCertificate CertificateSerialNumber,
058: * revocationDate Time,
059: * crlEntryExtensions Extensions OPTIONAL
060: * -- if present, MUST be v2
061: * } OPTIONAL,
062: * crlExtensions [0] EXPLICIT Extensions OPTIONAL
063: * -- if present, MUST be v2
064: * }
065: * </pre>
066: */
067: public class TBSCertList {
068:
069: // the value of version field of the structure
070: private final int version;
071: // the value of signature field of the structure
072: private final AlgorithmIdentifier signature;
073: // the value of issuer field of the structure
074: private final Name issuer;
075: // the value of thisUpdate of the structure
076: private final Date this Update;
077: // the value of nextUpdate of the structure
078: private final Date nextUpdate;
079: // the value of revokedCertificates of the structure
080: private final List revokedCertificates;
081: // the value of crlExtensions field of the structure
082: private final Extensions crlExtensions;
083: // the ASN.1 encoded form of TBSCertList
084: private byte[] encoding;
085:
086: public static class RevokedCertificate {
087: private final BigInteger userCertificate;
088: private final Date revocationDate;
089: private final Extensions crlEntryExtensions;
090:
091: private boolean issuerRetrieved;
092: private X500Principal issuer;
093: private byte[] encoding;
094:
095: public RevokedCertificate(BigInteger userCertificate,
096: Date revocationDate, Extensions crlEntryExtensions) {
097: this .userCertificate = userCertificate;
098: this .revocationDate = revocationDate;
099: this .crlEntryExtensions = crlEntryExtensions;
100: }
101:
102: public Extensions getCrlEntryExtensions() {
103: return crlEntryExtensions;
104: }
105:
106: public BigInteger getUserCertificate() {
107: return userCertificate;
108: }
109:
110: public Date getRevocationDate() {
111: return revocationDate;
112: }
113:
114: /**
115: * Returns the value of Certificate Issuer Extension, if it is
116: * presented.
117: */
118: public X500Principal getIssuer() {
119: if (crlEntryExtensions == null) {
120: return null;
121: }
122: if (!issuerRetrieved) {
123: try {
124: issuer = crlEntryExtensions
125: .valueOfCertificateIssuerExtension();
126: } catch (IOException e) {
127: e.printStackTrace();
128: }
129: issuerRetrieved = true;
130: }
131: return issuer;
132: }
133:
134: public byte[] getEncoded() {
135: if (encoding == null) {
136: encoding = ASN1.encode(this );
137: }
138: return encoding;
139: }
140:
141: public boolean equals(Object rc) {
142: if (!(rc instanceof RevokedCertificate)) {
143: return false;
144: }
145: RevokedCertificate rcert = (RevokedCertificate) rc;
146: return userCertificate.equals(rcert.userCertificate)
147: && ((revocationDate.getTime() / 1000) == (rcert.revocationDate
148: .getTime() / 1000))
149: && ((crlEntryExtensions == null) ? rcert.crlEntryExtensions == null
150: : crlEntryExtensions
151: .equals(rcert.crlEntryExtensions));
152: }
153:
154: public int hashCode() {
155: return userCertificate.hashCode()
156: * 37
157: + (int) revocationDate.getTime()
158: / 1000
159: + (crlEntryExtensions == null ? 0
160: : crlEntryExtensions.hashCode());
161: }
162:
163: /**
164: * Places the string representation of extension value
165: * into the StringBuffer object.
166: */
167: public void dumpValue(StringBuffer buffer, String prefix) {
168: buffer.append(prefix).append("Certificate Serial Number: ") //$NON-NLS-1$
169: .append(userCertificate).append('\n');
170: buffer.append(prefix).append("Revocation Date: ") //$NON-NLS-1$
171: .append(revocationDate);
172: if (crlEntryExtensions != null) {
173: buffer.append('\n').append(prefix).append(
174: "CRL Entry Extensions: ["); //$NON-NLS-1$
175: crlEntryExtensions.dumpValue(buffer, prefix + " "); //$NON-NLS-1$
176: buffer.append(prefix).append(']');
177: }
178: }
179:
180: public static final ASN1Sequence ASN1 = new ASN1Sequence(
181: new ASN1Type[] { ASN1Integer.getInstance(), Time.ASN1,
182: Extensions.ASN1 }) {
183: {
184: setOptional(2);
185: }
186:
187: protected Object getDecodedObject(BerInputStream in) {
188: Object[] values = (Object[]) in.content;
189:
190: return new RevokedCertificate(new BigInteger(
191: (byte[]) values[0]), (Date) values[1],
192: (Extensions) values[2]);
193: }
194:
195: protected void getValues(Object object, Object[] values) {
196: RevokedCertificate rcert = (RevokedCertificate) object;
197:
198: values[0] = rcert.userCertificate.toByteArray();
199: values[1] = rcert.revocationDate;
200: values[2] = rcert.crlEntryExtensions;
201: }
202: };
203: }
204:
205: /**
206: * Constructs the instance of TBSCertList without optional fields.
207: * Take a note, that regarding to the rfc 3280 (p. 49):
208: * "When CRLs are issued, the CRLs MUST be version 2 CRLs, include the date
209: * by which the next CRL will be issued in the nextUpdate field (section
210: * 5.1.2.5), include the CRL number extension (section 5.2.3), and include
211: * the authority key identifier extension (section 5.2.1). Conforming
212: * applications that support CRLs are REQUIRED to process both version 1 and
213: * version 2 complete CRLs that provide revocation information for all
214: * certificates issued by one CA. Conforming applications are NOT REQUIRED
215: * to support processing of delta CRLs, indirect CRLs, or CRLs with a scope
216: * other than all certificates issued by one CA."
217: * @param signature: AlgorithmIdentifier
218: * @param issuer: Name
219: * @param thisUpdate: Time
220: */
221: public TBSCertList(AlgorithmIdentifier signature, Name issuer,
222: Date this Update) {
223: this .version = 1;
224: this .signature = signature;
225: this .issuer = issuer;
226: this .this Update = this Update;
227: this .nextUpdate = null;
228: this .revokedCertificates = null;
229: this .crlExtensions = null;
230: }
231:
232: /**
233: * Constructs the instance of TBSCertList with all optional fields
234: * @param version: version of the CRL. Should be 1 or 2.
235: * Note that if the version of CRL is 1, then nextUpdate,
236: * crlExtensions fields of CRL and crlEntryExtensions field
237: * of CRL entry must not be presented in CRL.
238: * FIXME: do check for it.
239: * @param signature: AlgorithmIdentifier
240: * @param issuer: Name
241: * @param thisUpdate: Time
242: * @param nextUpdate: Time
243: * @param revokedCertificates: List
244: * @param crlExtensions: Extensions
245: */
246: public TBSCertList(int version, AlgorithmIdentifier signature,
247: Name issuer, Date this Update, Date nextUpdate,
248: List revokedCertificates, Extensions crlExtensions) {
249: this .version = version;
250: this .signature = signature;
251: this .issuer = issuer;
252: this .this Update = this Update;
253: this .nextUpdate = nextUpdate;
254: this .revokedCertificates = revokedCertificates;
255: this .crlExtensions = crlExtensions;
256: }
257:
258: // Constructs the object with associated ASN.1 encoding
259: private TBSCertList(int version, AlgorithmIdentifier signature,
260: Name issuer, Date this Update, Date nextUpdate,
261: List revokedCertificates, Extensions crlExtensions,
262: byte[] encoding) {
263: this .version = version;
264: this .signature = signature;
265: this .issuer = issuer;
266: this .this Update = this Update;
267: this .nextUpdate = nextUpdate;
268: this .revokedCertificates = revokedCertificates;
269: this .crlExtensions = crlExtensions;
270: this .encoding = encoding;
271: }
272:
273: /**
274: * Returns the value of version field of the structure.
275: * @return version
276: */
277: public int getVersion() {
278: return version;
279: }
280:
281: /**
282: * Returns the value of signature field of the structure.
283: * @return signature
284: */
285: public AlgorithmIdentifier getSignature() {
286: return signature;
287: }
288:
289: /**
290: * Returns the value of issuer field of the structure.
291: * @return issuer
292: */
293: public Name getIssuer() {
294: return issuer;
295: }
296:
297: /**
298: * Returns the value of thisUpdate field of the structure.
299: * @return thisUpdate
300: */
301: public Date getThisUpdate() {
302: return this Update;
303: }
304:
305: /**
306: * Returns the value of nextUpdate field of the structure.
307: * @return nextUpdate
308: */
309: public Date getNextUpdate() {
310: return nextUpdate;
311: }
312:
313: /**
314: * Returns the value of revokedCertificates field of the structure.
315: * @return revokedCertificates
316: */
317: public List getRevokedCertificates() {
318: return revokedCertificates;
319: }
320:
321: /**
322: * Returns the value of crlExtensions field of the structure.
323: * @return extensions
324: */
325: public Extensions getCrlExtensions() {
326: return crlExtensions;
327: }
328:
329: /**
330: * Returns ASN.1 encoded form of this X.509 TBSCertList value.
331: * @return a byte array containing ASN.1 encode form.
332: */
333: public byte[] getEncoded() {
334: if (encoding == null) {
335: encoding = ASN1.encode(this );
336: }
337: return encoding;
338: }
339:
340: public boolean equals(Object tbs) {
341: if (!(tbs instanceof TBSCertList)) {
342: return false;
343: }
344: TBSCertList tbscert = (TBSCertList) tbs;
345: return (version == tbscert.version)
346: && (signature.equals(tbscert.signature))
347: // FIXME use Name.equals when it will be implemented
348: && (Arrays.equals(issuer.getEncoded(), tbscert.issuer
349: .getEncoded()))
350: && ((this Update.getTime() / 1000) == (tbscert.this Update
351: .getTime() / 1000))
352: && ((nextUpdate == null) ? tbscert.nextUpdate == null
353: : ((nextUpdate.getTime() / 1000) == (tbscert.nextUpdate
354: .getTime() / 1000)))
355: && ((((revokedCertificates == null) || (tbscert.revokedCertificates == null)) && (revokedCertificates == tbscert.revokedCertificates)) || (revokedCertificates
356: .containsAll(tbscert.revokedCertificates) && (revokedCertificates
357: .size() == tbscert.revokedCertificates.size())))
358: && ((crlExtensions == null) ? tbscert.crlExtensions == null
359: : crlExtensions.equals(tbscert.crlExtensions));
360: }
361:
362: public int hashCode() {
363: return ((version * 37 + signature.hashCode()) * 37 + issuer
364: .getEncoded().hashCode())
365: * 37 + (int) this Update.getTime() / 1000;
366: }
367:
368: /**
369: * Places the string representation of extension value
370: * into the StringBuffer object.
371: */
372: public void dumpValue(StringBuffer buffer) {
373: buffer.append("X.509 CRL v").append(version); //$NON-NLS-1$
374: buffer.append("\nSignature Algorithm: ["); //$NON-NLS-1$
375: signature.dumpValue(buffer);
376: buffer.append(']');
377: buffer
378: .append("\nIssuer: ").append(issuer.getName(X500Principal.RFC2253)); //$NON-NLS-1$
379: buffer.append("\n\nThis Update: ").append(this Update); //$NON-NLS-1$
380: buffer
381: .append("\nNext Update: ").append(nextUpdate).append('\n'); //$NON-NLS-1$
382: if (revokedCertificates != null) {
383: buffer.append("\nRevoked Certificates: ") //$NON-NLS-1$
384: .append(revokedCertificates.size()).append(" ["); //$NON-NLS-1$
385: int number = 1;
386: for (Iterator it = revokedCertificates.iterator(); it
387: .hasNext();) {
388: buffer.append("\n [").append(number++).append(']'); //$NON-NLS-1$
389: ((RevokedCertificate) it.next())
390: .dumpValue(buffer, " "); //$NON-NLS-1$
391: buffer.append('\n');
392: }
393: buffer.append("]\n"); //$NON-NLS-1$
394: }
395: if (crlExtensions != null) {
396: buffer.append("\nCRL Extensions: ") //$NON-NLS-1$
397: .append(crlExtensions.size()).append(" ["); //$NON-NLS-1$
398: crlExtensions.dumpValue(buffer, " "); //$NON-NLS-1$
399: buffer.append("]\n"); //$NON-NLS-1$
400: }
401: }
402:
403: /**
404: * X.509 TBSCertList encoder/decoder.
405: */
406: public static final ASN1Sequence ASN1 = new ASN1Sequence(
407: new ASN1Type[] { ASN1Integer.getInstance(), // version
408: AlgorithmIdentifier.ASN1, // signature
409: Name.ASN1, // issuer
410: Time.ASN1, // thisUpdate
411: Time.ASN1, // nextUpdate
412: new ASN1SequenceOf(RevokedCertificate.ASN1), // revokedCertificates
413: new ASN1Explicit(0, Extensions.ASN1) // crlExtensions
414: }) {
415: {
416: setOptional(0);
417: setOptional(4);
418: setOptional(5);
419: setOptional(6);
420: }
421:
422: protected Object getDecodedObject(BerInputStream in)
423: throws IOException {
424: Object[] values = (Object[]) in.content;
425: return new TBSCertList((values[0] == null) ? 1
426: : ASN1Integer.toIntValue(values[0]) + 1,
427: (AlgorithmIdentifier) values[1], (Name) values[2],
428: (Date) values[3], (Date) values[4],
429: (List) values[5], (Extensions) values[6], in
430: .getEncoded());
431: }
432:
433: protected void getValues(Object object, Object[] values) {
434: TBSCertList tbs = (TBSCertList) object;
435: values[0] = (tbs.version > 1) ? ASN1Integer
436: .fromIntValue(tbs.version - 1) : null;
437: values[1] = tbs.signature;
438: values[2] = tbs.issuer;
439: values[3] = tbs.this Update;
440: values[4] = tbs.nextUpdate;
441: values[5] = tbs.revokedCertificates;
442: values[6] = tbs.crlExtensions;
443: }
444: };
445: }
|