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