1/* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18/** 19* @author Alexander Y. Kleymenov 20* @version $Revision$ 21*/ 22 23package org.apache.harmony.security.x509; 24 25import java.io.IOException; 26import java.util.Arrays; 27 28import org.apache.harmony.security.asn1.ASN1Boolean; 29import org.apache.harmony.security.asn1.ASN1OctetString; 30import org.apache.harmony.security.asn1.ASN1Oid; 31import org.apache.harmony.security.asn1.ASN1Sequence; 32import org.apache.harmony.security.asn1.ASN1Type; 33import org.apache.harmony.security.asn1.BerInputStream; 34import org.apache.harmony.security.asn1.ObjectIdentifier; 35import org.apache.harmony.security.utils.Array; 36 37/** 38 * The class encapsulates the ASN.1 DER encoding/decoding work 39 * with the Extension part of X.509 certificate 40 * (as specified in RFC 3280 - 41 * Internet X.509 Public Key Infrastructure. 42 * Certificate and Certificate Revocation List (CRL) Profile. 43 * http://www.ietf.org/rfc/rfc3280.txt): 44 * 45 * <pre> 46 * Extension ::= SEQUENCE { 47 * extnID OBJECT IDENTIFIER, 48 * critical BOOLEAN DEFAULT FALSE, 49 * extnValue OCTET STRING 50 * } 51 * </pre> 52 */ 53 54public class Extension { 55 // critical constants 56 public static final boolean CRITICAL = true; 57 public static final boolean NON_CRITICAL = false; 58 59 // constants: the extension OIDs 60 // certificate extensions: 61 static final int[] SUBJ_DIRECTORY_ATTRS = {2, 5, 29, 9}; 62 static final int[] SUBJ_KEY_ID = {2, 5, 29, 14}; 63 static final int[] KEY_USAGE = {2, 5, 29, 15}; 64 static final int[] PRIVATE_KEY_USAGE_PERIOD = {2, 5, 29, 16}; 65 static final int[] SUBJECT_ALT_NAME = {2, 5, 29, 17}; 66 static final int[] ISSUER_ALTERNATIVE_NAME = {2, 5, 29, 18}; 67 static final int[] BASIC_CONSTRAINTS = {2, 5, 29, 19}; 68 static final int[] NAME_CONSTRAINTS = {2, 5, 29, 30}; 69 static final int[] CRL_DISTR_POINTS = {2, 5, 29, 31}; 70 static final int[] CERTIFICATE_POLICIES = {2, 5, 29, 32}; 71 static final int[] POLICY_MAPPINGS = {2, 5, 29, 33}; 72 static final int[] AUTH_KEY_ID = {2, 5, 29, 35}; 73 static final int[] POLICY_CONSTRAINTS = {2, 5, 29, 36}; 74 static final int[] EXTENDED_KEY_USAGE = {2, 5, 29, 37}; 75 static final int[] FRESHEST_CRL = {2, 5, 29, 46}; 76 static final int[] INHIBIT_ANY_POLICY = {2, 5, 29, 54}; 77 static final int[] AUTHORITY_INFO_ACCESS = 78 {1, 3, 6, 1, 5, 5, 7, 1, 1}; 79 static final int[] SUBJECT_INFO_ACCESS = 80 {1, 3, 6, 1, 5, 5, 7, 1, 11}; 81 // crl extensions: 82 static final int[] ISSUING_DISTR_POINT = {2, 5, 29, 28}; 83 // crl entry extensions: 84 static final int[] CRL_NUMBER = {2, 5, 29, 20}; 85 static final int[] CERTIFICATE_ISSUER = {2, 5, 29, 29}; 86 static final int[] INVALIDITY_DATE = {2, 5, 29, 24}; 87 static final int[] REASON_CODE = {2, 5, 29, 21}; 88 static final int[] ISSUING_DISTR_POINTS = {2, 5, 29, 28}; 89 90 // the value of extnID field of the structure 91 private final int[] extnID; 92 private String extnID_str; 93 // the value of critical field of the structure 94 private final boolean critical; 95 // the value of extnValue field of the structure 96 private final byte[] extnValue; 97 // the ASN.1 encoded form of Extension 98 private byte[] encoding; 99 // the raw (not decoded) value of extnValue field of the structure 100 private byte[] rawExtnValue; 101 // the decoded extension value 102 protected ExtensionValue extnValueObject; 103 // tells whether extension value has been decoded or not 104 private boolean valueDecoded = false; 105 106 /** 107 * TODO 108 * @param extnID: String 109 * @param critical: boolean 110 * @param extnValue: byte[] 111 */ 112 public Extension(String extnID, boolean critical, 113 ExtensionValue extnValueObject) { 114 this.extnID_str = extnID; 115 this.extnID = ObjectIdentifier.toIntArray(extnID); 116 this.critical = critical; 117 this.extnValueObject = extnValueObject; 118 this.valueDecoded = true; 119 this.extnValue = extnValueObject.getEncoded(); 120 } 121 122 /** 123 * TODO 124 * @param extnID: String 125 * @param critical: boolean 126 * @param extnValue: byte[] 127 */ 128 public Extension(String extnID, boolean critical, byte[] extnValue) { 129 this.extnID_str = extnID; 130 this.extnID = ObjectIdentifier.toIntArray(extnID); 131 this.critical = critical; 132 this.extnValue = extnValue; 133 } 134 135 /** 136 * TODO 137 * @param extnID: int[] 138 * @param critical: boolean 139 * @param extnValue: byte[] 140 */ 141 public Extension(int[] extnID, boolean critical, byte[] extnValue) { 142 this.extnID = extnID; 143 this.critical = critical; 144 this.extnValue = extnValue; 145 } 146 147 /** 148 * TODO 149 * @param extnID: String 150 * @param extnValue: byte[] 151 */ 152 public Extension(String extnID, byte[] extnValue) { 153 this(extnID, NON_CRITICAL, extnValue); 154 } 155 156 /** 157 * TODO 158 * @param extnID: int[] 159 * @param extnValue: byte[] 160 */ 161 public Extension(int[] extnID, byte[] extnValue) { 162 this(extnID, NON_CRITICAL, extnValue); 163 } 164 165 // 166 // TODO 167 // @param extnID: int[] 168 // @param critical: boolean 169 // @param extnValue: byte[] 170 // @param encoding: byte[] 171 // 172 private Extension(int[] extnID, boolean critical, byte[] extnValue, 173 byte[] rawExtnValue, byte[] encoding, 174 ExtensionValue decodedExtValue) { 175 this(extnID, critical, extnValue); 176 this.rawExtnValue = rawExtnValue; 177 this.encoding = encoding; 178 this.extnValueObject = decodedExtValue; 179 this.valueDecoded = (decodedExtValue != null); 180 } 181 182 /** 183 * Returns the value of extnID field of the structure. 184 * @return extnID 185 */ 186 public String getExtnID() { 187 if (extnID_str == null) { 188 extnID_str = ObjectIdentifier.toString(extnID); 189 } 190 return extnID_str; 191 } 192 193 /** 194 * Returns the value of critical field of the structure. 195 * @return critical 196 */ 197 public boolean getCritical() { 198 return critical; 199 } 200 201 /** 202 * Returns the value of extnValue field of the structure. 203 * @return extnValue 204 */ 205 public byte[] getExtnValue() { 206 return extnValue; 207 } 208 209 /** 210 * Returns the raw (undecoded octet string) value of extnValue 211 * field of the structure. 212 * @return rawExtnValue 213 */ 214 public byte[] getRawExtnValue() { 215 if (rawExtnValue == null) { 216 rawExtnValue = ASN1OctetString.getInstance().encode(extnValue); 217 } 218 return rawExtnValue; 219 } 220 221 /** 222 * Returns ASN.1 encoded form of this X.509 Extension value. 223 * @return a byte array containing ASN.1 encode form. 224 */ 225 public byte[] getEncoded() { 226 if (encoding == null) { 227 encoding = Extension.ASN1.encode(this); 228 } 229 return encoding; 230 } 231 232 public boolean equals(Object ext) { 233 if (!(ext instanceof Extension)) { 234 return false; 235 } 236 Extension extn = (Extension) ext; 237 return Arrays.equals(extnID, extn.extnID) 238 && (critical == extn.critical) 239 && Arrays.equals(extnValue, extn.extnValue); 240 } 241 242 public int hashCode() { 243 return (extnID.hashCode() * 37 + (critical ? 1 : 0)) * 37 + extnValue.hashCode(); 244 } 245 246 public ExtensionValue getDecodedExtensionValue() throws IOException { 247 if (!valueDecoded) { 248 decodeExtensionValue(); 249 } 250 return extnValueObject; 251 } 252 253 public KeyUsage getKeyUsageValue() { 254 if (!valueDecoded) { 255 try { 256 decodeExtensionValue(); 257 } catch (IOException e) { } 258 } 259 if (extnValueObject instanceof KeyUsage) { 260 return (KeyUsage) extnValueObject; 261 } else { 262 return null; 263 } 264 } 265 266 public BasicConstraints getBasicConstraintsValue() { 267 if (!valueDecoded) { 268 try { 269 decodeExtensionValue(); 270 } catch (IOException e) { } 271 } 272 if (extnValueObject instanceof BasicConstraints) { 273 return (BasicConstraints) extnValueObject; 274 } else { 275 return null; 276 } 277 } 278 279 private void decodeExtensionValue() throws IOException { 280 if (valueDecoded) { 281 return; 282 } 283 valueDecoded = true; 284 if (oidEquals(extnID, SUBJ_KEY_ID)) { 285 extnValueObject = SubjectKeyIdentifier.decode(extnValue); 286 } else if (oidEquals(extnID, KEY_USAGE)) { 287 extnValueObject = new KeyUsage(extnValue); 288 } else if (oidEquals(extnID, SUBJECT_ALT_NAME)) { 289 extnValueObject = new AlternativeName( 290 AlternativeName.SUBJECT, extnValue); 291 } else if (oidEquals(extnID, ISSUER_ALTERNATIVE_NAME)) { 292 extnValueObject = new AlternativeName( 293 AlternativeName.SUBJECT, extnValue); 294 } else if (oidEquals(extnID, BASIC_CONSTRAINTS)) { 295 extnValueObject = new BasicConstraints(extnValue); 296 } else if (oidEquals(extnID, NAME_CONSTRAINTS)) { 297 extnValueObject = NameConstraints.decode(extnValue); 298 } else if (oidEquals(extnID, CERTIFICATE_POLICIES)) { 299 extnValueObject = CertificatePolicies.decode(extnValue); 300 } else if (oidEquals(extnID, AUTH_KEY_ID)) { 301 extnValueObject = AuthorityKeyIdentifier.decode(extnValue); 302 } else if (oidEquals(extnID, POLICY_CONSTRAINTS)) { 303 extnValueObject = new PolicyConstraints(extnValue); 304 } else if (oidEquals(extnID, EXTENDED_KEY_USAGE)) { 305 extnValueObject = new ExtendedKeyUsage(extnValue); 306 } else if (oidEquals(extnID, INHIBIT_ANY_POLICY)) { 307 extnValueObject = new InhibitAnyPolicy(extnValue); 308 } else if (oidEquals(extnID, CERTIFICATE_ISSUER)) { 309 extnValueObject = new CertificateIssuer(extnValue); 310 } else if (oidEquals(extnID, CRL_DISTR_POINTS)) { 311 extnValueObject = CRLDistributionPoints.decode(extnValue); 312 } else if (oidEquals(extnID, CERTIFICATE_ISSUER)) { 313 extnValueObject = new ReasonCode(extnValue); 314 } else if (oidEquals(extnID, INVALIDITY_DATE)) { 315 extnValueObject = new InvalidityDate(extnValue); 316 } else if (oidEquals(extnID, REASON_CODE)) { 317 extnValueObject = new ReasonCode(extnValue); 318 } else if (oidEquals(extnID, CRL_NUMBER)) { 319 extnValueObject = new CRLNumber(extnValue); 320 } else if (oidEquals(extnID, ISSUING_DISTR_POINTS)) { 321 extnValueObject = IssuingDistributionPoint.decode(extnValue); 322 } else if (oidEquals(extnID, AUTHORITY_INFO_ACCESS)) { 323 extnValueObject = InfoAccessSyntax.decode(extnValue); 324 } else if (oidEquals(extnID, SUBJECT_INFO_ACCESS)) { 325 extnValueObject = InfoAccessSyntax.decode(extnValue); 326 } 327 } 328 329 /** 330 * Places the string representation into the StringBuffer object. 331 */ 332 public void dumpValue(StringBuffer buffer, String prefix) { 333 buffer.append("OID: ").append(getExtnID()) //$NON-NLS-1$ 334 .append(", Critical: ").append(critical).append('\n'); //$NON-NLS-1$ 335 if (!valueDecoded) { 336 try { 337 decodeExtensionValue(); 338 } catch (IOException e) { } 339 } 340 if (extnValueObject != null) { 341 extnValueObject.dumpValue(buffer, prefix); 342 return; 343 } 344 // else: dump unparsed hex representation 345 buffer.append(prefix); 346 if (oidEquals(extnID, SUBJ_DIRECTORY_ATTRS)) { 347 buffer.append("Subject Directory Attributes Extension"); //$NON-NLS-1$ 348 } else if (oidEquals(extnID, SUBJ_KEY_ID)) { 349 buffer.append("Subject Key Identifier Extension"); //$NON-NLS-1$ 350 } else if (oidEquals(extnID, KEY_USAGE)) { 351 buffer.append("Key Usage Extension"); //$NON-NLS-1$ 352 } else if (oidEquals(extnID, PRIVATE_KEY_USAGE_PERIOD)) { 353 buffer.append("Private Key Usage Period Extension"); //$NON-NLS-1$ 354 } else if (oidEquals(extnID, SUBJECT_ALT_NAME)) { 355 buffer.append("Subject Alternative Name Extension"); //$NON-NLS-1$ 356 } else if (oidEquals(extnID, ISSUER_ALTERNATIVE_NAME)) { 357 buffer.append("Issuer Alternative Name Extension"); //$NON-NLS-1$ 358 } else if (oidEquals(extnID, BASIC_CONSTRAINTS)) { 359 buffer.append("Basic Constraints Extension"); //$NON-NLS-1$ 360 } else if (oidEquals(extnID, NAME_CONSTRAINTS)) { 361 buffer.append("Name Constraints Extension"); //$NON-NLS-1$ 362 } else if (oidEquals(extnID, CRL_DISTR_POINTS)) { 363 buffer.append("CRL Distribution Points Extension"); //$NON-NLS-1$ 364 } else if (oidEquals(extnID, CERTIFICATE_POLICIES)) { 365 buffer.append("Certificate Policies Extension"); //$NON-NLS-1$ 366 } else if (oidEquals(extnID, POLICY_MAPPINGS)) { 367 buffer.append("Policy Mappings Extension"); //$NON-NLS-1$ 368 } else if (oidEquals(extnID, AUTH_KEY_ID)) { 369 buffer.append("Authority Key Identifier Extension"); //$NON-NLS-1$ 370 } else if (oidEquals(extnID, POLICY_CONSTRAINTS)) { 371 buffer.append("Policy Constraints Extension"); //$NON-NLS-1$ 372 } else if (oidEquals(extnID, EXTENDED_KEY_USAGE)) { 373 buffer.append("Extended Key Usage Extension"); //$NON-NLS-1$ 374 } else if (oidEquals(extnID, INHIBIT_ANY_POLICY)) { 375 buffer.append("Inhibit Any-Policy Extension"); //$NON-NLS-1$ 376 } else if (oidEquals(extnID, AUTHORITY_INFO_ACCESS)) { 377 buffer.append("Authority Information Access Extension"); //$NON-NLS-1$ 378 } else if (oidEquals(extnID, SUBJECT_INFO_ACCESS)) { 379 buffer.append("Subject Information Access Extension"); //$NON-NLS-1$ 380 } else if (oidEquals(extnID, INVALIDITY_DATE)) { 381 buffer.append("Invalidity Date Extension"); //$NON-NLS-1$ 382 } else if (oidEquals(extnID, CRL_NUMBER)) { 383 buffer.append("CRL Number Extension"); //$NON-NLS-1$ 384 } else if (oidEquals(extnID, REASON_CODE)) { 385 buffer.append("Reason Code Extension"); //$NON-NLS-1$ 386 } else { 387 buffer.append("Unknown Extension"); //$NON-NLS-1$ 388 } 389 buffer.append('\n').append(prefix) 390 .append("Unparsed Extension Value:\n"); //$NON-NLS-1$ 391 buffer.append(Array.toString(extnValue, prefix)); 392 } 393 394 395 // Compares two OIDs 396 private static boolean oidEquals(int[] oid1, int[] oid2) { 397 int length = oid1.length; 398 if (length != oid2.length) { 399 return false; 400 } 401 while (length > 0) { 402 if (oid1[--length] != oid2[length]) { 403 return false; 404 } 405 } 406 return true; 407 } 408 409 /** 410 * X.509 Extension encoder/decoder. 411 */ 412 public static final ASN1Sequence ASN1 = new ASN1Sequence(new ASN1Type[] { 413 ASN1Oid.getInstance(), 414 ASN1Boolean.getInstance(), 415 new ASN1OctetString() { 416 public Object getDecodedObject(BerInputStream in) 417 throws IOException { 418 // first - decoded octet string, 419 // second - raw encoding of octet string 420 return new Object[] 421 {super.getDecodedObject(in), in.getEncoded()}; 422 } 423 } 424 }) { 425 { 426 setDefault(Boolean.FALSE, 1); 427 } 428 429 protected Object getDecodedObject(BerInputStream in) throws IOException { 430 Object[] values = (Object[]) in.content; 431 432 int[] oid = (int[]) values[0]; 433 byte[] extnValue = (byte[]) ((Object[]) values[2])[0]; 434 byte[] rawExtnValue = (byte[]) ((Object[]) values[2])[1]; 435 436 ExtensionValue decodedExtValue = null; 437 // decode Key Usage and Basic Constraints extension values 438 if (oidEquals(oid, KEY_USAGE)) { 439 decodedExtValue = new KeyUsage(extnValue); 440 } else if (oidEquals(oid, BASIC_CONSTRAINTS)) { 441 decodedExtValue = new BasicConstraints(extnValue); 442 } 443 444 return 445 new Extension((int[]) values[0], 446 ((Boolean) values[1]).booleanValue(), 447 extnValue, rawExtnValue, in.getEncoded(), decodedExtValue); 448 } 449 450 protected void getValues(Object object, Object[] values) { 451 452 Extension ext = (Extension) object; 453 454 values[0] = ext.extnID; 455 values[1] = (ext.critical) ? Boolean.TRUE : Boolean.FALSE; 456 values[2] = ext.extnValue; 457 } 458 }; 459} 460 461