/** * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Copyright (c) 2005 Sun Microsystems Inc. All Rights Reserved * * The contents of this file are subject to the terms * of the Common Development and Distribution License * (the License). You may not use this file except in * compliance with the License. * * You can obtain a copy of the License at * https://opensso.dev.java.net/public/CDDLv1.0.html or * opensso/legal/CDDLv1.0.txt * See the License for the specific language governing * permission and limitations under the License. * * When distributing Covered Code, include this CDDL * Header Notice in each file and include the License file * at opensso/legal/CDDLv1.0.txt. * If applicable, add the following below the CDDL Header, * with the fields enclosed by brackets [] replaced by * your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * * $Id: IssuingDistributionPointExtension.java,v 1.2 2008/06/25 05:52:46 qcheng Exp $ * */ package com.iplanet.security.x509; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.*; import sun.security.util.BitArray; import sun.security.util.DerInputStream; import sun.security.util.DerOutputStream; import sun.security.util.DerValue; import sun.security.util.ObjectIdentifier; import sun.security.x509.AVA; import sun.security.x509.Extension; import sun.security.x509.GeneralNames; import sun.security.x509.PKIXExtensions; import sun.security.x509.RDN; /** * A critical CRL extension that identifies the CRL distribution point * for a particular CRL * *
 * issuingDistributionPoint ::= SEQUENCE {
 *         distributionPoint       [0] DistributionPointName OPTIONAL,
 *         onlyContainsUserCerts   [1] BOOLEAN DEFAULT FALSE,
 *         onlyContainsCACerts     [2] BOOLEAN DEFAULT FALSE,
 *         onlySomeReasons         [3] ReasonFlags OPTIONAL,
 *         indirectCRL             [4] BOOLEAN DEFAULT FALSE }
 *
 * 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) }
 *
 * GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName
 *
 * GeneralName ::= CHOICE {
 *         otherName                       [0]     OtherName,
 *         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}
 *
 * OtherName ::= SEQUENCE {
 *         type-id    OBJECT IDENTIFIER,
 *         value      [0] EXPLICIT ANY DEFINED BY type-id }
 *
 * EDIPartyName ::= SEQUENCE {
 *         nameAssigner            [0]     DirectoryString OPTIONAL,
 *         partyName               [1]     DirectoryString }
 *
 * RelativeDistinguishedName ::=
 *         SET OF AttributeTypeAndValue
 *
 * AttributeTypeAndValue ::= SEQUENCE {
 *         type     AttributeType,
 *         value    AttributeValue }
 *
 * AttributeType ::= OBJECT IDENTIFIER
 *
 * AttributeValue ::= ANY DEFINED BY AttributeType
 * 
*/ public class IssuingDistributionPointExtension extends Extension { /** * Identifier for this attribute, to be used with the * get, set, delete methods of Certificate, x509 type. */ public static final String IDENT = "x509.info.extensions.IssuingDistributionPoint"; // reason flag bits 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; private static final String[] REASON_STRINGS = { null, "key compromise", "CA compromise", "affiliation changed", "superseded", "cessation of operation", "certificate hold" }; /** * Attribute name. */ public static final String NAME = "IssuingDistributionPoint"; // context specific tag values private static final byte TAG_DIST_PT = 0; private static final byte TAG_ONLY_USER_CERTS = 1; private static final byte TAG_ONLY_CA_CERTS = 2; private static final byte TAG_REASONS = 3; private static final byte TAG_INDIRECT_CRL = 4; 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 = null; private RDN relativeName = null; private boolean onlyContainsUserCerts = false; private boolean onlyContainsCACerts = false; // onlySomeReasons or null private boolean[] reasonFlags = null; private boolean indirectCRL = false; // cached hashCode value private volatile int hashCode; /** * Create a IssuingDistributionPointExtension. * * @param fullName the GeneralNames of the distribution point; may be null * @param onlyContainsUserCerts the 'onlyContainsUserCerts' attribute * @param onlyContainsCACerts the 'onlyContainsCACerts' attribute * @param reasonFlags the 'reasonFlags' attribute * @param indirectCRL the 'indirectCRL' attribute * @param critical true if this is a critical extension * @throws IOException on error */ public IssuingDistributionPointExtension(GeneralNames fullName, boolean onlyContainsUserCerts, boolean onlyContainsCACerts, boolean[] reasonFlags, boolean indirectCRL, boolean critical) throws IOException { this.extensionId = PKIXExtensions.IssuingDistributionPoint_Id; this.critical = critical; this.fullName = fullName; this.onlyContainsUserCerts = onlyContainsUserCerts; this.onlyContainsCACerts = onlyContainsCACerts; this.reasonFlags = reasonFlags; this.indirectCRL = indirectCRL; encodeThis(); } /** * Create a IssuingDistributionPointExtension. * * @param relativeName the RelativeDistinguishedName of the distribution * point; may not be null * @param onlyContainsUserCerts the 'onlyContainsUserCerts' attribute * @param onlyContainsCACerts the 'onlyContainsCACerts' attribute * @param reasonFlags the 'reasonFlags' attribute * @param indirectCRL the 'indirectCRL' attribute * @param critical true if this is a critical extension * @throws IOException on error */ public IssuingDistributionPointExtension(RDN relativeName, boolean onlyContainsUserCerts, boolean onlyContainsCACerts, boolean[] reasonFlags, boolean indirectCRL, boolean critical) throws IOException { this.extensionId = PKIXExtensions.IssuingDistributionPoint_Id; this.critical = critical; this.relativeName = relativeName; this.onlyContainsUserCerts = onlyContainsUserCerts; this.onlyContainsCACerts = onlyContainsCACerts; this.reasonFlags = reasonFlags; this.indirectCRL = indirectCRL; encodeThis(); } /** * Create the extension from the passed DER encoded value of the same. * * @param value Array of DER encoded bytes of the actual value. * @exception IOException on error. */ public IssuingDistributionPointExtension(Object value) throws IOException { this.extensionId = PKIXExtensions.IssuingDistributionPoint_Id; this.critical = true; if (!(value instanceof byte[])) { throw new IOException("Illegal argument type"); } extensionValue = (byte[])value; DerValue val = new DerValue(extensionValue); if (val.tag != DerValue.tag_Sequence) { throw new IOException("Invalid encoding for " + "IssuingDistributionPointExtension."); } if (val.data == null || val.data.available() == 0) { return; } DerValue opt = val.data.getDerValue(); if (opt.isContextSpecific(TAG_DIST_PT) && opt.isConstructed()) { 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(derValueToAVAs(distPnt)); } else { throw new IOException("Invalid encoding for " + "IssuingDistributionPointExtension."); } if (val.data.available() == 0) { return; } opt = val.data.getDerValue(); } if (opt.isContextSpecific(TAG_ONLY_USER_CERTS)) { opt.resetTag(DerValue.tag_Boolean); onlyContainsUserCerts = opt.getBoolean(); if (val.data.available() == 0) { return; } opt = val.data.getDerValue(); } if (opt.isContextSpecific(TAG_ONLY_CA_CERTS)) { opt.resetTag(DerValue.tag_Boolean); onlyContainsCACerts = opt.getBoolean(); if (onlyContainsUserCerts && onlyContainsCACerts) { throw new IOException("onlyContainsUserCerts and " + "onlyContainsCACerts can't both be true"); } if (val.data.available() == 0) { return; } opt = val.data.getDerValue(); } if (opt.isContextSpecific(TAG_REASONS) && !opt.isConstructed()) { opt.resetTag(DerValue.tag_BitString); reasonFlags = (opt.getUnalignedBitString()).toBooleanArray(); if (val.data.available() == 0) { return; } opt = val.data.getDerValue(); } if (opt.isContextSpecific(TAG_INDIRECT_CRL)) { opt.resetTag(DerValue.tag_Boolean); indirectCRL = opt.getBoolean(); if (val.data.available() == 0) { return; } } throw new IOException("Invalid encoding for " + "IssuingDistributionPointExtension."); } /** * Return the name of this attribute. */ public String getName() { return NAME; } /** * 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 onlyContainsUserCerts attribute */ public boolean getOnlyContainsUserCerts() { return onlyContainsUserCerts; } /** * Return the onlyContainsCACerts attribute */ public boolean getOnlyContainsCACerts() { return onlyContainsCACerts; } /** * Return the reason flags or null if not set. */ public boolean[] getOnlySomeReasons() { return reasonFlags; } /** * Return the indirectCRL attribute */ public boolean getIndirectCRL() { return indirectCRL; } /** * Sets the full distribution point name. */ public void setFullName(GeneralNames fullName) { this.fullName = fullName; if( fullName != null ) { this.relativeName = null; } } /** * Sets the relative distribution point name. */ public void setRelativeName(RDN relativeName) { this.relativeName = relativeName; if( relativeName != null ) { this.fullName = null; } } /** * Sets the onlyContainsUserCerts attribute. */ public void setOnlyContainsUserCerts(boolean onlyContainsUserCerts) { this.onlyContainsUserCerts = onlyContainsUserCerts; } /** * Sets the onlyContainsCACerts attribute. */ public void setOnlyContainsCACerts(boolean onlyContainsCACerts) { this.onlyContainsCACerts = onlyContainsCACerts; } /** * Sets the reason flags for this distribution point. */ public void setOnlySomeReasons(boolean[] reasonFlags) { this.reasonFlags = reasonFlags; } /** * Sets the indirectCRL attribute. */ public void setIndirectCRL(boolean indirectCRL) { this.indirectCRL = indirectCRL; } /** * Write the extension to the DerOutputStream. * * @param out the DerOutputStream to write the extension to. * @exception IOException on encoding errors. */ public void encode(OutputStream out) throws IOException { DerOutputStream tmp = new DerOutputStream(); if (this.extensionValue == null) { this.extensionId = PKIXExtensions.IssuingDistributionPoint_Id; this.critical = true; encodeThis(); } super.encode(tmp); out.write(tmp.toByteArray()); } // Encode this extension value private void encodeThis() throws IOException { if (onlyContainsUserCerts && onlyContainsCACerts) { throw new IOException("onlyContainsUserCerts and " + "onlyContainsCACerts can't both be true"); } 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(); encodeRDN(relativeName, 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 (onlyContainsUserCerts) { DerOutputStream doOnlyContainsUserCerts = new DerOutputStream(); doOnlyContainsUserCerts.putBoolean(onlyContainsUserCerts); tagged.writeImplicit(DerValue.createTag(DerValue.TAG_CONTEXT, false, TAG_ONLY_USER_CERTS), doOnlyContainsUserCerts); } if (onlyContainsCACerts) { DerOutputStream doOnlyContainsCACerts = new DerOutputStream(); doOnlyContainsCACerts.putBoolean(onlyContainsCACerts); tagged.writeImplicit(DerValue.createTag(DerValue.TAG_CONTEXT, false, TAG_ONLY_CA_CERTS), doOnlyContainsCACerts); } if (reasonFlags != null) { DerOutputStream reasons = new DerOutputStream(); BitArray rf = new BitArray(reasonFlags); reasons.putUnalignedBitString(rf); tagged.writeImplicit( DerValue.createTag(DerValue.TAG_CONTEXT, false, TAG_REASONS), reasons); } if (indirectCRL) { DerOutputStream doIndirectCRL = new DerOutputStream(); doIndirectCRL.putBoolean(indirectCRL); tagged.writeImplicit(DerValue.createTag(DerValue.TAG_CONTEXT, false, TAG_INDIRECT_CRL), doIndirectCRL); } this.extensionValue = tagged.toByteArray(); } /** * 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 the extension as user readable string. */ public String toString() { StringBuffer sb = new StringBuffer(); sb.append(super.toString() + "IssuingDistributionPoint [\n "); if (fullName != null) { sb.append(" fullName:\n " + fullName + "\n"); } if (relativeName != null) { sb.append(" relativeName:\n " + relativeName + "\n"); } sb.append(" onlyContainsUserCerts:\n " + onlyContainsUserCerts + "\n"); sb.append(" onlyContainsCACerts:\n " + onlyContainsCACerts + "\n"); if (reasonFlags != null) { sb.append(" ReasonFlags:\n"); for (int i = 0; i < reasonFlags.length; i++) { if (reasonFlags[i]) { sb.append(" " + reasonToString(i) + "\n"); } } } sb.append(" indirectCRL:\n " + indirectCRL + "\n"); return sb.toString(); } private static AVA[] derValueToAVAs(DerValue derValue) throws IOException { DerInputStream dis = new DerInputStream(derValue.toByteArray()); DerValue[] avaset = dis.getSet(5); AVA[] avas = new AVA[avaset.length]; for (int i = 0; i < avaset.length; i++) { DerValue derval = avaset[i]; avas[i] = new AVA(derval.data.getOID(), derval.data.getDerValue()); } return avas; } private static void encodeRDN(RDN rdn, DerOutputStream derOut) throws IOException { List avas = rdn.avas(); AVA[] avaArray = (AVA[])avas.toArray(new AVA[avas.size()]); derOut.putOrderedSetOf(DerValue.tag_Set, avaArray); } }