/* |
|
* Copyright (c) 2002, 2011, 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); |
|
} |
|
/** |
|
* 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 = Objects.equals(this.fullName, other.fullName) |
|
&& Objects.equals(this.relativeName, other.relativeName) |
|
&& Objects.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(); |
|
} |
|
} |