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