Home | History | Annotate | Download | only in x509
      1 /*
      2  * Copyright (c) 2002, 2011, Oracle and/or its affiliates. All rights reserved.
      3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
      4  *
      5  * This code is free software; you can redistribute it and/or modify it
      6  * under the terms of the GNU General Public License version 2 only, as
      7  * published by the Free Software Foundation.  Oracle designates this
      8  * particular file as subject to the "Classpath" exception as provided
      9  * by Oracle in the LICENSE file that accompanied this code.
     10  *
     11  * This code is distributed in the hope that it will be useful, but WITHOUT
     12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
     13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
     14  * version 2 for more details (a copy is included in the LICENSE file that
     15  * accompanied this code).
     16  *
     17  * You should have received a copy of the GNU General Public License version
     18  * 2 along with this work; if not, write to the Free Software Foundation,
     19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
     20  *
     21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
     22  * or visit www.oracle.com if you need additional information or have any
     23  * questions.
     24  */
     25 
     26 package sun.security.x509;
     27 
     28 import java.io.IOException;
     29 import java.util.*;
     30 
     31 import sun.security.util.BitArray;
     32 import sun.security.util.DerOutputStream;
     33 import sun.security.util.DerValue;
     34 
     35 /**
     36  * Represent the DistributionPoint sequence used in the CRL
     37  * Distribution Points Extension (OID = 2.5.29.31).
     38  * <p>
     39  * The ASN.1 definition for this is:
     40  * <pre>
     41  * DistributionPoint ::= SEQUENCE {
     42  *      distributionPoint       [0]     DistributionPointName OPTIONAL,
     43  *      reasons                 [1]     ReasonFlags OPTIONAL,
     44  *      cRLIssuer               [2]     GeneralNames OPTIONAL }
     45  *
     46  * DistributionPointName ::= CHOICE {
     47  *      fullName                [0]     GeneralNames,
     48  *      nameRelativeToCRLIssuer [1]     RelativeDistinguishedName }
     49  *
     50  * ReasonFlags ::= BIT STRING {
     51  *      unused                  (0),
     52  *      keyCompromise           (1),
     53  *      cACompromise            (2),
     54  *      affiliationChanged      (3),
     55  *      superseded              (4),
     56  *      cessationOfOperation    (5),
     57  *      certificateHold         (6),
     58  *      privilegeWithdrawn      (7),
     59  *      aACompromise            (8) }
     60  *
     61  * GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName
     62  *
     63  * GeneralName ::= CHOICE {
     64  *         otherName                   [0] INSTANCE OF OTHER-NAME,
     65  *         rfc822Name                  [1] IA5String,
     66  *         dNSName                     [2] IA5String,
     67  *         x400Address                 [3] ORAddress,
     68  *         directoryName               [4] Name,
     69  *         ediPartyName                [5] EDIPartyName,
     70  *         uniformResourceIdentifier   [6] IA5String,
     71  *         iPAddress                   [7] OCTET STRING,
     72  *         registeredID                [8] OBJECT IDENTIFIER }
     73  *
     74  * RelativeDistinguishedName ::=
     75  *   SET OF AttributeTypeAndValue
     76  *
     77  * AttributeTypeAndValue ::= SEQUENCE {
     78  *   type     AttributeType,
     79  *   value    AttributeValue }
     80  *
     81  * AttributeType ::= OBJECT IDENTIFIER
     82  *
     83  * AttributeValue ::= ANY DEFINED BY AttributeType
     84  * </pre>
     85  * <p>
     86  * Instances of this class are designed to be immutable. However, since this
     87  * is an internal API we do not use defensive cloning for values for
     88  * performance reasons. It is the responsibility of the consumer to ensure
     89  * that no mutable elements are modified.
     90  *
     91  * @author Anne Anderson
     92  * @author Andreas Sterbenz
     93  * @since 1.4.2
     94  * @see CRLDistributionPointsExtension
     95  */
     96 public class DistributionPoint {
     97 
     98     // reason flag bits
     99     // NOTE that these are NOT quite the same as the CRL reason code extension
    100     public final static int KEY_COMPROMISE         = 1;
    101     public final static int CA_COMPROMISE          = 2;
    102     public final static int AFFILIATION_CHANGED    = 3;
    103     public final static int SUPERSEDED             = 4;
    104     public final static int CESSATION_OF_OPERATION = 5;
    105     public final static int CERTIFICATE_HOLD       = 6;
    106     public final static int PRIVILEGE_WITHDRAWN    = 7;
    107     public final static int AA_COMPROMISE          = 8;
    108 
    109     private static final String[] REASON_STRINGS = {
    110         null,
    111         "key compromise",
    112         "CA compromise",
    113         "affiliation changed",
    114         "superseded",
    115         "cessation of operation",
    116         "certificate hold",
    117         "privilege withdrawn",
    118         "AA compromise",
    119     };
    120 
    121     // context specific tag values
    122     private static final byte TAG_DIST_PT = 0;
    123     private static final byte TAG_REASONS = 1;
    124     private static final byte TAG_ISSUER = 2;
    125 
    126     private static final byte TAG_FULL_NAME = 0;
    127     private static final byte TAG_REL_NAME = 1;
    128 
    129     // only one of fullName and relativeName can be set
    130     private GeneralNames fullName;
    131     private RDN relativeName;
    132 
    133     // reasonFlags or null
    134     private boolean[] reasonFlags;
    135 
    136     // crlIssuer or null
    137     private GeneralNames crlIssuer;
    138 
    139     // cached hashCode value
    140     private volatile int hashCode;
    141 
    142     /**
    143      * Constructor for the class using GeneralNames for DistributionPointName
    144      *
    145      * @param fullName the GeneralNames of the distribution point; may be null
    146      * @param reasons the CRL reasons included in the CRL at this distribution
    147      *        point; may be null
    148      * @param issuer the name(s) of the CRL issuer for the CRL at this
    149      *        distribution point; may be null
    150      */
    151     public DistributionPoint(GeneralNames fullName, boolean[] reasonFlags,
    152             GeneralNames crlIssuer) {
    153         if ((fullName == null) && (crlIssuer == null)) {
    154             throw new IllegalArgumentException
    155                         ("fullName and crlIssuer may not both be null");
    156         }
    157         this.fullName = fullName;
    158         this.reasonFlags = reasonFlags;
    159         this.crlIssuer = crlIssuer;
    160     }
    161 
    162     /**
    163      * Constructor for the class using RelativeDistinguishedName for
    164      * DistributionPointName
    165      *
    166      * @param relativeName the RelativeDistinguishedName of the distribution
    167      *        point; may not be null
    168      * @param reasons the CRL reasons included in the CRL at this distribution
    169      *        point; may be null
    170      * @param issuer the name(s) of the CRL issuer for the CRL at this
    171      *        distribution point; may not be null or empty.
    172      */
    173     public DistributionPoint(RDN relativeName, boolean[] reasonFlags,
    174             GeneralNames crlIssuer) {
    175         if ((relativeName == null) && (crlIssuer == null)) {
    176             throw new IllegalArgumentException
    177                         ("relativeName and crlIssuer may not both be null");
    178         }
    179         this.relativeName = relativeName;
    180         this.reasonFlags = reasonFlags;
    181         this.crlIssuer = crlIssuer;
    182     }
    183 
    184     /**
    185      * Create the object from the passed DER encoded form.
    186      *
    187      * @param val the DER encoded form of the DistributionPoint
    188      * @throws IOException on error
    189      */
    190     public DistributionPoint(DerValue val) throws IOException {
    191         if (val.tag != DerValue.tag_Sequence) {
    192             throw new IOException("Invalid encoding of DistributionPoint.");
    193         }
    194 
    195         // Note that all the fields in DistributionPoint are defined as
    196         // being OPTIONAL, i.e., there could be an empty SEQUENCE, resulting
    197         // in val.data being null.
    198         while ((val.data != null) && (val.data.available() != 0)) {
    199             DerValue opt = val.data.getDerValue();
    200 
    201             if (opt.isContextSpecific(TAG_DIST_PT) && opt.isConstructed()) {
    202                 if ((fullName != null) || (relativeName != null)) {
    203                     throw new IOException("Duplicate DistributionPointName in "
    204                                           + "DistributionPoint.");
    205                 }
    206                 DerValue distPnt = opt.data.getDerValue();
    207                 if (distPnt.isContextSpecific(TAG_FULL_NAME)
    208                         && distPnt.isConstructed()) {
    209                     distPnt.resetTag(DerValue.tag_Sequence);
    210                     fullName = new GeneralNames(distPnt);
    211                 } else if (distPnt.isContextSpecific(TAG_REL_NAME)
    212                         && distPnt.isConstructed()) {
    213                     distPnt.resetTag(DerValue.tag_Set);
    214                     relativeName = new RDN(distPnt);
    215                 } else {
    216                     throw new IOException("Invalid DistributionPointName in "
    217                                           + "DistributionPoint");
    218                 }
    219             } else if (opt.isContextSpecific(TAG_REASONS)
    220                                                 && !opt.isConstructed()) {
    221                 if (reasonFlags != null) {
    222                     throw new IOException("Duplicate Reasons in " +
    223                                           "DistributionPoint.");
    224                 }
    225                 opt.resetTag(DerValue.tag_BitString);
    226                 reasonFlags = (opt.getUnalignedBitString()).toBooleanArray();
    227             } else if (opt.isContextSpecific(TAG_ISSUER)
    228                                                 && opt.isConstructed()) {
    229                 if (crlIssuer != null) {
    230                     throw new IOException("Duplicate CRLIssuer in " +
    231                                           "DistributionPoint.");
    232                 }
    233                 opt.resetTag(DerValue.tag_Sequence);
    234                 crlIssuer = new GeneralNames(opt);
    235             } else {
    236                 throw new IOException("Invalid encoding of " +
    237                                       "DistributionPoint.");
    238             }
    239         }
    240         if ((crlIssuer == null) && (fullName == null) && (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(
    290                     DerValue.createTag(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(
    296                     DerValue.createTag(DerValue.TAG_CONTEXT, true, TAG_REL_NAME),
    297                     derOut);
    298             }
    299             tagged.write(
    300                 DerValue.createTag(DerValue.TAG_CONTEXT, true, TAG_DIST_PT),
    301                 distributionPoint);
    302         }
    303         if (reasonFlags != null) {
    304             DerOutputStream reasons = new DerOutputStream();
    305             BitArray rf = new BitArray(reasonFlags);
    306             reasons.putTruncatedUnalignedBitString(rf);
    307             tagged.writeImplicit(
    308                 DerValue.createTag(DerValue.TAG_CONTEXT, false, TAG_REASONS),
    309                 reasons);
    310         }
    311         if (crlIssuer != null) {
    312             DerOutputStream issuer = new DerOutputStream();
    313             crlIssuer.encode(issuer);
    314             tagged.writeImplicit(
    315                 DerValue.createTag(DerValue.TAG_CONTEXT, true, TAG_ISSUER),
    316                 issuer);
    317         }
    318         out.write(DerValue.tag_Sequence, tagged);
    319     }
    320 
    321     /**
    322      * Compare an object to this DistributionPoint for equality.
    323      *
    324      * @param obj Object to be compared to this
    325      * @return true if objects match; false otherwise
    326      */
    327     public boolean equals(Object obj) {
    328         if (this == obj) {
    329             return true;
    330         }
    331         if (obj instanceof DistributionPoint == false) {
    332             return false;
    333         }
    334         DistributionPoint other = (DistributionPoint)obj;
    335 
    336         boolean equal = Objects.equals(this.fullName, other.fullName)
    337                      && Objects.equals(this.relativeName, other.relativeName)
    338                      && Objects.equals(this.crlIssuer, other.crlIssuer)
    339                      && Arrays.equals(this.reasonFlags, other.reasonFlags);
    340         return equal;
    341     }
    342 
    343     public int hashCode() {
    344         int hash = hashCode;
    345         if (hash == 0) {
    346             hash = 1;
    347             if (fullName != null) {
    348                 hash += fullName.hashCode();
    349             }
    350             if (relativeName != null) {
    351                 hash += relativeName.hashCode();
    352             }
    353             if (crlIssuer != null) {
    354                 hash += crlIssuer.hashCode();
    355             }
    356             if (reasonFlags != null) {
    357                 for (int i = 0; i < reasonFlags.length; i++) {
    358                     if (reasonFlags[i]) {
    359                         hash += i;
    360                     }
    361                 }
    362             }
    363             hashCode = hash;
    364         }
    365         return hash;
    366     }
    367 
    368     /**
    369      * Return a string representation for reasonFlag bit 'reason'.
    370      */
    371     private static String reasonToString(int reason) {
    372         if ((reason > 0) && (reason < REASON_STRINGS.length)) {
    373             return REASON_STRINGS[reason];
    374         }
    375         return "Unknown reason " + reason;
    376     }
    377 
    378     /**
    379      * Return a printable string of the Distribution Point.
    380      */
    381     public String toString() {
    382         StringBuilder sb = new StringBuilder();
    383         if (fullName != null) {
    384             sb.append("DistributionPoint:\n     " + fullName + "\n");
    385         }
    386         if (relativeName != null) {
    387             sb.append("DistributionPoint:\n     " + relativeName + "\n");
    388         }
    389 
    390         if (reasonFlags != null) {
    391             sb.append("   ReasonFlags:\n");
    392             for (int i = 0; i < reasonFlags.length; i++) {
    393                 if (reasonFlags[i]) {
    394                     sb.append("    " + reasonToString(i) + "\n");
    395                 }
    396             }
    397         }
    398         if (crlIssuer != null) {
    399             sb.append("   CRLIssuer:" + crlIssuer + "\n");
    400         }
    401         return sb.toString();
    402     }
    403 
    404 }
    405