/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @author Alexander Y. Kleymenov
* @version $Revision$
*/
package org.apache.harmony.security.x509;
import java.io.IOException;
import java.util.Arrays;
import org.apache.harmony.security.asn1.ASN1Boolean;
import org.apache.harmony.security.asn1.ASN1OctetString;
import org.apache.harmony.security.asn1.ASN1Oid;
import org.apache.harmony.security.asn1.ASN1Sequence;
import org.apache.harmony.security.asn1.ASN1Type;
import org.apache.harmony.security.asn1.BerInputStream;
import org.apache.harmony.security.asn1.ObjectIdentifier;
import org.apache.harmony.security.utils.Array;
/**
* 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 class Extension {
// critical constants
public static final boolean CRITICAL = true;
public static final boolean NON_CRITICAL = false;
// constants: the extension OIDs
// certificate extensions:
public static final int[] SUBJ_DIRECTORY_ATTRS = {2, 5, 29, 9};
public static final int[] SUBJ_KEY_ID = {2, 5, 29, 14};
public static final int[] KEY_USAGE = {2, 5, 29, 15};
public static final int[] PRIVATE_KEY_USAGE_PERIOD = {2, 5, 29, 16};
public static final int[] SUBJECT_ALT_NAME = {2, 5, 29, 17};
public static final int[] ISSUER_ALTERNATIVE_NAME = {2, 5, 29, 18};
public static final int[] BASIC_CONSTRAINTS = {2, 5, 29, 19};
public static final int[] NAME_CONSTRAINTS = {2, 5, 29, 30};
public static final int[] CRL_DISTR_POINTS = {2, 5, 29, 31};
public static final int[] CERTIFICATE_POLICIES = {2, 5, 29, 32};
public static final int[] POLICY_MAPPINGS = {2, 5, 29, 33};
public static final int[] AUTH_KEY_ID = {2, 5, 29, 35};
public static final int[] POLICY_CONSTRAINTS = {2, 5, 29, 36};
public static final int[] EXTENDED_KEY_USAGE = {2, 5, 29, 37};
public static final int[] FRESHEST_CRL = {2, 5, 29, 46};
public static final int[] INHIBIT_ANY_POLICY = {2, 5, 29, 54};
public static final int[] AUTHORITY_INFO_ACCESS =
{1, 3, 6, 1, 5, 5, 7, 1, 1};
public static final int[] SUBJECT_INFO_ACCESS =
{1, 3, 6, 1, 5, 5, 7, 1, 11};
// crl extensions:
public static final int[] ISSUING_DISTR_POINT = {2, 5, 29, 28};
// crl entry extensions:
public static final int[] CRL_NUMBER = {2, 5, 29, 20};
public static final int[] CERTIFICATE_ISSUER = {2, 5, 29, 29};
public static final int[] INVALIDITY_DATE = {2, 5, 29, 24};
public static final int[] REASON_CODE = {2, 5, 29, 21};
public 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;
/**
* TODO
* @param extnID: String
* @param critical: boolean
* @param extnValue: byte[]
*/
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();
}
/**
* TODO
* @param extnID: String
* @param critical: boolean
* @param extnValue: byte[]
*/
public Extension(String extnID, boolean critical, byte[] extnValue) {
this.extnID_str = extnID;
this.extnID = ObjectIdentifier.toIntArray(extnID);
this.critical = critical;
this.extnValue = extnValue;
}
/**
* TODO
* @param extnID: int[]
* @param critical: boolean
* @param extnValue: byte[]
*/
public Extension(int[] extnID, boolean critical, byte[] extnValue) {
this.extnID = extnID;
this.critical = critical;
this.extnValue = extnValue;
}
/**
* TODO
* @param extnID: String
* @param extnValue: byte[]
*/
public Extension(String extnID, byte[] extnValue) {
this(extnID, NON_CRITICAL, extnValue);
}
/**
* TODO
* @param extnID: int[]
* @param extnValue: byte[]
*/
public Extension(int[] extnID, byte[] extnValue) {
this(extnID, NON_CRITICAL, extnValue);
}
//
// TODO
// @param extnID: int[]
// @param critical: boolean
// @param extnValue: byte[]
// @param encoding: byte[]
//
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.
* @return extnID
*/
public String getExtnID() {
if (extnID_str == null) {
extnID_str = ObjectIdentifier.toString(extnID);
}
return extnID_str;
}
/**
* Returns the value of critical field of the structure.
* @return critical
*/
public boolean getCritical() {
return critical;
}
/**
* Returns the value of extnValue field of the structure.
* @return extnValue
*/
public byte[] getExtnValue() {
return extnValue;
}
/**
* Returns the raw (undecoded octet string) value of extnValue
* field of the structure.
* @return rawExtnValue
*/
public byte[] getRawExtnValue() {
if (rawExtnValue == null) {
rawExtnValue = ASN1OctetString.getInstance().encode(extnValue);
}
return rawExtnValue;
}
/**
* Returns ASN.1 encoded form of this X.509 Extension value.
* @return a byte array containing ASN.1 encode form.
*/
public byte[] getEncoded() {
if (encoding == null) {
encoding = Extension.ASN1.encode(this);
}
return encoding;
}
public boolean equals(Object ext) {
if (!(ext instanceof Extension)) {
return false;
}
Extension extn = (Extension) ext;
return Arrays.equals(extnID, extn.extnID)
&& (critical == extn.critical)
&& Arrays.equals(extnValue, extn.extnValue);
}
public ExtensionValue getDecodedExtensionValue() throws IOException {
if (!valueDecoded) {
decodeExtensionValue();
}
return extnValueObject;
}
public KeyUsage getKeyUsageValue() {
if (!valueDecoded) {
try {
decodeExtensionValue();
} catch (IOException e) { }
}
if (extnValueObject instanceof KeyUsage) {
return (KeyUsage) extnValueObject;
} else {
return null;
}
}
public BasicConstraints getBasicConstraintsValue() {
if (!valueDecoded) {
try {
decodeExtensionValue();
} catch (IOException e) { }
}
if (extnValueObject instanceof BasicConstraints) {
return (BasicConstraints) extnValueObject;
} else {
return null;
}
}
private void decodeExtensionValue() throws IOException {
if (valueDecoded) {
return;
}
valueDecoded = true;
if (oidEquals(extnID, SUBJ_KEY_ID)) {
extnValueObject = SubjectKeyIdentifier.decode(extnValue);
} else if (oidEquals(extnID, KEY_USAGE)) {
extnValueObject = new KeyUsage(extnValue);
} else if (oidEquals(extnID, SUBJECT_ALT_NAME)) {
extnValueObject = new AlternativeName(
AlternativeName.SUBJECT, extnValue);
} else if (oidEquals(extnID, ISSUER_ALTERNATIVE_NAME)) {
extnValueObject = new AlternativeName(
AlternativeName.SUBJECT, extnValue);
} else if (oidEquals(extnID, BASIC_CONSTRAINTS)) {
extnValueObject = new BasicConstraints(extnValue);
} else if (oidEquals(extnID, NAME_CONSTRAINTS)) {
extnValueObject = NameConstraints.decode(extnValue);
} else if (oidEquals(extnID, CERTIFICATE_POLICIES)) {
extnValueObject = CertificatePolicies.decode(extnValue);
} else if (oidEquals(extnID, AUTH_KEY_ID)) {
extnValueObject = AuthorityKeyIdentifier.decode(extnValue);
} else if (oidEquals(extnID, POLICY_CONSTRAINTS)) {
extnValueObject = new PolicyConstraints(extnValue);
} else if (oidEquals(extnID, EXTENDED_KEY_USAGE)) {
extnValueObject = new ExtendedKeyUsage(extnValue);
} else if (oidEquals(extnID, INHIBIT_ANY_POLICY)) {
extnValueObject = new InhibitAnyPolicy(extnValue);
} else if (oidEquals(extnID, CERTIFICATE_ISSUER)) {
extnValueObject = new CertificateIssuer(extnValue);
} else if (oidEquals(extnID, CRL_DISTR_POINTS)) {
extnValueObject = CRLDistributionPoints.decode(extnValue);
} else if (oidEquals(extnID, CERTIFICATE_ISSUER)) {
extnValueObject = new ReasonCode(extnValue);
} else if (oidEquals(extnID, INVALIDITY_DATE)) {
extnValueObject = new InvalidityDate(extnValue);
} else if (oidEquals(extnID, REASON_CODE)) {
extnValueObject = new ReasonCode(extnValue);
} else if (oidEquals(extnID, CRL_NUMBER)) {
extnValueObject = new CRLNumber(extnValue);
} else if (oidEquals(extnID, ISSUING_DISTR_POINTS)) {
extnValueObject = IssuingDistributionPoint.decode(extnValue);
} else if (oidEquals(extnID, AUTHORITY_INFO_ACCESS)) {
extnValueObject = InfoAccessSyntax.decode(extnValue);
} else if (oidEquals(extnID, SUBJECT_INFO_ACCESS)) {
extnValueObject = InfoAccessSyntax.decode(extnValue);
}
}
/**
* Places the string representation into the StringBuffer object.
*/
public void dumpValue(StringBuffer buffer, String prefix) {
buffer.append("OID: ").append(getExtnID()) //$NON-NLS-1$
.append(", Critical: ").append(critical).append('\n'); //$NON-NLS-1$
if (!valueDecoded) {
try {
decodeExtensionValue();
} catch (IOException e) { }
}
if (extnValueObject != null) {
extnValueObject.dumpValue(buffer, prefix);
return;
}
// else: dump unparsed hex representation
buffer.append(prefix);
if (oidEquals(extnID, SUBJ_DIRECTORY_ATTRS)) {
buffer.append("Subject Directory Attributes Extension"); //$NON-NLS-1$
} else if (oidEquals(extnID, SUBJ_KEY_ID)) {
buffer.append("Subject Key Identifier Extension"); //$NON-NLS-1$
} else if (oidEquals(extnID, KEY_USAGE)) {
buffer.append("Key Usage Extension"); //$NON-NLS-1$
} else if (oidEquals(extnID, PRIVATE_KEY_USAGE_PERIOD)) {
buffer.append("Private Key Usage Period Extension"); //$NON-NLS-1$
} else if (oidEquals(extnID, SUBJECT_ALT_NAME)) {
buffer.append("Subject Alternative Name Extension"); //$NON-NLS-1$
} else if (oidEquals(extnID, ISSUER_ALTERNATIVE_NAME)) {
buffer.append("Issuer Alternative Name Extension"); //$NON-NLS-1$
} else if (oidEquals(extnID, BASIC_CONSTRAINTS)) {
buffer.append("Basic Constraints Extension"); //$NON-NLS-1$
} else if (oidEquals(extnID, NAME_CONSTRAINTS)) {
buffer.append("Name Constraints Extension"); //$NON-NLS-1$
} else if (oidEquals(extnID, CRL_DISTR_POINTS)) {
buffer.append("CRL Distribution Points Extension"); //$NON-NLS-1$
} else if (oidEquals(extnID, CERTIFICATE_POLICIES)) {
buffer.append("Certificate Policies Extension"); //$NON-NLS-1$
} else if (oidEquals(extnID, POLICY_MAPPINGS)) {
buffer.append("Policy Mappings Extension"); //$NON-NLS-1$
} else if (oidEquals(extnID, AUTH_KEY_ID)) {
buffer.append("Authority Key Identifier Extension"); //$NON-NLS-1$
} else if (oidEquals(extnID, POLICY_CONSTRAINTS)) {
buffer.append("Policy Constraints Extension"); //$NON-NLS-1$
} else if (oidEquals(extnID, EXTENDED_KEY_USAGE)) {
buffer.append("Extended Key Usage Extension"); //$NON-NLS-1$
} else if (oidEquals(extnID, INHIBIT_ANY_POLICY)) {
buffer.append("Inhibit Any-Policy Extension"); //$NON-NLS-1$
} else if (oidEquals(extnID, AUTHORITY_INFO_ACCESS)) {
buffer.append("Authority Information Access Extension"); //$NON-NLS-1$
} else if (oidEquals(extnID, SUBJECT_INFO_ACCESS)) {
buffer.append("Subject Information Access Extension"); //$NON-NLS-1$
} else if (oidEquals(extnID, INVALIDITY_DATE)) {
buffer.append("Invalidity Date Extension"); //$NON-NLS-1$
} else if (oidEquals(extnID, CRL_NUMBER)) {
buffer.append("CRL Number Extension"); //$NON-NLS-1$
} else if (oidEquals(extnID, REASON_CODE)) {
buffer.append("Reason Code Extension"); //$NON-NLS-1$
} else {
buffer.append("Unknown Extension"); //$NON-NLS-1$
}
buffer.append('\n').append(prefix)
.append("Unparsed Extension Value:\n"); //$NON-NLS-1$
buffer.append(Array.toString(extnValue, prefix));
}
// Compares two OIDs
private static boolean oidEquals(int[] oid1, int[] oid2) {
int length = oid1.length;
if (length != oid2.length) {
return false;
}
while (length > 0) {
if (oid1[--length] != oid2[length]) {
return false;
}
}
return true;
}
/**
* X.509 Extension encoder/decoder.
*/
public static final ASN1Sequence ASN1 = new ASN1Sequence(new ASN1Type[] {
ASN1Oid.getInstance(),
ASN1Boolean.getInstance(),
new ASN1OctetString() {
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);
}
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 (oidEquals(oid, KEY_USAGE)) {
decodedExtValue = new KeyUsage(extnValue);
} else if (oidEquals(oid, BASIC_CONSTRAINTS)) {
decodedExtValue = new BasicConstraints(extnValue);
}
return
new Extension((int[]) values[0],
((Boolean) values[1]).booleanValue(),
extnValue, rawExtnValue, in.getEncoded(), decodedExtValue);
}
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;
}
};
}
|