1 /* 2 * Copyright (c) 1997, 2009, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package sun.security.x509; 27 28 import java.io.IOException; 29 import java.io.OutputStream; 30 import java.util.Enumeration; 31 32 import sun.security.util.*; 33 34 /** 35 * This class represents the Authority Key Identifier Extension. 36 * 37 * <p>The authority key identifier extension provides a means of 38 * identifying the particular public key used to sign a certificate. 39 * This extension would be used where an issuer has multiple signing 40 * keys (either due to multiple concurrent key pairs or due to 41 * changeover). 42 * <p> 43 * The ASN.1 syntax for this is: 44 * <pre> 45 * AuthorityKeyIdentifier ::= SEQUENCE { 46 * keyIdentifier [0] KeyIdentifier OPTIONAL, 47 * authorityCertIssuer [1] GeneralNames OPTIONAL, 48 * authorityCertSerialNumber [2] CertificateSerialNumber OPTIONAL 49 * } 50 * KeyIdentifier ::= OCTET STRING 51 * </pre> 52 * @author Amit Kapoor 53 * @author Hemma Prafullchandra 54 * @see Extension 55 * @see CertAttrSet 56 */ 57 public class AuthorityKeyIdentifierExtension extends Extension 58 implements CertAttrSet<String> { 59 /** 60 * Identifier for this attribute, to be used with the 61 * get, set, delete methods of Certificate, x509 type. 62 */ 63 public static final String IDENT = 64 "x509.info.extensions.AuthorityKeyIdentifier"; 65 /** 66 * Attribute names. 67 */ 68 public static final String NAME = "AuthorityKeyIdentifier"; 69 public static final String KEY_ID = "key_id"; 70 public static final String AUTH_NAME = "auth_name"; 71 public static final String SERIAL_NUMBER = "serial_number"; 72 73 // Private data members 74 private static final byte TAG_ID = 0; 75 private static final byte TAG_NAMES = 1; 76 private static final byte TAG_SERIAL_NUM = 2; 77 78 private KeyIdentifier id = null; 79 private GeneralNames names = null; 80 private SerialNumber serialNum = null; 81 82 // Encode only the extension value encodeThis()83 private void encodeThis() throws IOException { 84 if (id == null && names == null && serialNum == null) { 85 this.extensionValue = null; 86 return; 87 } 88 DerOutputStream seq = new DerOutputStream(); 89 DerOutputStream tmp = new DerOutputStream(); 90 if (id != null) { 91 DerOutputStream tmp1 = new DerOutputStream(); 92 id.encode(tmp1); 93 tmp.writeImplicit(DerValue.createTag(DerValue.TAG_CONTEXT, 94 false, TAG_ID), tmp1); 95 } 96 try { 97 if (names != null) { 98 DerOutputStream tmp1 = new DerOutputStream(); 99 names.encode(tmp1); 100 tmp.writeImplicit(DerValue.createTag(DerValue.TAG_CONTEXT, 101 true, TAG_NAMES), tmp1); 102 } 103 } catch (Exception e) { 104 throw new IOException(e.toString()); 105 } 106 if (serialNum != null) { 107 DerOutputStream tmp1 = new DerOutputStream(); 108 serialNum.encode(tmp1); 109 tmp.writeImplicit(DerValue.createTag(DerValue.TAG_CONTEXT, 110 false, TAG_SERIAL_NUM), tmp1); 111 } 112 seq.write(DerValue.tag_Sequence, tmp); 113 this.extensionValue = seq.toByteArray(); 114 } 115 116 /** 117 * The default constructor for this extension. Null parameters make 118 * the element optional (not present). 119 * 120 * @param id the KeyIdentifier associated with this extension. 121 * @param names the GeneralNames associated with this extension 122 * @param serialNum the CertificateSerialNumber associated with 123 * this extension. 124 * @exception IOException on error. 125 */ AuthorityKeyIdentifierExtension(KeyIdentifier kid, GeneralNames name, SerialNumber sn)126 public AuthorityKeyIdentifierExtension(KeyIdentifier kid, GeneralNames name, 127 SerialNumber sn) 128 throws IOException { 129 this.id = kid; 130 this.names = name; 131 this.serialNum = sn; 132 133 this.extensionId = PKIXExtensions.AuthorityKey_Id; 134 this.critical = false; 135 encodeThis(); 136 } 137 138 /** 139 * Create the extension from the passed DER encoded value of the same. 140 * 141 * @param critical true if the extension is to be treated as critical. 142 * @param value an array of DER encoded bytes of the actual value. 143 * @exception ClassCastException if value is not an array of bytes 144 * @exception IOException on error. 145 */ AuthorityKeyIdentifierExtension(Boolean critical, Object value)146 public AuthorityKeyIdentifierExtension(Boolean critical, Object value) 147 throws IOException { 148 this.extensionId = PKIXExtensions.AuthorityKey_Id; 149 this.critical = critical.booleanValue(); 150 151 this.extensionValue = (byte[]) value; 152 DerValue val = new DerValue(this.extensionValue); 153 if (val.tag != DerValue.tag_Sequence) { 154 throw new IOException("Invalid encoding for " + 155 "AuthorityKeyIdentifierExtension."); 156 } 157 158 // Note that all the fields in AuthorityKeyIdentifier are defined as 159 // being OPTIONAL, i.e., there could be an empty SEQUENCE, resulting 160 // in val.data being null. 161 while ((val.data != null) && (val.data.available() != 0)) { 162 DerValue opt = val.data.getDerValue(); 163 164 // NB. this is always encoded with the IMPLICIT tag 165 // The checks only make sense if we assume implicit tagging, 166 // with explicit tagging the form is always constructed. 167 if (opt.isContextSpecific(TAG_ID) && !opt.isConstructed()) { 168 if (id != null) 169 throw new IOException("Duplicate KeyIdentifier in " + 170 "AuthorityKeyIdentifier."); 171 opt.resetTag(DerValue.tag_OctetString); 172 id = new KeyIdentifier(opt); 173 174 } else if (opt.isContextSpecific(TAG_NAMES) && 175 opt.isConstructed()) { 176 if (names != null) 177 throw new IOException("Duplicate GeneralNames in " + 178 "AuthorityKeyIdentifier."); 179 opt.resetTag(DerValue.tag_Sequence); 180 names = new GeneralNames(opt); 181 182 } else if (opt.isContextSpecific(TAG_SERIAL_NUM) && 183 !opt.isConstructed()) { 184 if (serialNum != null) 185 throw new IOException("Duplicate SerialNumber in " + 186 "AuthorityKeyIdentifier."); 187 opt.resetTag(DerValue.tag_Integer); 188 serialNum = new SerialNumber(opt); 189 } else 190 throw new IOException("Invalid encoding of " + 191 "AuthorityKeyIdentifierExtension."); 192 } 193 } 194 195 /** 196 * Return the object as a string. 197 */ toString()198 public String toString() { 199 String s = super.toString() + "AuthorityKeyIdentifier [\n"; 200 if (id != null) { 201 s += id.toString(); // id already has a newline 202 } 203 if (names != null) { 204 s += names.toString() + "\n"; 205 } 206 if (serialNum != null) { 207 s += serialNum.toString() + "\n"; 208 } 209 return (s + "]\n"); 210 } 211 212 /** 213 * Write the extension to the OutputStream. 214 * 215 * @param out the OutputStream to write the extension to. 216 * @exception IOException on error. 217 */ encode(OutputStream out)218 public void encode(OutputStream out) throws IOException { 219 DerOutputStream tmp = new DerOutputStream(); 220 if (this.extensionValue == null) { 221 extensionId = PKIXExtensions.AuthorityKey_Id; 222 critical = false; 223 encodeThis(); 224 } 225 super.encode(tmp); 226 out.write(tmp.toByteArray()); 227 } 228 229 /** 230 * Set the attribute value. 231 */ set(String name, Object obj)232 public void set(String name, Object obj) throws IOException { 233 if (name.equalsIgnoreCase(KEY_ID)) { 234 if (!(obj instanceof KeyIdentifier)) { 235 throw new IOException("Attribute value should be of " + 236 "type KeyIdentifier."); 237 } 238 id = (KeyIdentifier)obj; 239 } else if (name.equalsIgnoreCase(AUTH_NAME)) { 240 if (!(obj instanceof GeneralNames)) { 241 throw new IOException("Attribute value should be of " + 242 "type GeneralNames."); 243 } 244 names = (GeneralNames)obj; 245 } else if (name.equalsIgnoreCase(SERIAL_NUMBER)) { 246 if (!(obj instanceof SerialNumber)) { 247 throw new IOException("Attribute value should be of " + 248 "type SerialNumber."); 249 } 250 serialNum = (SerialNumber)obj; 251 } else { 252 throw new IOException("Attribute name not recognized by " + 253 "CertAttrSet:AuthorityKeyIdentifier."); 254 } 255 encodeThis(); 256 } 257 258 /** 259 * Get the attribute value. 260 */ get(String name)261 public Object get(String name) throws IOException { 262 if (name.equalsIgnoreCase(KEY_ID)) { 263 return (id); 264 } else if (name.equalsIgnoreCase(AUTH_NAME)) { 265 return (names); 266 } else if (name.equalsIgnoreCase(SERIAL_NUMBER)) { 267 return (serialNum); 268 } else { 269 throw new IOException("Attribute name not recognized by " + 270 "CertAttrSet:AuthorityKeyIdentifier."); 271 } 272 } 273 274 /** 275 * Delete the attribute value. 276 */ delete(String name)277 public void delete(String name) throws IOException { 278 if (name.equalsIgnoreCase(KEY_ID)) { 279 id = null; 280 } else if (name.equalsIgnoreCase(AUTH_NAME)) { 281 names = null; 282 } else if (name.equalsIgnoreCase(SERIAL_NUMBER)) { 283 serialNum = null; 284 } else { 285 throw new IOException("Attribute name not recognized by " + 286 "CertAttrSet:AuthorityKeyIdentifier."); 287 } 288 encodeThis(); 289 } 290 291 /** 292 * Return an enumeration of names of attributes existing within this 293 * attribute. 294 */ getElements()295 public Enumeration<String> getElements() { 296 AttributeNameEnumeration elements = new AttributeNameEnumeration(); 297 elements.addElement(KEY_ID); 298 elements.addElement(AUTH_NAME); 299 elements.addElement(SERIAL_NUMBER); 300 301 return (elements.elements()); 302 } 303 304 /** 305 * Return the name of this attribute. 306 */ getName()307 public String getName() { 308 return (NAME); 309 } 310 311 /** 312 * Return the encoded key identifier, or null if not specified. 313 */ getEncodedKeyIdentifier()314 public byte[] getEncodedKeyIdentifier() throws IOException { 315 if (id != null) { 316 DerOutputStream derOut = new DerOutputStream(); 317 id.encode(derOut); 318 return derOut.toByteArray(); 319 } 320 return null; 321 } 322 } 323