1 /*
2  * Copyright (c) 1997, 2011, 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
encodeThis()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      */
SubjectAlternativeNameExtension(GeneralNames names)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      */
SubjectAlternativeNameExtension(Boolean critical, GeneralNames names)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      */
SubjectAlternativeNameExtension()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      */
SubjectAlternativeNameExtension(Boolean critical, Object value)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      */
toString()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      */
encode(OutputStream out)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      */
set(String name, Object obj)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      */
get(String name)198     public GeneralNames 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      */
delete(String name)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      */
getElements()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      */
getName()234     public String getName() {
235         return (NAME);
236     }
237 }
238