411 lines · 14.8 KB · Java
RawDownload
/* * 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();    } }