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.io.OutputStream; 27import java.util.Arrays; 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 */ 53public final class Extension implements java.security.cert.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 volatile boolean valueDecoded = false; 104 105 public Extension(String extnID, boolean critical, 106 ExtensionValue extnValueObject) { 107 this.extnID_str = extnID; 108 this.extnID = ObjectIdentifier.toIntArray(extnID); 109 this.critical = critical; 110 this.extnValueObject = extnValueObject; 111 this.valueDecoded = true; 112 this.extnValue = extnValueObject.getEncoded(); 113 } 114 115 public Extension(String extnID, boolean critical, byte[] extnValue) { 116 this.extnID_str = extnID; 117 this.extnID = ObjectIdentifier.toIntArray(extnID); 118 this.critical = critical; 119 this.extnValue = extnValue; 120 } 121 122 public Extension(int[] extnID, boolean critical, byte[] extnValue) { 123 this.extnID = extnID; 124 this.critical = critical; 125 this.extnValue = extnValue; 126 } 127 128 public Extension(String extnID, byte[] extnValue) { 129 this(extnID, NON_CRITICAL, extnValue); 130 } 131 132 public Extension(int[] extnID, byte[] extnValue) { 133 this(extnID, NON_CRITICAL, extnValue); 134 } 135 136 private Extension(int[] extnID, boolean critical, byte[] extnValue, 137 byte[] rawExtnValue, byte[] encoding, 138 ExtensionValue decodedExtValue) { 139 this(extnID, critical, extnValue); 140 this.rawExtnValue = rawExtnValue; 141 this.encoding = encoding; 142 this.extnValueObject = decodedExtValue; 143 this.valueDecoded = (decodedExtValue != null); 144 } 145 146 /** 147 * Returns the value of extnID field of the structure. 148 */ 149 @Override 150 public String getId() { 151 if (extnID_str == null) { 152 extnID_str = ObjectIdentifier.toString(extnID); 153 } 154 return extnID_str; 155 } 156 157 /** 158 * Returns the value of critical field of the structure. 159 */ 160 @Override 161 public boolean isCritical() { 162 return critical; 163 } 164 165 /** 166 * Returns the value of extnValue field of the structure. 167 */ 168 @Override 169 public byte[] getValue() { 170 return extnValue; 171 } 172 173 /** 174 * Returns the raw (undecoded octet string) value of extnValue 175 * field of the structure. 176 */ 177 public byte[] getRawExtnValue() { 178 if (rawExtnValue == null) { 179 rawExtnValue = ASN1OctetString.getInstance().encode(extnValue); 180 } 181 return rawExtnValue; 182 } 183 184 /** 185 * Returns ASN.1 encoded form of this X.509 Extension value. 186 */ 187 public byte[] getEncoded() { 188 if (encoding == null) { 189 encoding = Extension.ASN1.encode(this); 190 } 191 return encoding; 192 } 193 194 @Override 195 public void encode(OutputStream out) throws IOException { 196 out.write(getEncoded()); 197 } 198 199 @Override public boolean equals(Object ext) { 200 if (!(ext instanceof Extension)) { 201 return false; 202 } 203 Extension extension = (Extension) ext; 204 return Arrays.equals(extnID, extension.extnID) 205 && (critical == extension.critical) 206 && Arrays.equals(extnValue, extension.extnValue); 207 } 208 209 @Override public int hashCode() { 210 return (Arrays.hashCode(extnID) * 37 + (critical ? 1 : 0)) * 37 + Arrays.hashCode(extnValue); 211 } 212 213 public ExtensionValue getDecodedExtensionValue() throws IOException { 214 if (!valueDecoded) { 215 decodeExtensionValue(); 216 } 217 return extnValueObject; 218 } 219 220 public KeyUsage getKeyUsageValue() { 221 if (!valueDecoded) { 222 try { 223 decodeExtensionValue(); 224 } catch (IOException ignored) { 225 } 226 } 227 if (extnValueObject instanceof KeyUsage) { 228 return (KeyUsage) extnValueObject; 229 } else { 230 return null; 231 } 232 } 233 234 public BasicConstraints getBasicConstraintsValue() { 235 if (!valueDecoded) { 236 try { 237 decodeExtensionValue(); 238 } catch (IOException ignored) { 239 } 240 } 241 if (extnValueObject instanceof BasicConstraints) { 242 return (BasicConstraints) extnValueObject; 243 } else { 244 return null; 245 } 246 } 247 248 private void decodeExtensionValue() throws IOException { 249 if (valueDecoded) { 250 return; 251 } 252 if (Arrays.equals(extnID, SUBJ_KEY_ID)) { 253 extnValueObject = SubjectKeyIdentifier.decode(extnValue); 254 } else if (Arrays.equals(extnID, KEY_USAGE)) { 255 extnValueObject = new KeyUsage(extnValue); 256 } else if (Arrays.equals(extnID, SUBJECT_ALT_NAME)) { 257 extnValueObject = new AlternativeName( 258 AlternativeName.SUBJECT, extnValue); 259 } else if (Arrays.equals(extnID, ISSUER_ALTERNATIVE_NAME)) { 260 extnValueObject = new AlternativeName( 261 AlternativeName.SUBJECT, extnValue); 262 } else if (Arrays.equals(extnID, BASIC_CONSTRAINTS)) { 263 extnValueObject = new BasicConstraints(extnValue); 264 } else if (Arrays.equals(extnID, NAME_CONSTRAINTS)) { 265 extnValueObject = NameConstraints.decode(extnValue); 266 } else if (Arrays.equals(extnID, CERTIFICATE_POLICIES)) { 267 extnValueObject = CertificatePolicies.decode(extnValue); 268 } else if (Arrays.equals(extnID, AUTH_KEY_ID)) { 269 extnValueObject = AuthorityKeyIdentifier.decode(extnValue); 270 } else if (Arrays.equals(extnID, POLICY_CONSTRAINTS)) { 271 extnValueObject = new PolicyConstraints(extnValue); 272 } else if (Arrays.equals(extnID, EXTENDED_KEY_USAGE)) { 273 extnValueObject = new ExtendedKeyUsage(extnValue); 274 } else if (Arrays.equals(extnID, INHIBIT_ANY_POLICY)) { 275 extnValueObject = new InhibitAnyPolicy(extnValue); 276 } else if (Arrays.equals(extnID, CERTIFICATE_ISSUER)) { 277 extnValueObject = new CertificateIssuer(extnValue); 278 } else if (Arrays.equals(extnID, CRL_DISTR_POINTS)) { 279 extnValueObject = CRLDistributionPoints.decode(extnValue); 280 } else if (Arrays.equals(extnID, CERTIFICATE_ISSUER)) { 281 extnValueObject = new ReasonCode(extnValue); 282 } else if (Arrays.equals(extnID, INVALIDITY_DATE)) { 283 extnValueObject = new InvalidityDate(extnValue); 284 } else if (Arrays.equals(extnID, REASON_CODE)) { 285 extnValueObject = new ReasonCode(extnValue); 286 } else if (Arrays.equals(extnID, CRL_NUMBER)) { 287 extnValueObject = new CRLNumber(extnValue); 288 } else if (Arrays.equals(extnID, ISSUING_DISTR_POINTS)) { 289 extnValueObject = IssuingDistributionPoint.decode(extnValue); 290 } else if (Arrays.equals(extnID, AUTHORITY_INFO_ACCESS)) { 291 extnValueObject = InfoAccessSyntax.decode(extnValue); 292 } else if (Arrays.equals(extnID, SUBJECT_INFO_ACCESS)) { 293 extnValueObject = InfoAccessSyntax.decode(extnValue); 294 } 295 valueDecoded = true; 296 } 297 298 public void dumpValue(StringBuilder sb, String prefix) { 299 sb.append("OID: ").append(getId()).append(", Critical: ").append(critical).append('\n'); 300 if (!valueDecoded) { 301 try { 302 decodeExtensionValue(); 303 } catch (IOException ignored) { 304 } 305 } 306 if (extnValueObject != null) { 307 extnValueObject.dumpValue(sb, prefix); 308 return; 309 } 310 // else: dump unparsed hex representation 311 sb.append(prefix); 312 if (Arrays.equals(extnID, SUBJ_DIRECTORY_ATTRS)) { 313 sb.append("Subject Directory Attributes Extension"); 314 } else if (Arrays.equals(extnID, SUBJ_KEY_ID)) { 315 sb.append("Subject Key Identifier Extension"); 316 } else if (Arrays.equals(extnID, KEY_USAGE)) { 317 sb.append("Key Usage Extension"); 318 } else if (Arrays.equals(extnID, PRIVATE_KEY_USAGE_PERIOD)) { 319 sb.append("Private Key Usage Period Extension"); 320 } else if (Arrays.equals(extnID, SUBJECT_ALT_NAME)) { 321 sb.append("Subject Alternative Name Extension"); 322 } else if (Arrays.equals(extnID, ISSUER_ALTERNATIVE_NAME)) { 323 sb.append("Issuer Alternative Name Extension"); 324 } else if (Arrays.equals(extnID, BASIC_CONSTRAINTS)) { 325 sb.append("Basic Constraints Extension"); 326 } else if (Arrays.equals(extnID, NAME_CONSTRAINTS)) { 327 sb.append("Name Constraints Extension"); 328 } else if (Arrays.equals(extnID, CRL_DISTR_POINTS)) { 329 sb.append("CRL Distribution Points Extension"); 330 } else if (Arrays.equals(extnID, CERTIFICATE_POLICIES)) { 331 sb.append("Certificate Policies Extension"); 332 } else if (Arrays.equals(extnID, POLICY_MAPPINGS)) { 333 sb.append("Policy Mappings Extension"); 334 } else if (Arrays.equals(extnID, AUTH_KEY_ID)) { 335 sb.append("Authority Key Identifier Extension"); 336 } else if (Arrays.equals(extnID, POLICY_CONSTRAINTS)) { 337 sb.append("Policy Constraints Extension"); 338 } else if (Arrays.equals(extnID, EXTENDED_KEY_USAGE)) { 339 sb.append("Extended Key Usage Extension"); 340 } else if (Arrays.equals(extnID, INHIBIT_ANY_POLICY)) { 341 sb.append("Inhibit Any-Policy Extension"); 342 } else if (Arrays.equals(extnID, AUTHORITY_INFO_ACCESS)) { 343 sb.append("Authority Information Access Extension"); 344 } else if (Arrays.equals(extnID, SUBJECT_INFO_ACCESS)) { 345 sb.append("Subject Information Access Extension"); 346 } else if (Arrays.equals(extnID, INVALIDITY_DATE)) { 347 sb.append("Invalidity Date Extension"); 348 } else if (Arrays.equals(extnID, CRL_NUMBER)) { 349 sb.append("CRL Number Extension"); 350 } else if (Arrays.equals(extnID, REASON_CODE)) { 351 sb.append("Reason Code Extension"); 352 } else { 353 sb.append("Unknown Extension"); 354 } 355 sb.append('\n').append(prefix).append("Unparsed Extension Value:\n"); 356 sb.append(Array.toString(extnValue, prefix)); 357 } 358 359 360 /** 361 * X.509 Extension encoder/decoder. 362 */ 363 public static final ASN1Sequence ASN1 = new ASN1Sequence(new ASN1Type[] { 364 ASN1Oid.getInstance(), 365 ASN1Boolean.getInstance(), 366 new ASN1OctetString() { 367 @Override public Object getDecodedObject(BerInputStream in) throws IOException { 368 // first - decoded octet string, 369 // second - raw encoding of octet string 370 return new Object[] 371 {super.getDecodedObject(in), in.getEncoded()}; 372 } 373 } 374 }) { 375 { 376 setDefault(Boolean.FALSE, 1); 377 } 378 379 @Override protected Object getDecodedObject(BerInputStream in) throws IOException { 380 Object[] values = (Object[]) in.content; 381 382 int[] oid = (int[]) values[0]; 383 byte[] extnValue = (byte[]) ((Object[]) values[2])[0]; 384 byte[] rawExtnValue = (byte[]) ((Object[]) values[2])[1]; 385 386 ExtensionValue decodedExtValue = null; 387 // decode Key Usage and Basic Constraints extension values 388 if (Arrays.equals(oid, KEY_USAGE)) { 389 decodedExtValue = new KeyUsage(extnValue); 390 } else if (Arrays.equals(oid, BASIC_CONSTRAINTS)) { 391 decodedExtValue = new BasicConstraints(extnValue); 392 } 393 394 return new Extension((int[]) values[0], (Boolean) values[1], 395 extnValue, rawExtnValue, in.getEncoded(), decodedExtValue); 396 } 397 398 @Override protected void getValues(Object object, Object[] values) { 399 Extension ext = (Extension) object; 400 values[0] = ext.extnID; 401 values[1] = (ext.critical) ? Boolean.TRUE : Boolean.FALSE; 402 values[2] = ext.extnValue; 403 } 404 }; 405} 406