Use Tree Navigation
public class

DistributionPoint

extends Object
/*
 * Copyright (c) 2002, 2006, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */


package sun.security.x509;

import java.io.IOException;
import java.util.*;

import sun.security.util.BitArray;
import sun.security.util.DerOutputStream;
import sun.security.util.DerValue;

/**
 * Represent the DistributionPoint sequence used in the CRL
 * Distribution Points Extension (OID = 2.5.29.31).
 * <p>
 * The ASN.1 definition for this is:
 * <pre>
 * DistributionPoint ::= SEQUENCE {
 *      distributionPoint       [0]     DistributionPointName OPTIONAL,
 *      reasons                 [1]     ReasonFlags OPTIONAL,
 *      cRLIssuer               [2]     GeneralNames OPTIONAL }
 *
 * DistributionPointName ::= CHOICE {
 *      fullName                [0]     GeneralNames,
 *      nameRelativeToCRLIssuer [1]     RelativeDistinguishedName }
 *
 * ReasonFlags ::= BIT STRING {
 *      unused                  (0),
 *      keyCompromise           (1),
 *      cACompromise            (2),
 *      affiliationChanged      (3),
 *      superseded              (4),
 *      cessationOfOperation    (5),
 *      certificateHold         (6),
 *      privilegeWithdrawn      (7),
 *      aACompromise            (8) }
 *
 * GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName
 *
 * GeneralName ::= CHOICE {
 *         otherName                   [0] INSTANCE OF OTHER-NAME,
 *         rfc822Name                  [1] IA5String,
 *         dNSName                     [2] IA5String,
 *         x400Address                 [3] ORAddress,
 *         directoryName               [4] Name,
 *         ediPartyName                [5] EDIPartyName,
 *         uniformResourceIdentifier   [6] IA5String,
 *         iPAddress                   [7] OCTET STRING,
 *         registeredID                [8] OBJECT IDENTIFIER }
 *
 * RelativeDistinguishedName ::=
 *   SET OF AttributeTypeAndValue
 *
 * AttributeTypeAndValue ::= SEQUENCE {
 *   type     AttributeType,
 *   value    AttributeValue }
 *
 * AttributeType ::= OBJECT IDENTIFIER
 *
 * AttributeValue ::= ANY DEFINED BY AttributeType
 * </pre>
 * <p>
 * Instances of this class are designed to be immutable. However, since this
 * is an internal API we do not use defensive cloning for values for
 * performance reasons. It is the responsibility of the consumer to ensure
 * that no mutable elements are modified.
 *
 * @author Anne Anderson
 * @author Andreas Sterbenz
 * @since 1.4.2
 * @see CRLDistributionPointsExtension
 */

