• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 package javax.security.auth.x500;
19 
20 import java.io.IOException;
21 import java.io.InputStream;
22 import java.io.ObjectInputStream;
23 import java.io.ObjectOutputStream;
24 import java.io.Serializable;
25 import java.security.Principal;
26 import java.util.Map;
27 import org.apache.harmony.security.x501.Name;
28 
29 /**
30  * Represents an X.500 principal, which holds the distinguished name of some
31  * network entity. An example of a distinguished name is {@code "O=SomeOrg,
32  * OU=SomeOrgUnit, C=US"}. The class can be instantiated from a byte representation
33  * of an object identifier (OID), an ASN.1 DER-encoded version, or a simple
34  * string holding the distinguished name. The representations must follow either
35  * RFC 2253, RFC 1779, or RFC2459.
36  */
37 public final class X500Principal implements Serializable, Principal {
38 
39     private static final long serialVersionUID = -500463348111345721L;
40 
41     /**
42      * Defines a constant for the canonical string format of distinguished
43      * names.
44      */
45     public static final String CANONICAL = "CANONICAL";
46 
47     /**
48      * Defines a constant for the RFC 1779 string format of distinguished
49      * names.
50      */
51     public static final String RFC1779 = "RFC1779";
52 
53     /**
54      * Defines a constant for the RFC 2253 string format of distinguished
55      * names.
56      */
57     public static final String RFC2253 = "RFC2253";
58 
59     //Distinguished Name
60     private transient Name dn;
61 
62     /**
63      * Creates a new X500Principal from a given ASN.1 DER encoding of a
64      * distinguished name.
65      *
66      * @param name
67      *            the ASN.1 DER-encoded distinguished name
68      *
69      * @throws IllegalArgumentException
70      *             if the ASN.1 DER-encoded distinguished name is incorrect
71      */
X500Principal(byte[] name)72     public X500Principal(byte[] name) {
73         if (name == null) {
74             throw new IllegalArgumentException("Name cannot be null");
75         }
76         try {
77             // FIXME dn = new Name(name);
78             dn = (Name) Name.ASN1.decode(name);
79         } catch (IOException e) {
80             throw incorrectInputEncoding(e);
81         }
82     }
83 
84     /**
85      * Creates a new X500Principal from a given ASN.1 DER encoding of a
86      * distinguished name.
87      *
88      * @param in
89      *            an {@code InputStream} holding the ASN.1 DER-encoded
90      *            distinguished name
91      *
92      * @throws IllegalArgumentException
93      *             if the ASN.1 DER-encoded distinguished name is incorrect
94      */
X500Principal(InputStream in)95     public X500Principal(InputStream in) {
96         if (in == null) {
97             throw new NullPointerException("in == null");
98         }
99         try {
100             // FIXME dn = new Name(is);
101             dn = (Name) Name.ASN1.decode(in);
102         } catch (IOException e) {
103             throw incorrectInputEncoding(e);
104         }
105     }
106 
incorrectInputEncoding(IOException e)107     private IllegalArgumentException incorrectInputEncoding(IOException e) {
108         IllegalArgumentException iae = new IllegalArgumentException("Incorrect input encoding");
109         iae.initCause(e);
110         throw iae;
111     }
112 
113     /**
114      * Creates a new X500Principal from a string representation of a
115      * distinguished name.
116      *
117      * @param name
118      *            the string representation of the distinguished name
119      *
120      * @throws IllegalArgumentException
121      *             if the string representation of the distinguished name is
122      *             incorrect
123      */
X500Principal(String name)124     public X500Principal(String name) {
125         if (name == null) {
126             throw new NullPointerException("name == null");
127         }
128         try {
129             dn = new Name(name);
130         } catch (IOException e) {
131             throw incorrectInputName(e, name);
132         }
133     }
134 
X500Principal(String name, Map<String,String> keywordMap)135     public X500Principal(String name, Map<String,String> keywordMap){
136         if (name == null) {
137             throw new NullPointerException("name == null");
138         }
139         try {
140             dn = new Name(substituteNameFromMap(name, keywordMap));
141         } catch (IOException e) {
142             throw incorrectInputName(e, name);
143         }
144     }
145 
incorrectInputName(IOException e, String name)146     private IllegalArgumentException incorrectInputName(IOException e, String name) {
147         IllegalArgumentException iae = new IllegalArgumentException("Incorrect input name:" + name);
148         iae.initCause(e);
149         throw iae;
150     }
151 
152     private transient String canonicalName;
getCanonicalName()153     private synchronized String getCanonicalName() {
154         if (canonicalName == null) {
155             canonicalName = dn.getName(CANONICAL);
156         }
157         return canonicalName;
158     }
159 
160     @Override
equals(Object o)161     public boolean equals(Object o) {
162         if (this == o) {
163             return true;
164         }
165         if (o == null || this.getClass() != o.getClass()) {
166             return false;
167         }
168         X500Principal principal = (X500Principal) o;
169         return getCanonicalName().equals(principal.getCanonicalName());
170     }
171 
172     /**
173      * Returns an ASN.1 DER-encoded representation of the distinguished name
174      * contained in this X.500 principal.
175      *
176      * @return the ASN.1 DER-encoded representation
177      */
getEncoded()178     public byte[] getEncoded() {
179         byte[] src = dn.getEncoded();
180         byte[] dst = new byte[src.length];
181         System.arraycopy(src, 0, dst, 0, dst.length);
182         return dst;
183     }
184 
185     /**
186      * Returns a human-readable string representation of the distinguished name
187      * contained in this X.500 principal.
188      *
189      * @return the string representation
190      */
getName()191     public String getName() {
192         return dn.getName(RFC2253);
193     }
194 
195     /**
196      * Returns a string representation of the distinguished name contained in
197      * this X.500 principal. The format of the representation can be chosen.
198      * Valid arguments are {@link #RFC1779}, {@link #RFC2253}, and
199      * {@link #CANONICAL}. The representations are specified in RFC 1779 and RFC
200      * 2253, respectively. The canonical form is based on RFC 2253, but adds
201      * some canonicalizing operations like removing leading and trailing
202      * whitespace, lower-casing the whole name, and bringing it into a
203      * normalized Unicode representation.
204      *
205      * @param format
206      *            the name of the format to use for the representation
207      *
208      * @return the string representation
209      *
210      * @throws IllegalArgumentException
211      *             if the {@code format} argument is not one of the three
212      *             mentioned above
213      */
getName(String format)214     public String getName(String format) {
215         if (CANONICAL.equals(format)) {
216             return getCanonicalName();
217         }
218 
219         return dn.getName(format);
220     }
221 
getName(String format, Map<String, String> oidMap)222     public String getName(String format, Map<String, String> oidMap) {
223         String rfc1779Name = dn.getName(RFC1779);
224         String rfc2253Name = dn.getName(RFC2253);
225 
226         if (format.equalsIgnoreCase("RFC1779")) {
227             StringBuilder resultName = new StringBuilder(rfc1779Name);
228             int fromIndex = resultName.length();
229             int equalIndex = -1;
230             while (-1 != (equalIndex = resultName.lastIndexOf("=", fromIndex))) {
231                 int commaIndex = resultName.lastIndexOf(",", equalIndex);
232                 String subName = resultName.substring(commaIndex + 1,
233                         equalIndex).trim();
234                 if (subName.length() > 4
235                         && subName.substring(0, 4).equals("OID.")) {
236                     String subSubName = subName.substring(4);
237                     if (oidMap.containsKey(subSubName)) {
238                         String replaceName = oidMap.get(subSubName);
239                         if(commaIndex > 0) replaceName = " " + replaceName;
240                         resultName.replace(commaIndex + 1, equalIndex, replaceName);
241                     }
242                 }
243                 fromIndex = commaIndex;
244             }
245             return resultName.toString();
246         } else if (format.equalsIgnoreCase("RFC2253")) {
247             StringBuilder resultName = new StringBuilder(rfc2253Name);
248             StringBuilder subsidyName = new StringBuilder(rfc1779Name);
249 
250             int fromIndex = resultName.length();
251             int subsidyFromIndex = subsidyName.length();
252             int equalIndex = -1;
253             int subsidyEqualIndex = -1;
254             while (-1 != (equalIndex = resultName.lastIndexOf("=", fromIndex))) {
255                 subsidyEqualIndex = subsidyName.lastIndexOf("=",
256                         subsidyFromIndex);
257                 int commaIndex = resultName.lastIndexOf(",", equalIndex);
258                 String subName = resultName.substring(commaIndex + 1,
259                         equalIndex).trim();
260                 if (oidMap.containsKey(subName)) {
261                     int subOrignalEndIndex = resultName
262                             .indexOf(",", equalIndex);
263                     if (subOrignalEndIndex == -1)
264                         subOrignalEndIndex = resultName.length();
265                     int subGoalEndIndex = subsidyName.indexOf(",",
266                             subsidyEqualIndex);
267                     if (subGoalEndIndex == -1)
268                         subGoalEndIndex = subsidyName.length();
269                     resultName.replace(equalIndex + 1, subOrignalEndIndex,
270                             subsidyName.substring(subsidyEqualIndex + 1,
271                                     subGoalEndIndex));
272                     resultName.replace(commaIndex + 1, equalIndex, oidMap
273                             .get(subName));
274                 }
275                 fromIndex = commaIndex;
276                 subsidyFromIndex = subsidyEqualIndex - 1;
277             }
278             return resultName.toString();
279         } else {
280             throw new IllegalArgumentException("invalid format specified: " + format);
281         }
282     }
283 
284     @Override
hashCode()285     public int hashCode() {
286         return getCanonicalName().hashCode();
287     }
288 
289     @Override
toString()290     public String toString() {
291         return dn.getName(RFC1779);
292     }
293 
writeObject(ObjectOutputStream out)294     private void writeObject(ObjectOutputStream out) throws IOException {
295         out.writeObject(dn.getEncoded());
296     }
297 
readObject(ObjectInputStream in)298     private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
299         dn = (Name) Name.ASN1.decode((byte[]) in.readObject());
300     }
301 
substituteNameFromMap(String name, Map<String, String> keywordMap)302     private String substituteNameFromMap(String name, Map<String, String> keywordMap) {
303         StringBuilder sbName = new StringBuilder(name);
304         int fromIndex = sbName.length();
305         int equalIndex;
306         while (-1 != (equalIndex = sbName.lastIndexOf("=", fromIndex))) {
307             int commaIndex = sbName.lastIndexOf(",", equalIndex);
308             String subName = sbName.substring(commaIndex + 1, equalIndex).trim();
309             if (keywordMap.containsKey(subName)) {
310                 sbName.replace(commaIndex + 1, equalIndex, keywordMap.get(subName));
311             }
312             fromIndex = commaIndex;
313         }
314         return sbName.toString();
315     }
316 }
317