001: /*
002: * Copyright 2002-2006 Sun Microsystems, Inc. All Rights Reserved.
003: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004: *
005: * This code is free software; you can redistribute it and/or modify it
006: * under the terms of the GNU General Public License version 2 only, as
007: * published by the Free Software Foundation. Sun designates this
008: * particular file as subject to the "Classpath" exception as provided
009: * by Sun in the LICENSE file that accompanied this code.
010: *
011: * This code is distributed in the hope that it will be useful, but WITHOUT
012: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014: * version 2 for more details (a copy is included in the LICENSE file that
015: * accompanied this code).
016: *
017: * You should have received a copy of the GNU General Public License version
018: * 2 along with this work; if not, write to the Free Software Foundation,
019: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020: *
021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022: * CA 95054 USA or visit www.sun.com if you need additional information or
023: * have any questions.
024: */
025:
026: package sun.security.x509;
027:
028: import java.io.IOException;
029: import java.util.*;
030:
031: import sun.security.util.BitArray;
032: import sun.security.util.DerOutputStream;
033: import sun.security.util.DerValue;
034:
035: /**
036: * Represent the DistributionPoint sequence used in the CRL
037: * Distribution Points Extension (OID = 2.5.29.31).
038: * <p>
039: * The ASN.1 definition for this is:
040: * <pre>
041: * DistributionPoint ::= SEQUENCE {
042: * distributionPoint [0] DistributionPointName OPTIONAL,
043: * reasons [1] ReasonFlags OPTIONAL,
044: * cRLIssuer [2] GeneralNames OPTIONAL }
045: *
046: * DistributionPointName ::= CHOICE {
047: * fullName [0] GeneralNames,
048: * nameRelativeToCRLIssuer [1] RelativeDistinguishedName }
049: *
050: * ReasonFlags ::= BIT STRING {
051: * unused (0),
052: * keyCompromise (1),
053: * cACompromise (2),
054: * affiliationChanged (3),
055: * superseded (4),
056: * cessationOfOperation (5),
057: * certificateHold (6),
058: * privilegeWithdrawn (7),
059: * aACompromise (8) }
060: *
061: * GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName
062: *
063: * GeneralName ::= CHOICE {
064: * otherName [0] INSTANCE OF OTHER-NAME,
065: * rfc822Name [1] IA5String,
066: * dNSName [2] IA5String,
067: * x400Address [3] ORAddress,
068: * directoryName [4] Name,
069: * ediPartyName [5] EDIPartyName,
070: * uniformResourceIdentifier [6] IA5String,
071: * iPAddress [7] OCTET STRING,
072: * registeredID [8] OBJECT IDENTIFIER }
073: *
074: * RelativeDistinguishedName ::=
075: * SET OF AttributeTypeAndValue
076: *
077: * AttributeTypeAndValue ::= SEQUENCE {
078: * type AttributeType,
079: * value AttributeValue }
080: *
081: * AttributeType ::= OBJECT IDENTIFIER
082: *
083: * AttributeValue ::= ANY DEFINED BY AttributeType
084: * </pre>
085: * <p>
086: * Instances of this class are designed to be immutable. However, since this
087: * is an internal API we do not use defensive cloning for values for
088: * performance reasons. It is the responsibility of the consumer to ensure
089: * that no mutable elements are modified.
090: *
091: * @author Anne Anderson
092: * @author Andreas Sterbenz
093: * @version 1.11, 05/05/07
094: * @since 1.4.2
095: * @see CRLDistributionPointsExtension
096: */
097: public class DistributionPoint {
098:
099: // reason flag bits
100: // NOTE that these are NOT quite the same as the CRL reason code extension
101: public final static int KEY_COMPROMISE = 1;
102: public final static int CA_COMPROMISE = 2;
103: public final static int AFFILIATION_CHANGED = 3;
104: public final static int SUPERSEDED = 4;
105: public final static int CESSATION_OF_OPERATION = 5;
106: public final static int CERTIFICATE_HOLD = 6;
107: public final static int PRIVILEGE_WITHDRAWN = 7;
108: public final static int AA_COMPROMISE = 8;
109:
110: private static final String[] REASON_STRINGS = { null,
111: "key compromise", "CA compromise", "affiliation changed",
112: "superseded", "cessation of operation", "certificate hold",
113: "privilege withdrawn", "AA compromise", };
114:
115: // context specific tag values
116: private static final byte TAG_DIST_PT = 0;
117: private static final byte TAG_REASONS = 1;
118: private static final byte TAG_ISSUER = 2;
119:
120: private static final byte TAG_FULL_NAME = 0;
121: private static final byte TAG_REL_NAME = 1;
122:
123: // only one of fullName and relativeName can be set
124: private GeneralNames fullName;
125: private RDN relativeName;
126:
127: // reasonFlags or null
128: private boolean[] reasonFlags;
129:
130: // crlIssuer or null
131: private GeneralNames crlIssuer;
132:
133: // cached hashCode value
134: private volatile int hashCode;
135:
136: /**
137: * Constructor for the class using GeneralNames for DistributionPointName
138: *
139: * @param fullName the GeneralNames of the distribution point; may be null
140: * @param reasons the CRL reasons included in the CRL at this distribution
141: * point; may be null
142: * @param issuer the name(s) of the CRL issuer for the CRL at this
143: * distribution point; may be null
144: */
145: public DistributionPoint(GeneralNames fullName,
146: boolean[] reasonFlags, GeneralNames crlIssuer) {
147: if ((fullName == null) && (crlIssuer == null)) {
148: throw new IllegalArgumentException(
149: "fullName and crlIssuer may not both be null");
150: }
151: this .fullName = fullName;
152: this .reasonFlags = reasonFlags;
153: this .crlIssuer = crlIssuer;
154: }
155:
156: /**
157: * Constructor for the class using RelativeDistinguishedName for
158: * DistributionPointName
159: *
160: * @param relativeName the RelativeDistinguishedName of the distribution
161: * point; may not be null
162: * @param reasons the CRL reasons included in the CRL at this distribution
163: * point; may be null
164: * @param issuer the name(s) of the CRL issuer for the CRL at this
165: * distribution point; may not be null or empty.
166: */
167: public DistributionPoint(RDN relativeName, boolean[] reasonFlags,
168: GeneralNames crlIssuer) {
169: if ((relativeName == null) && (crlIssuer == null)) {
170: throw new IllegalArgumentException(
171: "relativeName and crlIssuer may not both be null");
172: }
173: this .relativeName = relativeName;
174: this .reasonFlags = reasonFlags;
175: this .crlIssuer = crlIssuer;
176: }
177:
178: /**
179: * Create the object from the passed DER encoded form.
180: *
181: * @param val the DER encoded form of the DistributionPoint
182: * @throws IOException on error
183: */
184: public DistributionPoint(DerValue val) throws IOException {
185: if (val.tag != DerValue.tag_Sequence) {
186: throw new IOException(
187: "Invalid encoding of DistributionPoint.");
188: }
189:
190: // Note that all the fields in DistributionPoint are defined as
191: // being OPTIONAL, i.e., there could be an empty SEQUENCE, resulting
192: // in val.data being null.
193: while ((val.data != null) && (val.data.available() != 0)) {
194: DerValue opt = val.data.getDerValue();
195:
196: if (opt.isContextSpecific(TAG_DIST_PT)
197: && opt.isConstructed()) {
198: if ((fullName != null) || (relativeName != null)) {
199: throw new IOException(
200: "Duplicate DistributionPointName in "
201: + "DistributionPoint.");
202: }
203: DerValue distPnt = opt.data.getDerValue();
204: if (distPnt.isContextSpecific(TAG_FULL_NAME)
205: && distPnt.isConstructed()) {
206: distPnt.resetTag(DerValue.tag_Sequence);
207: fullName = new GeneralNames(distPnt);
208: } else if (distPnt.isContextSpecific(TAG_REL_NAME)
209: && distPnt.isConstructed()) {
210: distPnt.resetTag(DerValue.tag_Set);
211: relativeName = new RDN(distPnt);
212: } else {
213: throw new IOException(
214: "Invalid DistributionPointName in "
215: + "DistributionPoint");
216: }
217: } else if (opt.isContextSpecific(TAG_REASONS)
218: && !opt.isConstructed()) {
219: if (reasonFlags != null) {
220: throw new IOException("Duplicate Reasons in "
221: + "DistributionPoint.");
222: }
223: opt.resetTag(DerValue.tag_BitString);
224: reasonFlags = (opt.getUnalignedBitString())
225: .toBooleanArray();
226: } else if (opt.isContextSpecific(TAG_ISSUER)
227: && opt.isConstructed()) {
228: if (crlIssuer != null) {
229: throw new IOException("Duplicate CRLIssuer in "
230: + "DistributionPoint.");
231: }
232: opt.resetTag(DerValue.tag_Sequence);
233: crlIssuer = new GeneralNames(opt);
234: } else {
235: throw new IOException("Invalid encoding of "
236: + "DistributionPoint.");
237: }
238: }
239: if ((crlIssuer == null) && (fullName == null)
240: && (relativeName == null)) {
241: throw new IOException("One of fullName, relativeName, "
242: + " and crlIssuer has to be set");
243: }
244: }
245:
246: /**
247: * Return the full distribution point name or null if not set.
248: */
249: public GeneralNames getFullName() {
250: return fullName;
251: }
252:
253: /**
254: * Return the relative distribution point name or null if not set.
255: */
256: public RDN getRelativeName() {
257: return relativeName;
258: }
259:
260: /**
261: * Return the reason flags or null if not set.
262: */
263: public boolean[] getReasonFlags() {
264: return reasonFlags;
265: }
266:
267: /**
268: * Return the CRL issuer name or null if not set.
269: */
270: public GeneralNames getCRLIssuer() {
271: return crlIssuer;
272: }
273:
274: /**
275: * Write the DistributionPoint value to the DerOutputStream.
276: *
277: * @param out the DerOutputStream to write the extension to.
278: * @exception IOException on error.
279: */
280: public void encode(DerOutputStream out) throws IOException {
281: DerOutputStream tagged = new DerOutputStream();
282:
283: // NOTE: only one of pointNames and pointRDN can be set
284: if ((fullName != null) || (relativeName != null)) {
285: DerOutputStream distributionPoint = new DerOutputStream();
286: if (fullName != null) {
287: DerOutputStream derOut = new DerOutputStream();
288: fullName.encode(derOut);
289: distributionPoint.writeImplicit(DerValue.createTag(
290: DerValue.TAG_CONTEXT, true, TAG_FULL_NAME),
291: derOut);
292: } else if (relativeName != null) {
293: DerOutputStream derOut = new DerOutputStream();
294: relativeName.encode(derOut);
295: distributionPoint.writeImplicit(DerValue.createTag(
296: DerValue.TAG_CONTEXT, true, TAG_REL_NAME),
297: derOut);
298: }
299: tagged.write(DerValue.createTag(DerValue.TAG_CONTEXT, true,
300: TAG_DIST_PT), distributionPoint);
301: }
302: if (reasonFlags != null) {
303: DerOutputStream reasons = new DerOutputStream();
304: BitArray rf = new BitArray(reasonFlags);
305: reasons.putTruncatedUnalignedBitString(rf);
306: tagged.writeImplicit(DerValue.createTag(
307: DerValue.TAG_CONTEXT, false, TAG_REASONS), reasons);
308: }
309: if (crlIssuer != null) {
310: DerOutputStream issuer = new DerOutputStream();
311: crlIssuer.encode(issuer);
312: tagged.writeImplicit(DerValue.createTag(
313: DerValue.TAG_CONTEXT, true, TAG_ISSUER), issuer);
314: }
315: out.write(DerValue.tag_Sequence, tagged);
316: }
317:
318: /**
319: * Utility function for a.equals(b) where both a and b may be null.
320: */
321: private static boolean equals(Object a, Object b) {
322: return (a == null) ? (b == null) : a.equals(b);
323: }
324:
325: /**
326: * Compare an object to this DistributionPoint for equality.
327: *
328: * @param obj Object to be compared to this
329: * @return true if objects match; false otherwise
330: */
331: public boolean equals(Object obj) {
332: if (this == obj) {
333: return true;
334: }
335: if (obj instanceof DistributionPoint == false) {
336: return false;
337: }
338: DistributionPoint other = (DistributionPoint) obj;
339:
340: boolean equal = equals(this .fullName, other.fullName)
341: && equals(this .relativeName, other.relativeName)
342: && equals(this .crlIssuer, other.crlIssuer)
343: && Arrays.equals(this .reasonFlags, other.reasonFlags);
344: return equal;
345: }
346:
347: public int hashCode() {
348: int hash = hashCode;
349: if (hash == 0) {
350: hash = 1;
351: if (fullName != null) {
352: hash += fullName.hashCode();
353: }
354: if (relativeName != null) {
355: hash += relativeName.hashCode();
356: }
357: if (crlIssuer != null) {
358: hash += crlIssuer.hashCode();
359: }
360: if (reasonFlags != null) {
361: for (int i = 0; i < reasonFlags.length; i++) {
362: if (reasonFlags[i]) {
363: hash += i;
364: }
365: }
366: }
367: hashCode = hash;
368: }
369: return hash;
370: }
371:
372: /**
373: * Return a string representation for reasonFlag bit 'reason'.
374: */
375: private static String reasonToString(int reason) {
376: if ((reason > 0) && (reason < REASON_STRINGS.length)) {
377: return REASON_STRINGS[reason];
378: }
379: return "Unknown reason " + reason;
380: }
381:
382: /**
383: * Return a printable string of the Distribution Point.
384: */
385: public String toString() {
386: StringBuilder sb = new StringBuilder();
387: if (fullName != null) {
388: sb.append("DistributionPoint:\n " + fullName + "\n");
389: }
390: if (relativeName != null) {
391: sb
392: .append("DistributionPoint:\n " + relativeName
393: + "\n");
394: }
395:
396: if (reasonFlags != null) {
397: sb.append(" ReasonFlags:\n");
398: for (int i = 0; i < reasonFlags.length; i++) {
399: if (reasonFlags[i]) {
400: sb.append(" " + reasonToString(i) + "\n");
401: }
402: }
403: }
404: if (crlIssuer != null) {
405: sb.append(" CRLIssuer:" + crlIssuer + "\n");
406: }
407: return sb.toString();
408: }
409:
410: }
|