X500Principal.java revision b2bb47be12d2f58ab0b5e7003f2efef515542d82
1/*
2 * Copyright (c) 2000, 2013, 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
26package javax.security.auth.x500;
27
28import java.io.*;
29import java.security.Principal;
30import java.util.Collections;
31import java.util.Map;
32import sun.security.x509.X500Name;
33import sun.security.util.*;
34
35/**
36 * <p> This class represents an X.500 {@code Principal}.
37 * {@code X500Principal}s are represented by distinguished names such as
38 * "CN=Duke, OU=JavaSoft, O=Sun Microsystems, C=US".
39 *
40 * <p> This class can be instantiated by using a string representation
41 * of the distinguished name, or by using the ASN.1 DER encoded byte
42 * representation of the distinguished name.  The current specification
43 * for the string representation of a distinguished name is defined in
44 * <a href="http://www.ietf.org/rfc/rfc2253.txt">RFC 2253: Lightweight
45 * Directory Access Protocol (v3): UTF-8 String Representation of
46 * Distinguished Names</a>. This class, however, accepts string formats from
47 * both RFC 2253 and <a href="http://www.ietf.org/rfc/rfc1779.txt">RFC 1779:
48 * A String Representation of Distinguished Names</a>, and also recognizes
49 * attribute type keywords whose OIDs (Object Identifiers) are defined in
50 * <a href="http://www.ietf.org/rfc/rfc3280.txt">RFC 3280: Internet X.509
51 * Public Key Infrastructure Certificate and CRL Profile</a>.
52 *
53 * <p> The string representation for this {@code X500Principal}
54 * can be obtained by calling the {@code getName} methods.
55 *
56 * <p> Note that the {@code getSubjectX500Principal} and
57 * {@code getIssuerX500Principal} methods of
58 * {@code X509Certificate} return X500Principals representing the
59 * issuer and subject fields of the certificate.
60 *
61 * @see java.security.cert.X509Certificate
62 * @since 1.4
63 */
64public final class X500Principal implements Principal, java.io.Serializable {
65
66    private static final long serialVersionUID = -500463348111345721L;
67
68    /**
69     * RFC 1779 String format of Distinguished Names.
70     */
71    public static final String RFC1779 = "RFC1779";
72    /**
73     * RFC 2253 String format of Distinguished Names.
74     */
75    public static final String RFC2253 = "RFC2253";
76    /**
77     * Canonical String format of Distinguished Names.
78     */
79    public static final String CANONICAL = "CANONICAL";
80
81    /**
82     * The X500Name representing this principal.
83     *
84     * NOTE: this field is reflectively accessed from within X500Name.
85     */
86    private transient X500Name thisX500Name;
87
88    /**
89     * Creates an X500Principal by wrapping an X500Name.
90     *
91     * NOTE: The constructor is package private. It is intended to be accessed
92     * using privileged reflection from classes in sun.security.*.
93     * Currently referenced from sun.security.x509.X500Name.asX500Principal().
94     */
95    X500Principal(X500Name x500Name) {
96        thisX500Name = x500Name;
97    }
98
99    /**
100     * Creates an {@code X500Principal} from a string representation of
101     * an X.500 distinguished name (ex:
102     * "CN=Duke, OU=JavaSoft, O=Sun Microsystems, C=US").
103     * The distinguished name must be specified using the grammar defined in
104     * RFC 1779 or RFC 2253 (either format is acceptable).
105     *
106     * <p>This constructor recognizes the attribute type keywords
107     * defined in RFC 1779 and RFC 2253
108     * (and listed in {@link #getName(String format) getName(String format)}),
109     * as well as the T, DNQ or DNQUALIFIER, SURNAME, GIVENNAME, INITIALS,
110     * GENERATION, EMAILADDRESS, and SERIALNUMBER keywords whose Object
111     * Identifiers (OIDs) are defined in RFC 3280 and its successor.
112     * Any other attribute type must be specified as an OID.
113     *
114     * <p>This implementation enforces a more restrictive OID syntax than
115     * defined in RFC 1779 and 2253. It uses the more correct syntax defined in
116     * <a href="http://www.ietf.org/rfc/rfc4512.txt">RFC 4512</a>, which
117     * specifies that OIDs contain at least 2 digits:
118     *
119     * <p>{@code numericoid = number 1*( DOT number ) }
120     *
121     * @param name an X.500 distinguished name in RFC 1779 or RFC 2253 format
122     * @exception NullPointerException if the {@code name}
123     *                  is {@code null}
124     * @exception IllegalArgumentException if the {@code name}
125     *                  is improperly specified
126     */
127    public X500Principal(String name) {
128        this(name, Collections.<String, String>emptyMap());
129    }
130
131    /**
132     * Creates an {@code X500Principal} from a string representation of
133     * an X.500 distinguished name (ex:
134     * "CN=Duke, OU=JavaSoft, O=Sun Microsystems, C=US").
135     * The distinguished name must be specified using the grammar defined in
136     * RFC 1779 or RFC 2253 (either format is acceptable).
137     *
138     * <p> This constructor recognizes the attribute type keywords specified
139     * in {@link #X500Principal(String)} and also recognizes additional
140     * keywords that have entries in the {@code keywordMap} parameter.
141     * Keyword entries in the keywordMap take precedence over the default
142     * keywords recognized by {@code X500Principal(String)}. Keywords
143     * MUST be specified in all upper-case, otherwise they will be ignored.
144     * Improperly specified keywords are ignored; however if a keyword in the
145     * name maps to an improperly specified Object Identifier (OID), an
146     * {@code IllegalArgumentException} is thrown. It is permissible to
147     * have 2 different keywords that map to the same OID.
148     *
149     * <p>This implementation enforces a more restrictive OID syntax than
150     * defined in RFC 1779 and 2253. It uses the more correct syntax defined in
151     * <a href="http://www.ietf.org/rfc/rfc4512.txt">RFC 4512</a>, which
152     * specifies that OIDs contain at least 2 digits:
153     *
154     * <p>{@code numericoid = number 1*( DOT number ) }
155     *
156     * @param name an X.500 distinguished name in RFC 1779 or RFC 2253 format
157     * @param keywordMap an attribute type keyword map, where each key is a
158     *   keyword String that maps to a corresponding object identifier in String
159     *   form (a sequence of nonnegative integers separated by periods). The map
160     *   may be empty but never {@code null}.
161     * @exception NullPointerException if {@code name} or
162     *   {@code keywordMap} is {@code null}
163     * @exception IllegalArgumentException if the {@code name} is
164     *   improperly specified or a keyword in the {@code name} maps to an
165     *   OID that is not in the correct form
166     * @since 1.6
167     */
168    public X500Principal(String name, Map<String, String> keywordMap) {
169        if (name == null) {
170            throw new NullPointerException
171                (sun.security.util.ResourcesMgr.getString
172                ("provided.null.name"));
173        }
174        if (keywordMap == null) {
175            throw new NullPointerException
176                (sun.security.util.ResourcesMgr.getString
177                ("provided.null.keyword.map"));
178        }
179
180        try {
181            thisX500Name = new X500Name(name, keywordMap);
182        } catch (Exception e) {
183            IllegalArgumentException iae = new IllegalArgumentException
184                        ("improperly specified input name: " + name);
185            iae.initCause(e);
186            throw iae;
187        }
188    }
189
190    /**
191     * Creates an {@code X500Principal} from a distinguished name in
192     * ASN.1 DER encoded form. The ASN.1 notation for this structure is as
193     * follows.
194     * <pre>{@code
195     * Name ::= CHOICE {
196     *   RDNSequence }
197     *
198     * RDNSequence ::= SEQUENCE OF RelativeDistinguishedName
199     *
200     * RelativeDistinguishedName ::=
201     *   SET SIZE (1 .. MAX) OF AttributeTypeAndValue
202     *
203     * AttributeTypeAndValue ::= SEQUENCE {
204     *   type     AttributeType,
205     *   value    AttributeValue }
206     *
207     * AttributeType ::= OBJECT IDENTIFIER
208     *
209     * AttributeValue ::= ANY DEFINED BY AttributeType
210     * ....
211     * DirectoryString ::= CHOICE {
212     *       teletexString           TeletexString (SIZE (1..MAX)),
213     *       printableString         PrintableString (SIZE (1..MAX)),
214     *       universalString         UniversalString (SIZE (1..MAX)),
215     *       utf8String              UTF8String (SIZE (1.. MAX)),
216     *       bmpString               BMPString (SIZE (1..MAX)) }
217     * }</pre>
218     *
219     * @param name a byte array containing the distinguished name in ASN.1
220     * DER encoded form
221     * @throws IllegalArgumentException if an encoding error occurs
222     *          (incorrect form for DN)
223     */
224    public X500Principal(byte[] name) {
225        try {
226            thisX500Name = new X500Name(name);
227        } catch (Exception e) {
228            IllegalArgumentException iae = new IllegalArgumentException
229                        ("improperly specified input name");
230            iae.initCause(e);
231            throw iae;
232        }
233    }
234
235    /**
236     * Creates an {@code X500Principal} from an {@code InputStream}
237     * containing the distinguished name in ASN.1 DER encoded form.
238     * The ASN.1 notation for this structure is supplied in the
239     * documentation for
240     * {@link #X500Principal(byte[] name) X500Principal(byte[] name)}.
241     *
242     * <p> The read position of the input stream is positioned
243     * to the next available byte after the encoded distinguished name.
244     *
245     * @param is an {@code InputStream} containing the distinguished
246     *          name in ASN.1 DER encoded form
247     *
248     * @exception NullPointerException if the {@code InputStream}
249     *          is {@code null}
250     * @exception IllegalArgumentException if an encoding error occurs
251     *          (incorrect form for DN)
252     */
253    public X500Principal(InputStream is) {
254        if (is == null) {
255            throw new NullPointerException("provided null input stream");
256        }
257
258        try {
259            if (is.markSupported())
260                is.mark(is.available() + 1);
261            DerValue der = new DerValue(is);
262            thisX500Name = new X500Name(der.data);
263        } catch (Exception e) {
264            if (is.markSupported()) {
265                try {
266                    is.reset();
267                } catch (IOException ioe) {
268                    IllegalArgumentException iae = new IllegalArgumentException
269                        ("improperly specified input stream " +
270                        ("and unable to reset input stream"));
271                    iae.initCause(e);
272                    throw iae;
273                }
274            }
275            IllegalArgumentException iae = new IllegalArgumentException
276                        ("improperly specified input stream");
277            iae.initCause(e);
278            throw iae;
279        }
280    }
281
282    /**
283     * Returns a string representation of the X.500 distinguished name using
284     * the format defined in RFC 2253.
285     *
286     * <p>This method is equivalent to calling
287     * {@code getName(X500Principal.RFC2253)}.
288     *
289     * @return the distinguished name of this {@code X500Principal}
290     */
291    public String getName() {
292        return getName(X500Principal.RFC2253);
293    }
294
295    /**
296     * Returns a string representation of the X.500 distinguished name
297     * using the specified format. Valid values for the format are
298     * "RFC1779", "RFC2253", and "CANONICAL" (case insensitive).
299     *
300     * <p> If "RFC1779" is specified as the format,
301     * this method emits the attribute type keywords defined in
302     * RFC 1779 (CN, L, ST, O, OU, C, STREET).
303     * Any other attribute type is emitted as an OID.
304     *
305     * <p> If "RFC2253" is specified as the format,
306     * this method emits the attribute type keywords defined in
307     * RFC 2253 (CN, L, ST, O, OU, C, STREET, DC, UID).
308     * Any other attribute type is emitted as an OID.
309     * Under a strict reading, RFC 2253 only specifies a UTF-8 string
310     * representation. The String returned by this method is the
311     * Unicode string achieved by decoding this UTF-8 representation.
312     *
313     * <p> If "CANONICAL" is specified as the format,
314     * this method returns an RFC 2253 conformant string representation
315     * with the following additional canonicalizations:
316     *
317     * <ol>
318     * <li> Leading zeros are removed from attribute types
319     *          that are encoded as dotted decimal OIDs
320     * <li> DirectoryString attribute values of type
321     *          PrintableString and UTF8String are not
322     *          output in hexadecimal format
323     * <li> DirectoryString attribute values of types
324     *          other than PrintableString and UTF8String
325     *          are output in hexadecimal format
326     * <li> Leading and trailing white space characters
327     *          are removed from non-hexadecimal attribute values
328     *          (unless the value consists entirely of white space characters)
329     * <li> Internal substrings of one or more white space characters are
330     *          converted to a single space in non-hexadecimal
331     *          attribute values
332     * <li> Relative Distinguished Names containing more than one
333     *          Attribute Value Assertion (AVA) are output in the
334     *          following order: an alphabetical ordering of AVAs
335     *          containing standard keywords, followed by a numeric
336     *          ordering of AVAs containing OID keywords.
337     * <li> The only characters in attribute values that are escaped are
338     *          those which section 2.4 of RFC 2253 states must be escaped
339     *          (they are escaped using a preceding backslash character)
340     * <li> The entire name is converted to upper case
341     *          using {@code String.toUpperCase(Locale.US)}
342     * <li> The entire name is converted to lower case
343     *          using {@code String.toLowerCase(Locale.US)}
344     * <li> The name is finally normalized using normalization form KD,
345     *          as described in the Unicode Standard and UAX #15
346     * </ol>
347     *
348     * <p> Additional standard formats may be introduced in the future.
349     *
350     * @param format the format to use
351     *
352     * @return a string representation of this {@code X500Principal}
353     *          using the specified format
354     * @throws IllegalArgumentException if the specified format is invalid
355     *          or null
356     */
357    public String getName(String format) {
358        if (format != null) {
359            if (format.equalsIgnoreCase(RFC1779)) {
360                return thisX500Name.getRFC1779Name();
361            } else if (format.equalsIgnoreCase(RFC2253)) {
362                return thisX500Name.getRFC2253Name();
363            } else if (format.equalsIgnoreCase(CANONICAL)) {
364                return thisX500Name.getRFC2253CanonicalName();
365            }
366        }
367        throw new IllegalArgumentException("invalid format specified");
368    }
369
370    /**
371     * Returns a string representation of the X.500 distinguished name
372     * using the specified format. Valid values for the format are
373     * "RFC1779" and "RFC2253" (case insensitive). "CANONICAL" is not
374     * permitted and an {@code IllegalArgumentException} will be thrown.
375     *
376     * <p>This method returns Strings in the format as specified in
377     * {@link #getName(String)} and also emits additional attribute type
378     * keywords for OIDs that have entries in the {@code oidMap}
379     * parameter. OID entries in the oidMap take precedence over the default
380     * OIDs recognized by {@code getName(String)}.
381     * Improperly specified OIDs are ignored; however if an OID
382     * in the name maps to an improperly specified keyword, an
383     * {@code IllegalArgumentException} is thrown.
384     *
385     * <p> Additional standard formats may be introduced in the future.
386     *
387     * <p> Warning: additional attribute type keywords may not be recognized
388     * by other implementations; therefore do not use this method if
389     * you are unsure if these keywords will be recognized by other
390     * implementations.
391     *
392     * @param format the format to use
393     * @param oidMap an OID map, where each key is an object identifier in
394     *  String form (a sequence of nonnegative integers separated by periods)
395     *  that maps to a corresponding attribute type keyword String.
396     *  The map may be empty but never {@code null}.
397     * @return a string representation of this {@code X500Principal}
398     *          using the specified format
399     * @throws IllegalArgumentException if the specified format is invalid,
400     *  null, or an OID in the name maps to an improperly specified keyword
401     * @throws NullPointerException if {@code oidMap} is {@code null}
402     * @since 1.6
403     */
404    public String getName(String format, Map<String, String> oidMap) {
405        if (oidMap == null) {
406            throw new NullPointerException
407                (sun.security.util.ResourcesMgr.getString
408                ("provided.null.OID.map"));
409        }
410        if (format != null) {
411            if (format.equalsIgnoreCase(RFC1779)) {
412                return thisX500Name.getRFC1779Name(oidMap);
413            } else if (format.equalsIgnoreCase(RFC2253)) {
414                return thisX500Name.getRFC2253Name(oidMap);
415            }
416        }
417        throw new IllegalArgumentException("invalid format specified");
418    }
419
420    /**
421     * Returns the distinguished name in ASN.1 DER encoded form. The ASN.1
422     * notation for this structure is supplied in the documentation for
423     * {@link #X500Principal(byte[] name) X500Principal(byte[] name)}.
424     *
425     * <p>Note that the byte array returned is cloned to protect against
426     * subsequent modifications.
427     *
428     * @return a byte array containing the distinguished name in ASN.1 DER
429     * encoded form
430     */
431    public byte[] getEncoded() {
432        try {
433            return thisX500Name.getEncoded();
434        } catch (IOException e) {
435            throw new RuntimeException("unable to get encoding", e);
436        }
437    }
438
439    /**
440     * Return a user-friendly string representation of this
441     * {@code X500Principal}.
442     *
443     * @return a string representation of this {@code X500Principal}
444     */
445    public String toString() {
446        return thisX500Name.toString();
447    }
448
449    /**
450     * Compares the specified {@code Object} with this
451     * {@code X500Principal} for equality.
452     *
453     * <p> Specifically, this method returns {@code true} if
454     * the {@code Object} <i>o</i> is an {@code X500Principal}
455     * and if the respective canonical string representations
456     * (obtained via the {@code getName(X500Principal.CANONICAL)} method)
457     * of this object and <i>o</i> are equal.
458     *
459     * <p> This implementation is compliant with the requirements of RFC 3280.
460     *
461     * @param o Object to be compared for equality with this
462     *          {@code X500Principal}
463     *
464     * @return {@code true} if the specified {@code Object} is equal
465     *          to this {@code X500Principal}, {@code false} otherwise
466     */
467    public boolean equals(Object o) {
468        if (this == o) {
469            return true;
470        }
471        if (o instanceof X500Principal == false) {
472            return false;
473        }
474        X500Principal other = (X500Principal)o;
475        return this.thisX500Name.equals(other.thisX500Name);
476    }
477
478    /**
479     * Return a hash code for this {@code X500Principal}.
480     *
481     * <p> The hash code is calculated via:
482     * {@code getName(X500Principal.CANONICAL).hashCode()}
483     *
484     * @return a hash code for this {@code X500Principal}
485     */
486    public int hashCode() {
487        return thisX500Name.hashCode();
488    }
489
490    /**
491     * Save the X500Principal object to a stream.
492     *
493     * @serialData this {@code X500Principal} is serialized
494     *          by writing out its DER-encoded form
495     *          (the value of {@code getEncoded} is serialized).
496     */
497    private void writeObject(java.io.ObjectOutputStream s)
498        throws IOException {
499        s.writeObject(thisX500Name.getEncodedInternal());
500    }
501
502    /**
503     * Reads this object from a stream (i.e., deserializes it).
504     */
505    private void readObject(java.io.ObjectInputStream s)
506        throws java.io.IOException,
507               java.io.NotActiveException,
508               ClassNotFoundException {
509
510        // re-create thisX500Name
511        thisX500Name = new X500Name((byte[])s.readObject());
512    }
513}
514