public class DistributionPoint {

   
// reason flag bits
   
// NOTE that these are NOT quite the same as the CRL reason code extension
   
public final static int KEY_COMPROMISE         = 1;
   
public final static int CA_COMPROMISE          = 2;
   
public final static int AFFILIATION_CHANGED    = 3;
   
public final static int SUPERSEDED             = 4;
   
public final static int CESSATION_OF_OPERATION = 5;
   
public final static int CERTIFICATE_HOLD       = 6;
   
public final static int PRIVILEGE_WITHDRAWN    = 7;
   
public final static int AA_COMPROMISE          = 8;

   
private static final String[] REASON_STRINGS = {
       
null,
       
"key compromise",
       
"CA compromise",
       
"affiliation changed",
       
"superseded",
       
"cessation of operation",
       
"certificate hold",
       
"privilege withdrawn",
       
"AA compromise",
   
};

   
// context specific tag values
   
private static final byte TAG_DIST_PT = 0;
   
private static final byte TAG_REASONS = 1;
   
private static final byte TAG_ISSUER = 2;

   
private static final byte TAG_FULL_NAME = 0;
   
private static final byte TAG_REL_NAME = 1;

   
// only one of fullName and relativeName can be set
   
private GeneralNames fullName;
   
private RDN relativeName;

   
// reasonFlags or null
   
private boolean[] reasonFlags;

   
// crlIssuer or null
   
private GeneralNames crlIssuer;

   
// cached hashCode value
   
private volatile int hashCode;

   
/**
     * Constructor for the class using GeneralNames for DistributionPointName
     *
     * @param fullName the GeneralNames of the distribution point; may be null
     * @param reasons the CRL reasons included in the CRL at this distribution
     *        point; may be null
     * @param issuer the name(s) of the CRL issuer for the CRL at this
     *        distribution point; may be null
     */

   
public DistributionPoint(GeneralNames fullName, boolean[] reasonFlags,
           
GeneralNames crlIssuer) {
       
if ((fullName == null) && (crlIssuer == null)) {
           
throw new IllegalArgumentException
                       
("fullName and crlIssuer may not both be null");
       
}
       
this.fullName = fullName;
       
this.reasonFlags = reasonFlags;
       
this.crlIssuer = crlIssuer;
   
}

   
/**
     * Constructor for the class using RelativeDistinguishedName for
     * DistributionPointName
     *
     * @param relativeName the RelativeDistinguishedName of the distribution
     *        point; may not be null
     * @param reasons the CRL reasons included in the CRL at this distribution
     *        point; may be null
     * @param issuer the name(s) of the CRL issuer for the CRL at this
     *        distribution point; may not be null or empty.
     */

   
public DistributionPoint(RDN relativeName, boolean[] reasonFlags,
           
GeneralNames crlIssuer) {
       
if ((relativeName == null) && (crlIssuer == null)) {
           
throw new IllegalArgumentException
                       
("relativeName and crlIssuer may not both be null");
       
}
       
this.relativeName = relativeName;
       
this.reasonFlags = reasonFlags;
       
this.crlIssuer = crlIssuer;
   
}

   
/**
     * Create the object from the passed DER encoded form.
     *
     * @param val the DER encoded form of the DistributionPoint
     * @throws IOException on error
     */

   
public DistributionPoint(DerValue val) throws IOException {
       
if (val.tag != DerValue.tag_Sequence) {
           
throw new IOException("Invalid encoding of DistributionPoint.");
       
}

       
// Note that all the fields in DistributionPoint are defined as
       
// being OPTIONAL, i.e., there could be an empty SEQUENCE, resulting
       
// in val.data being null.
       
while ((val.data != null) && (val.data.available() != 0)) {
           
DerValue opt = val.data.getDerValue();

           
if (opt.isContextSpecific(TAG_DIST_PT) && opt.isConstructed()) {
               
if ((fullName != null) || (relativeName != null)) {
                   
throw new IOException("Duplicate DistributionPointName in "
                                         
+ "DistributionPoint.");
               
}
               
DerValue distPnt = opt.data.getDerValue();
               
if (distPnt.isContextSpecific(TAG_FULL_NAME)
                       
&& distPnt.isConstructed()) {
                    distPnt
.resetTag(DerValue.tag_Sequence);
                    fullName
= new GeneralNames(distPnt);
               
} else if (distPnt.isContextSpecific(TAG_REL_NAME)
                       
&& distPnt.isConstructed()) {
                    distPnt
.resetTag(DerValue.tag_Set);
                    relativeName
= new RDN(distPnt);
               
} else {
                   
throw new IOException("Invalid DistributionPointName in "
                                         
+ "DistributionPoint");
               
}
           
} else if (opt.isContextSpecific(TAG_REASONS)
                                               
&& !opt.isConstructed()) {
               
if (reasonFlags != null) {
                   
throw new IOException("Duplicate Reasons in " +
                                         
"DistributionPoint.");
               
}
                opt
.resetTag(DerValue.tag_BitString);
                reasonFlags
= (opt.getUnalignedBitString()).toBooleanArray();
           
} else if (opt.isContextSpecific(TAG_ISSUER)
                                               
&& opt.isConstructed()) {
               
if (crlIssuer != null) {
                   
throw new IOException("Duplicate CRLIssuer in " +
                                         
"DistributionPoint.");
               
}
                opt
.resetTag(DerValue.tag_Sequence);
                crlIssuer
= new GeneralNames(opt);
           
} else {
               
throw new IOException("Invalid encoding of " +
                                     
"DistributionPoint.");
           
}
       
}
       
if ((crlIssuer == null) && (fullName == null) && (relativeName == null)) {
           
throw new IOException("One of fullName, relativeName, "
               
+ " and crlIssuer has to be set");
       
}
   
}

   
/**
     * Return the full distribution point name or null if not set.
     */

   
public GeneralNames getFullName() {
       
return fullName;
   
}

   
/**
     * Return the relative distribution point name or null if not set.
     */

   
public RDN getRelativeName() {
       
return relativeName;
   
}

   
/**
     * Return the reason flags or null if not set.
     */

   
public boolean[] getReasonFlags() {
       
return reasonFlags;
   
}

   
/**
     * Return the CRL issuer name or null if not set.
     */

   
public GeneralNames getCRLIssuer() {
       
return crlIssuer;
   
}

   
/**
     * Write the DistributionPoint value to the DerOutputStream.
     *
     * @param out the DerOutputStream to write the extension to.
     * @exception IOException on error.
     */

   
public void encode(DerOutputStream out) throws IOException {
       
DerOutputStream tagged = new DerOutputStream();

       
// NOTE: only one of pointNames and pointRDN can be set
       
if ((fullName != null) || (relativeName != null)) {
           
DerOutputStream distributionPoint = new DerOutputStream();
           
if (fullName != null) {
               
DerOutputStream derOut = new DerOutputStream();
                fullName
.encode(derOut);
                distributionPoint
.writeImplicit(
                   
DerValue.createTag(DerValue.TAG_CONTEXT, true, TAG_FULL_NAME),
                    derOut
);
           
} else if (relativeName != null) {
               
DerOutputStream derOut = new DerOutputStream();
                relativeName
.encode(derOut);
                distributionPoint
.writeImplicit(
                   
DerValue.createTag(DerValue.TAG_CONTEXT, true, TAG_REL_NAME),
                    derOut
);
           
}
            tagged
.write(
               
DerValue.createTag(DerValue.TAG_CONTEXT, true, TAG_DIST_PT),
                distributionPoint
);
       
}
       
if (reasonFlags != null) {
           
DerOutputStream reasons = new DerOutputStream();
           
BitArray rf = new BitArray(reasonFlags);
            reasons
.putTruncatedUnalignedBitString(rf);
            tagged
.writeImplicit(
               
DerValue.createTag(DerValue.TAG_CONTEXT, false, TAG_REASONS),
                reasons
);
       
}
       
if (crlIssuer != null) {
           
DerOutputStream issuer = new DerOutputStream();
            crlIssuer
.encode(issuer);
            tagged
.writeImplicit(
               
DerValue.createTag(DerValue.TAG_CONTEXT, true, TAG_ISSUER),
                issuer
);
       
}
       
out.write(DerValue.tag_Sequence, tagged);
   
}

   
/**
     * Utility function for a.equals(b) where both a and b may be null.
     */

   
private static boolean equals(Object a, Object b) {
       
return (a == null) ? (b == null) : a.equals(b);
   
}

   
/**
     * Compare an object to this DistributionPoint for equality.
     *
     * @param obj Object to be compared to this
     * @return true if objects match; false otherwise
     */

   
public boolean equals(Object obj) {
       
if (this == obj) {
           
return true;
       
}
       
if (obj instanceof DistributionPoint == false) {
           
return false;
       
}
       
DistributionPoint other = (DistributionPoint)obj;

       
boolean equal = equals(this.fullName, other.fullName)
                     
&& equals(this.relativeName, other.relativeName)
                     
&& equals(this.crlIssuer, other.crlIssuer)
                     
&& Arrays.equals(this.reasonFlags, other.reasonFlags);
       
return equal;
   
}

   
public int hashCode() {
       
int hash = hashCode;
       
if (hash == 0) {
            hash
= 1;
           
if (fullName != null) {
                hash
+= fullName.hashCode();
           
}
           
if (relativeName != null) {
                hash
+= relativeName.hashCode();
           
}
           
if (crlIssuer != null) {
                hash
+= crlIssuer.hashCode();
           
}
           
if (reasonFlags != null) {
               
for (int i = 0; i < reasonFlags.length; i++) {
                   
if (reasonFlags[i]) {
                        hash
+= i;
                   
}
               
}
           
}
            hashCode
= hash;
       
}
       
return hash;
   
}

   
/**
     * Return a string representation for reasonFlag bit 'reason'.
     */

   
private static String reasonToString(int reason) {
       
if ((reason > 0) && (reason < REASON_STRINGS.length)) {
           
return REASON_STRINGS[reason];
       
}
       
return "Unknown reason " + reason;
   
}

   
/**
     * Return a printable string of the Distribution Point.
     */

   
public String toString() {
       
StringBuilder sb = new StringBuilder();
       
if (fullName != null) {
            sb
.append("DistributionPoint:\n     " + fullName + "\n");
       
}
       
if (relativeName != null) {
            sb
.append("DistributionPoint:\n     " + relativeName + "\n");
       
}

       
if (reasonFlags != null) {
            sb
.append("   ReasonFlags:\n");
           
for (int i = 0; i < reasonFlags.length; i++) {
               
if (reasonFlags[i]) {
                    sb
.append("    " + reasonToString(i) + "\n");
               
}
           
}
       
}
       
if (crlIssuer != null) {
            sb
.append("   CRLIssuer:" + crlIssuer + "\n");
       
}
       
return sb.toString();
   
}

}