/** * The class encapsulates the ASN.1 DER encoding/decoding work with the Extension part of X.509 * certificate (as specified in RFC 3280 - Internet X.509 Public Key Infrastructure. Certificate and * Certificate Revocation List (CRL) Profile. http://www.ietf.org/rfc/rfc3280.txt): * * <pre> * Extension ::= SEQUENCE { * extnID OBJECT IDENTIFIER, * critical BOOLEAN DEFAULT FALSE, * extnValue OCTET STRING * } * </pre> */ public final class Extension { // critical constants public static final boolean CRITICAL = true; public static final boolean NON_CRITICAL = false; // constants: the extension OIDs // certificate extensions: static final int[] SUBJ_DIRECTORY_ATTRS = {2, 5, 29, 9}; static final int[] SUBJ_KEY_ID = {2, 5, 29, 14}; static final int[] KEY_USAGE = {2, 5, 29, 15}; static final int[] PRIVATE_KEY_USAGE_PERIOD = {2, 5, 29, 16}; static final int[] SUBJECT_ALT_NAME = {2, 5, 29, 17}; static final int[] ISSUER_ALTERNATIVE_NAME = {2, 5, 29, 18}; static final int[] BASIC_CONSTRAINTS = {2, 5, 29, 19}; static final int[] NAME_CONSTRAINTS = {2, 5, 29, 30}; static final int[] CRL_DISTR_POINTS = {2, 5, 29, 31}; static final int[] CERTIFICATE_POLICIES = {2, 5, 29, 32}; static final int[] POLICY_MAPPINGS = {2, 5, 29, 33}; static final int[] AUTH_KEY_ID = {2, 5, 29, 35}; static final int[] POLICY_CONSTRAINTS = {2, 5, 29, 36}; static final int[] EXTENDED_KEY_USAGE = {2, 5, 29, 37}; static final int[] FRESHEST_CRL = {2, 5, 29, 46}; static final int[] INHIBIT_ANY_POLICY = {2, 5, 29, 54}; static final int[] AUTHORITY_INFO_ACCESS = {1, 3, 6, 1, 5, 5, 7, 1, 1}; static final int[] SUBJECT_INFO_ACCESS = {1, 3, 6, 1, 5, 5, 7, 1, 11}; // crl extensions: static final int[] ISSUING_DISTR_POINT = {2, 5, 29, 28}; // crl entry extensions: static final int[] CRL_NUMBER = {2, 5, 29, 20}; static final int[] CERTIFICATE_ISSUER = {2, 5, 29, 29}; static final int[] INVALIDITY_DATE = {2, 5, 29, 24}; static final int[] REASON_CODE = {2, 5, 29, 21}; static final int[] ISSUING_DISTR_POINTS = {2, 5, 29, 28}; // the value of extnID field of the structure private final int[] extnID; private String extnID_str; // the value of critical field of the structure private final boolean critical; // the value of extnValue field of the structure private final byte[] extnValue; // the ASN.1 encoded form of Extension private byte[] encoding; // the raw (not decoded) value of extnValue field of the structure private byte[] rawExtnValue; // the decoded extension value protected ExtensionValue extnValueObject; // tells whether extension value has been decoded or not private boolean valueDecoded = false; public Extension(String extnID, boolean critical, ExtensionValue extnValueObject) { this.extnID_str = extnID; this.extnID = ObjectIdentifier.toIntArray(extnID); this.critical = critical; this.extnValueObject = extnValueObject; this.valueDecoded = true; this.extnValue = extnValueObject.getEncoded(); } public Extension(String extnID, boolean critical, byte[] extnValue) { this.extnID_str = extnID; this.extnID = ObjectIdentifier.toIntArray(extnID); this.critical = critical; this.extnValue = extnValue; } public Extension(int[] extnID, boolean critical, byte[] extnValue) { this.extnID = extnID; this.critical = critical; this.extnValue = extnValue; } public Extension(String extnID, byte[] extnValue) { this(extnID, NON_CRITICAL, extnValue); } public Extension(int[] extnID, byte[] extnValue) { this(extnID, NON_CRITICAL, extnValue); } private Extension( int[] extnID, boolean critical, byte[] extnValue, byte[] rawExtnValue, byte[] encoding, ExtensionValue decodedExtValue) { this(extnID, critical, extnValue); this.rawExtnValue = rawExtnValue; this.encoding = encoding; this.extnValueObject = decodedExtValue; this.valueDecoded = (decodedExtValue != null); } /** Returns the value of extnID field of the structure. */ public String getExtnID() { if (extnID_str == null) { extnID_str = ObjectIdentifier.toString(extnID); } return extnID_str; } /** Returns the value of critical field of the structure. */ public boolean getCritical() { return critical; } /** Returns the value of extnValue field of the structure. */ public byte[] getExtnValue() { return extnValue; } /** Returns the raw (undecoded octet string) value of extnValue field of the structure. */ public byte[] getRawExtnValue() { if (rawExtnValue == null) { rawExtnValue = ASN1OctetString.getInstance().encode(extnValue); } return rawExtnValue; } /** Returns ASN.1 encoded form of this X.509 Extension value. */ public byte[] getEncoded() { if (encoding == null) { encoding = Extension.ASN1.encode(this); } return encoding; } @Override public boolean equals(Object ext) { if (!(ext instanceof Extension)) { return false; } Extension extension = (Extension) ext; return Arrays.equals(extnID, extension.extnID) && (critical == extension.critical) && Arrays.equals(extnValue, extension.extnValue); } @Override public int hashCode() { return (Arrays.hashCode(extnID) * 37 + (critical ? 1 : 0)) * 37 + Arrays.hashCode(extnValue); } public ExtensionValue getDecodedExtensionValue() throws IOException { if (!valueDecoded) { decodeExtensionValue(); } return extnValueObject; } public KeyUsage getKeyUsageValue() { if (!valueDecoded) { try { decodeExtensionValue(); } catch (IOException ignored) { } } if (extnValueObject instanceof KeyUsage) { return (KeyUsage) extnValueObject; } else { return null; } } public BasicConstraints getBasicConstraintsValue() { if (!valueDecoded) { try { decodeExtensionValue(); } catch (IOException ignored) { } } if (extnValueObject instanceof BasicConstraints) { return (BasicConstraints) extnValueObject; } else { return null; } } private void decodeExtensionValue() throws IOException { if (valueDecoded) { return; } valueDecoded = true; if (Arrays.equals(extnID, SUBJ_KEY_ID)) { extnValueObject = SubjectKeyIdentifier.decode(extnValue); } else if (Arrays.equals(extnID, KEY_USAGE)) { extnValueObject = new KeyUsage(extnValue); } else if (Arrays.equals(extnID, SUBJECT_ALT_NAME)) { extnValueObject = new AlternativeName(AlternativeName.SUBJECT, extnValue); } else if (Arrays.equals(extnID, ISSUER_ALTERNATIVE_NAME)) { extnValueObject = new AlternativeName(AlternativeName.SUBJECT, extnValue); } else if (Arrays.equals(extnID, BASIC_CONSTRAINTS)) { extnValueObject = new BasicConstraints(extnValue); } else if (Arrays.equals(extnID, NAME_CONSTRAINTS)) { extnValueObject = NameConstraints.decode(extnValue); } else if (Arrays.equals(extnID, CERTIFICATE_POLICIES)) { extnValueObject = CertificatePolicies.decode(extnValue); } else if (Arrays.equals(extnID, AUTH_KEY_ID)) { extnValueObject = AuthorityKeyIdentifier.decode(extnValue); } else if (Arrays.equals(extnID, POLICY_CONSTRAINTS)) { extnValueObject = new PolicyConstraints(extnValue); } else if (Arrays.equals(extnID, EXTENDED_KEY_USAGE)) { extnValueObject = new ExtendedKeyUsage(extnValue); } else if (Arrays.equals(extnID, INHIBIT_ANY_POLICY)) { extnValueObject = new InhibitAnyPolicy(extnValue); } else if (Arrays.equals(extnID, CERTIFICATE_ISSUER)) { extnValueObject = new CertificateIssuer(extnValue); } else if (Arrays.equals(extnID, CRL_DISTR_POINTS)) { extnValueObject = CRLDistributionPoints.decode(extnValue); } else if (Arrays.equals(extnID, CERTIFICATE_ISSUER)) { extnValueObject = new ReasonCode(extnValue); } else if (Arrays.equals(extnID, INVALIDITY_DATE)) { extnValueObject = new InvalidityDate(extnValue); } else if (Arrays.equals(extnID, REASON_CODE)) { extnValueObject = new ReasonCode(extnValue); } else if (Arrays.equals(extnID, CRL_NUMBER)) { extnValueObject = new CRLNumber(extnValue); } else if (Arrays.equals(extnID, ISSUING_DISTR_POINTS)) { extnValueObject = IssuingDistributionPoint.decode(extnValue); } else if (Arrays.equals(extnID, AUTHORITY_INFO_ACCESS)) { extnValueObject = InfoAccessSyntax.decode(extnValue); } else if (Arrays.equals(extnID, SUBJECT_INFO_ACCESS)) { extnValueObject = InfoAccessSyntax.decode(extnValue); } } public void dumpValue(StringBuilder sb, String prefix) { sb.append("OID: ").append(getExtnID()).append(", Critical: ").append(critical).append('\n'); if (!valueDecoded) { try { decodeExtensionValue(); } catch (IOException ignored) { } } if (extnValueObject != null) { extnValueObject.dumpValue(sb, prefix); return; } // else: dump unparsed hex representation sb.append(prefix); if (Arrays.equals(extnID, SUBJ_DIRECTORY_ATTRS)) { sb.append("Subject Directory Attributes Extension"); } else if (Arrays.equals(extnID, SUBJ_KEY_ID)) { sb.append("Subject Key Identifier Extension"); } else if (Arrays.equals(extnID, KEY_USAGE)) { sb.append("Key Usage Extension"); } else if (Arrays.equals(extnID, PRIVATE_KEY_USAGE_PERIOD)) { sb.append("Private Key Usage Period Extension"); } else if (Arrays.equals(extnID, SUBJECT_ALT_NAME)) { sb.append("Subject Alternative Name Extension"); } else if (Arrays.equals(extnID, ISSUER_ALTERNATIVE_NAME)) { sb.append("Issuer Alternative Name Extension"); } else if (Arrays.equals(extnID, BASIC_CONSTRAINTS)) { sb.append("Basic Constraints Extension"); } else if (Arrays.equals(extnID, NAME_CONSTRAINTS)) { sb.append("Name Constraints Extension"); } else if (Arrays.equals(extnID, CRL_DISTR_POINTS)) { sb.append("CRL Distribution Points Extension"); } else if (Arrays.equals(extnID, CERTIFICATE_POLICIES)) { sb.append("Certificate Policies Extension"); } else if (Arrays.equals(extnID, POLICY_MAPPINGS)) { sb.append("Policy Mappings Extension"); } else if (Arrays.equals(extnID, AUTH_KEY_ID)) { sb.append("Authority Key Identifier Extension"); } else if (Arrays.equals(extnID, POLICY_CONSTRAINTS)) { sb.append("Policy Constraints Extension"); } else if (Arrays.equals(extnID, EXTENDED_KEY_USAGE)) { sb.append("Extended Key Usage Extension"); } else if (Arrays.equals(extnID, INHIBIT_ANY_POLICY)) { sb.append("Inhibit Any-Policy Extension"); } else if (Arrays.equals(extnID, AUTHORITY_INFO_ACCESS)) { sb.append("Authority Information Access Extension"); } else if (Arrays.equals(extnID, SUBJECT_INFO_ACCESS)) { sb.append("Subject Information Access Extension"); } else if (Arrays.equals(extnID, INVALIDITY_DATE)) { sb.append("Invalidity Date Extension"); } else if (Arrays.equals(extnID, CRL_NUMBER)) { sb.append("CRL Number Extension"); } else if (Arrays.equals(extnID, REASON_CODE)) { sb.append("Reason Code Extension"); } else { sb.append("Unknown Extension"); } sb.append('\n').append(prefix).append("Unparsed Extension Value:\n"); sb.append(Array.toString(extnValue, prefix)); } /** X.509 Extension encoder/decoder. */ public static final ASN1Sequence ASN1 = new ASN1Sequence( new ASN1Type[] { ASN1Oid.getInstance(), ASN1Boolean.getInstance(), new ASN1OctetString() { @Override public Object getDecodedObject(BerInputStream in) throws IOException { // first - decoded octet string, // second - raw encoding of octet string return new Object[] {super.getDecodedObject(in), in.getEncoded()}; } } }) { { setDefault(Boolean.FALSE, 1); } @Override protected Object getDecodedObject(BerInputStream in) throws IOException { Object[] values = (Object[]) in.content; int[] oid = (int[]) values[0]; byte[] extnValue = (byte[]) ((Object[]) values[2])[0]; byte[] rawExtnValue = (byte[]) ((Object[]) values[2])[1]; ExtensionValue decodedExtValue = null; // decode Key Usage and Basic Constraints extension values if (Arrays.equals(oid, KEY_USAGE)) { decodedExtValue = new KeyUsage(extnValue); } else if (Arrays.equals(oid, BASIC_CONSTRAINTS)) { decodedExtValue = new BasicConstraints(extnValue); } return new Extension( (int[]) values[0], (Boolean) values[1], extnValue, rawExtnValue, in.getEncoded(), decodedExtValue); } @Override protected void getValues(Object object, Object[] values) { Extension ext = (Extension) object; values[0] = ext.extnID; values[1] = (ext.critical) ? Boolean.TRUE : Boolean.FALSE; values[2] = ext.extnValue; } }; }
/** * CRL's Issuing Distribution Point Extension (OID = 2.5.29.28). * * <pre> * id-ce-issuingDistributionPoint OBJECT IDENTIFIER ::= { id-ce 28 } * * 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, * onlyContainsAttributeCerts [5] BOOLEAN DEFAULT FALSE * } * </pre> * * (as specified in RFC 3280 http://www.ietf.org/rfc/rfc3280.txt) */ public class IssuingDistributionPoint extends ExtensionValue { // values of the fields of the structure private DistributionPointName distributionPoint; private boolean onlyContainsUserCerts = false; private boolean onlyContainsCACerts = false; private ReasonFlags onlySomeReasons; private boolean indirectCRL = false; private boolean onlyContainsAttributeCerts = false; /** * Constructs the object on the base of its distributionPoint and onlySomeReasons fields values. */ public IssuingDistributionPoint( DistributionPointName distributionPoint, ReasonFlags onlySomeReasons) { this.distributionPoint = distributionPoint; this.onlySomeReasons = onlySomeReasons; } /** Creates the extension object on the base of its encoded form. */ public static IssuingDistributionPoint decode(byte[] encoding) throws IOException { IssuingDistributionPoint idp = (IssuingDistributionPoint) ASN1.decode(encoding); idp.encoding = encoding; return idp; } /** Sets the value of onlyContainsUserCerts field of the structure. */ public void setOnlyContainsUserCerts(boolean onlyContainsUserCerts) { this.onlyContainsUserCerts = onlyContainsUserCerts; } /** Sets the value of onlyContainsCACerts field of the structure. */ public void setOnlyContainsCACerts(boolean onlyContainsCACerts) { this.onlyContainsCACerts = onlyContainsCACerts; } /** Sets the value of indirectCRL field of the structure. */ public void setIndirectCRL(boolean indirectCRL) { this.indirectCRL = indirectCRL; } /** Sets the value of onlyContainsAttributeCerts field of the structure. */ public void setOnlyContainsAttributeCerts(boolean onlyContainsAttributeCerts) { this.onlyContainsAttributeCerts = onlyContainsAttributeCerts; } /** Returns value of distributionPoint field of the structure. */ public DistributionPointName getDistributionPoint() { return distributionPoint; } /** Returns value of onlyContainsUserCerts field of the structure. */ public boolean getOnlyContainsUserCerts() { return onlyContainsUserCerts; } /** Returns value of onlyContainsCACerts field of the structure. */ public boolean getOnlyContainsCACerts() { return onlyContainsCACerts; } /** Returns value of onlySomeReasons field of the structure. */ public ReasonFlags getOnlySomeReasons() { return onlySomeReasons; } /** Returns value of indirectCRL field of the structure. */ public boolean getIndirectCRL() { return indirectCRL; } /** Returns value of onlyContainsAttributeCerts field of the structure. */ public boolean getOnlyContainsAttributeCerts() { return onlyContainsAttributeCerts; } /** * Returns ASN.1 encoded form of this X.509 IssuingDistributionPoint value. * * @return a byte array containing ASN.1 encoded form. */ public byte[] getEncoded() { if (encoding == null) { encoding = ASN1.encode(this); } return encoding; } /** Places the string representation of extension value into the StringBuffer object. */ public void dumpValue(StringBuffer buffer, String prefix) { buffer.append(prefix).append("Issuing Distribution Point: [\n"); // $NON-NLS-1$ if (distributionPoint != null) { distributionPoint.dumpValue(buffer, " " + prefix); // $NON-NLS-1$ } buffer .append(prefix) .append(" onlyContainsUserCerts: ") // $NON-NLS-1$ .append(onlyContainsUserCerts) .append('\n'); buffer .append(prefix) .append(" onlyContainsCACerts: ") // $NON-NLS-1$ .append(onlyContainsCACerts) .append('\n'); if (onlySomeReasons != null) { onlySomeReasons.dumpValue(buffer, prefix + " "); // $NON-NLS-1$ } buffer .append(prefix) .append(" indirectCRL: ") // $NON-NLS-1$ .append(indirectCRL) .append('\n'); buffer .append(prefix) .append(" onlyContainsAttributeCerts: ") // $NON-NLS-1$ .append(onlyContainsAttributeCerts) .append('\n'); } /** ASN.1 Encoder/Decoder. */ public static final ASN1Type ASN1 = new ASN1Sequence( new ASN1Type[] { // ASN.1 prohibits implicitly tagged CHOICE new ASN1Explicit(0, DistributionPointName.ASN1), new ASN1Implicit(1, ASN1Boolean.getInstance()), new ASN1Implicit(2, ASN1Boolean.getInstance()), new ASN1Implicit(3, ReasonFlags.ASN1), new ASN1Implicit(4, ASN1Boolean.getInstance()), new ASN1Implicit(5, ASN1Boolean.getInstance()) }) { { setOptional(0); setOptional(3); setDefault(Boolean.FALSE, 1); setDefault(Boolean.FALSE, 2); setDefault(Boolean.FALSE, 4); setDefault(Boolean.FALSE, 5); } protected Object getDecodedObject(BerInputStream in) { Object[] values = (Object[]) in.content; IssuingDistributionPoint idp = new IssuingDistributionPoint( (DistributionPointName) values[0], (ReasonFlags) values[3]); idp.encoding = in.getEncoded(); if (values[1] != null) { idp.setOnlyContainsUserCerts(((Boolean) values[1]).booleanValue()); } if (values[2] != null) { idp.setOnlyContainsCACerts(((Boolean) values[2]).booleanValue()); } if (values[4] != null) { idp.setIndirectCRL(((Boolean) values[4]).booleanValue()); } if (values[5] != null) { idp.setOnlyContainsAttributeCerts(((Boolean) values[5]).booleanValue()); } return idp; } protected void getValues(Object object, Object[] values) { IssuingDistributionPoint idp = (IssuingDistributionPoint) object; values[0] = idp.distributionPoint; values[1] = (idp.onlyContainsUserCerts) ? Boolean.TRUE : null; values[2] = (idp.onlyContainsCACerts) ? Boolean.TRUE : null; values[3] = idp.onlySomeReasons; values[4] = (idp.indirectCRL) ? Boolean.TRUE : null; values[5] = (idp.onlyContainsAttributeCerts) ? Boolean.TRUE : null; } }; }