1 /*
   2  * Copyright (c) 1997, 2006, 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 represents the Subject Alternative Name Extension.
  36  *
  37  * This extension, if present, allows the subject to specify multiple
  38  * alternative names.
  39  *
  40  * <p>Extensions are represented as a sequence of the extension identifier
  41  * (Object Identifier), a boolean flag stating whether the extension is to
  42  * be treated as being critical and the extension value itself (this is again
  43  * a DER encoding of the extension value).
  44  * <p>
  45  * The ASN.1 syntax for this is:
  46  * <pre>
  47  * SubjectAltName ::= GeneralNames
  48  * GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName
  49  * </pre>
  50  * @author Amit Kapoor
  51  * @author Hemma Prafullchandra
  52  * @see Extension
  53  * @see CertAttrSet
  54  */
  55 public class SubjectAlternativeNameExtension extends Extension
  56 implements CertAttrSet<String> {
  57     /**
  58      * Identifier for this attribute, to be used with the
  59      * get, set, delete methods of Certificate, x509 type.
  60      */
  61     public static final String IDENT =
  62                          "x509.info.extensions.SubjectAlternativeName";
  63     /**
  64      * Attribute names.
  65      */
  66     public static final String NAME = "SubjectAlternativeName";
  67     public static final String SUBJECT_NAME = "subject_name";
  68 
  69     // private data members
  70     GeneralNames        names = null;
  71 
  72     // Encode this extension
  73     private void encodeThis() throws IOException {
  74         if (names == null || names.isEmpty()) {
  75             this.extensionValue = null;
  76             return;
  77         }
  78         DerOutputStream os = new DerOutputStream();
  79         names.encode(os);
  80         this.extensionValue = os.toByteArray();
  81     }
  82 
  83     /**
  84      * Create a SubjectAlternativeNameExtension with the passed GeneralNames.
  85      * The extension is marked non-critical.
  86      *
  87      * @param names the GeneralNames for the subject.
  88      * @exception IOException on error.
  89      */
  90     public SubjectAlternativeNameExtension(GeneralNames names)
  91     throws IOException {
  92         this(Boolean.FALSE, names);
  93     }
  94 
  95     /**
  96      * Create a SubjectAlternativeNameExtension with the specified
  97      * criticality and GeneralNames.
  98      *
  99      * @param critical true if the extension is to be treated as critical.
 100      * @param names the GeneralNames for the subject.
 101      * @exception IOException on error.
 102      */
 103     public SubjectAlternativeNameExtension(Boolean critical, GeneralNames names)
 104     throws IOException {
 105         this.names = names;
 106         this.extensionId = PKIXExtensions.SubjectAlternativeName_Id;
 107         this.critical = critical.booleanValue();
 108         encodeThis();
 109     }
 110 
 111     /**
 112      * Create a default SubjectAlternativeNameExtension. The extension
 113      * is marked non-critical.
 114      */
 115     public SubjectAlternativeNameExtension() {
 116         extensionId = PKIXExtensions.SubjectAlternativeName_Id;
 117         critical = false;
 118         names = new GeneralNames();
 119     }
 120 
 121     /**
 122      * Create the extension from the passed DER encoded value.
 123      *
 124      * @param critical true if the extension is to be treated as critical.
 125      * @param value an array of DER encoded bytes of the actual value.
 126      * @exception ClassCastException if value is not an array of bytes
 127      * @exception IOException on error.
 128      */
 129     public SubjectAlternativeNameExtension(Boolean critical, Object value)
 130     throws IOException {
 131         this.extensionId = PKIXExtensions.SubjectAlternativeName_Id;
 132         this.critical = critical.booleanValue();
 133 
 134         this.extensionValue = (byte[]) value;
 135         DerValue val = new DerValue(this.extensionValue);
 136         if (val.data == null) {
 137             names = new GeneralNames();
 138             return;
 139         }
 140 
 141         names = new GeneralNames(val);
 142     }
 143 
 144     /**
 145      * Returns a printable representation of the SubjectAlternativeName.
 146      */
 147     public String toString() {
 148 
 149         String result = super.toString() + "SubjectAlternativeName [\n";
 150         if(names == null) {
 151             result += "  null\n";
 152         } else {
 153             for(GeneralName name: names.names()) {
 154                 result += "  "+name+"\n";
 155             }
 156         }
 157         result += "]\n";
 158         return result;
 159     }
 160 
 161     /**
 162      * Write the extension to the OutputStream.
 163      *
 164      * @param out the OutputStream to write the extension to.
 165      * @exception IOException on encoding errors.
 166      */
 167     public void encode(OutputStream out) throws IOException {
 168         DerOutputStream tmp = new DerOutputStream();
 169         if (extensionValue == null) {
 170             extensionId = PKIXExtensions.SubjectAlternativeName_Id;
 171             critical = false;
 172             encodeThis();
 173         }
 174         super.encode(tmp);
 175         out.write(tmp.toByteArray());
 176     }
 177 
 178     /**
 179      * Set the attribute value.
 180      */
 181     public void set(String name, Object obj) throws IOException {
 182         if (name.equalsIgnoreCase(SUBJECT_NAME)) {
 183             if (!(obj instanceof GeneralNames)) {
 184               throw new IOException("Attribute value should be of " +
 185                                     "type GeneralNames.");
 186             }
 187             names = (GeneralNames)obj;
 188         } else {
 189           throw new IOException("Attribute name not recognized by " +
 190                         "CertAttrSet:SubjectAlternativeName.");
 191         }
 192         encodeThis();
 193     }
 194 
 195     /**
 196      * Get the attribute value.
 197      */
 198     public Object get(String name) throws IOException {
 199         if (name.equalsIgnoreCase(SUBJECT_NAME)) {
 200             return (names);
 201         } else {
 202           throw new IOException("Attribute name not recognized by " +
 203                         "CertAttrSet:SubjectAlternativeName.");
 204         }
 205     }
 206 
 207     /**
 208      * Delete the attribute value.
 209      */
 210     public void delete(String name) throws IOException {
 211         if (name.equalsIgnoreCase(SUBJECT_NAME)) {
 212             names = null;
 213         } else {
 214           throw new IOException("Attribute name not recognized by " +
 215                         "CertAttrSet:SubjectAlternativeName.");
 216         }
 217         encodeThis();
 218     }
 219 
 220     /**
 221      * Return an enumeration of names of attributes existing within this
 222      * attribute.
 223      */
 224     public Enumeration<String> getElements() {
 225         AttributeNameEnumeration elements = new AttributeNameEnumeration();
 226         elements.addElement(SUBJECT_NAME);
 227 
 228         return (elements.elements());
 229     }
 230 
 231     /**
 232      * Return the name of this attribute.
 233      */
 234     public String getName() {
 235         return (NAME);
 236     }
 237 }