Path: blob/master/src/java.base/share/classes/javax/security/auth/x500/X500Principal.java
41161 views
/*1* Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package javax.security.auth.x500;2627import java.io.*;28import java.security.Principal;29import java.util.Collections;30import java.util.Map;31import sun.security.x509.X500Name;32import sun.security.util.*;3334/**35* <p> This class represents an X.500 {@code Principal}.36* {@code X500Principal}s are represented by distinguished names such as37* "CN=Duke, OU=JavaSoft, O=Sun Microsystems, C=US".38*39* <p> This class can be instantiated by using a string representation40* of the distinguished name, or by using the ASN.1 DER encoded byte41* representation of the distinguished name. The current specification42* for the string representation of a distinguished name is defined in43* <a href="http://tools.ietf.org/html/rfc2253">RFC 2253: Lightweight44* Directory Access Protocol (v3): UTF-8 String Representation of45* Distinguished Names</a>. This class, however, accepts string formats from46* both RFC 2253 and <a href="http://tools.ietf.org/html/rfc1779">RFC 1779:47* A String Representation of Distinguished Names</a>, and also recognizes48* attribute type keywords whose OIDs (Object Identifiers) are defined in49* <a href="http://tools.ietf.org/html/rfc5280">RFC 5280: Internet X.50950* Public Key Infrastructure Certificate and CRL Profile</a>.51*52* <p> The string representation for this {@code X500Principal}53* can be obtained by calling the {@code getName} methods.54*55* <p> Note that the {@code getSubjectX500Principal} and56* {@code getIssuerX500Principal} methods of57* {@code X509Certificate} return X500Principals representing the58* issuer and subject fields of the certificate.59*60* @see java.security.cert.X509Certificate61* @since 1.462*/63public final class X500Principal implements Principal, java.io.Serializable {6465@java.io.Serial66private static final long serialVersionUID = -500463348111345721L;6768/**69* RFC 1779 String format of Distinguished Names.70*/71public static final String RFC1779 = "RFC1779";72/**73* RFC 2253 String format of Distinguished Names.74*/75public static final String RFC2253 = "RFC2253";76/**77* Canonical String format of Distinguished Names.78*/79public static final String CANONICAL = "CANONICAL";8081/**82* The X500Name representing this principal.83*84* NOTE: this field is reflectively accessed from within X500Name.85*/86private transient X500Name thisX500Name;8788/**89* Creates an X500Principal by wrapping an X500Name.90*91* NOTE: The constructor is package private. It is intended to be accessed92* using privileged reflection from classes in sun.security.*.93* Currently referenced from sun.security.x509.X500Name.asX500Principal().94*/95X500Principal(X500Name x500Name) {96thisX500Name = x500Name;97}9899/**100* Creates an {@code X500Principal} from a string representation of101* 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 in104* RFC 1779 or RFC 2253 (either format is acceptable).105*106* <p>This constructor recognizes the attribute type keywords107* defined in RFC 1779 and RFC 2253108* (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 Object111* Identifiers (OIDs) are defined in RFC 5280.112* Any other attribute type must be specified as an OID.113*114* <p>This implementation enforces a more restrictive OID syntax than115* defined in RFC 1779 and 2253. It uses the more correct syntax defined in116* <a href="http://www.ietf.org/rfc/rfc4512.txt">RFC 4512</a>, which117* 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 format122* @exception NullPointerException if the {@code name}123* is {@code null}124* @exception IllegalArgumentException if the {@code name}125* is improperly specified126*/127public X500Principal(String name) {128this(name, Collections.<String, String>emptyMap());129}130131/**132* Creates an {@code X500Principal} from a string representation of133* 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 in136* RFC 1779 or RFC 2253 (either format is acceptable).137*138* <p> This constructor recognizes the attribute type keywords specified139* in {@link #X500Principal(String)} and also recognizes additional140* keywords that have entries in the {@code keywordMap} parameter.141* Keyword entries in the keywordMap take precedence over the default142* keywords recognized by {@code X500Principal(String)}. Keywords143* MUST be specified in all upper-case, otherwise they will be ignored.144* Improperly specified keywords are ignored; however if a keyword in the145* name maps to an improperly specified Object Identifier (OID), an146* {@code IllegalArgumentException} is thrown. It is permissible to147* have 2 different keywords that map to the same OID.148*149* <p>This implementation enforces a more restrictive OID syntax than150* defined in RFC 1779 and 2253. It uses the more correct syntax defined in151* <a href="http://www.ietf.org/rfc/rfc4512.txt">RFC 4512</a>, which152* 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 format157* @param keywordMap an attribute type keyword map, where each key is a158* keyword String that maps to a corresponding object identifier in String159* form (a sequence of nonnegative integers separated by periods). The map160* may be empty but never {@code null}.161* @exception NullPointerException if {@code name} or162* {@code keywordMap} is {@code null}163* @exception IllegalArgumentException if the {@code name} is164* improperly specified or a keyword in the {@code name} maps to an165* OID that is not in the correct form166* @since 1.6167*/168public X500Principal(String name, Map<String, String> keywordMap) {169if (name == null) {170throw new NullPointerException171(sun.security.util.ResourcesMgr.getString172("provided.null.name"));173}174if (keywordMap == null) {175throw new NullPointerException176(sun.security.util.ResourcesMgr.getString177("provided.null.keyword.map"));178}179180try {181thisX500Name = new X500Name(name, keywordMap);182} catch (Exception e) {183IllegalArgumentException iae = new IllegalArgumentException184("improperly specified input name: " + name);185iae.initCause(e);186throw iae;187}188}189190/**191* Creates an {@code X500Principal} from a distinguished name in192* ASN.1 DER encoded form. The ASN.1 notation for this structure is as193* follows.194* <pre>{@code195* Name ::= CHOICE {196* RDNSequence }197*198* RDNSequence ::= SEQUENCE OF RelativeDistinguishedName199*200* RelativeDistinguishedName ::=201* SET SIZE (1 .. MAX) OF AttributeTypeAndValue202*203* AttributeTypeAndValue ::= SEQUENCE {204* type AttributeType,205* value AttributeValue }206*207* AttributeType ::= OBJECT IDENTIFIER208*209* AttributeValue ::= ANY DEFINED BY AttributeType210* ....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.1220* DER encoded form221* @throws IllegalArgumentException if an encoding error occurs222* (incorrect form for DN)223*/224public X500Principal(byte[] name) {225try {226thisX500Name = new X500Name(name);227} catch (Exception e) {228IllegalArgumentException iae = new IllegalArgumentException229("improperly specified input name");230iae.initCause(e);231throw iae;232}233}234235/**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 the239* documentation for240* {@link #X500Principal(byte[] name) X500Principal(byte[] name)}.241*242* <p> The read position of the input stream is positioned243* to the next available byte after the encoded distinguished name.244*245* @param is an {@code InputStream} containing the distinguished246* name in ASN.1 DER encoded form247*248* @exception NullPointerException if the {@code InputStream}249* is {@code null}250* @exception IllegalArgumentException if an encoding error occurs251* (incorrect form for DN)252*/253public X500Principal(InputStream is) {254if (is == null) {255throw new NullPointerException("provided null input stream");256}257258try {259if (is.markSupported())260is.mark(is.available() + 1);261DerValue der = new DerValue(is);262thisX500Name = new X500Name(der.data);263} catch (Exception e) {264if (is.markSupported()) {265try {266is.reset();267} catch (IOException ioe) {268IllegalArgumentException iae = new IllegalArgumentException269("improperly specified input stream " +270("and unable to reset input stream"));271iae.initCause(e);272throw iae;273}274}275IllegalArgumentException iae = new IllegalArgumentException276("improperly specified input stream");277iae.initCause(e);278throw iae;279}280}281282/**283* Returns a string representation of the X.500 distinguished name using284* the format defined in RFC 2253.285*286* <p>This method is equivalent to calling287* {@code getName(X500Principal.RFC2253)}.288*289* @return the distinguished name of this {@code X500Principal}290*/291public String getName() {292return getName(X500Principal.RFC2253);293}294295/**296* Returns a string representation of the X.500 distinguished name297* using the specified format. Valid values for the format are298* "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 in302* 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 in307* 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 string310* representation. The String returned by this method is the311* 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 representation315* with the following additional canonicalizations:316*317* <ol>318* <li> Leading zeros are removed from attribute types319* that are encoded as dotted decimal OIDs320* <li> DirectoryString attribute values of type321* PrintableString and UTF8String are not322* output in hexadecimal format323* <li> DirectoryString attribute values of types324* other than PrintableString and UTF8String325* are output in hexadecimal format326* <li> Leading and trailing white space characters327* are removed from non-hexadecimal attribute values328* (unless the value consists entirely of white space characters)329* <li> Internal substrings of one or more white space characters are330* converted to a single space in non-hexadecimal331* attribute values332* <li> Relative Distinguished Names containing more than one333* Attribute Value Assertion (AVA) are output in the334* following order: an alphabetical ordering of AVAs335* containing standard keywords, followed by a numeric336* ordering of AVAs containing OID keywords.337* <li> The only characters in attribute values that are escaped are338* those which section 2.4 of RFC 2253 states must be escaped339* (they are escaped using a preceding backslash character)340* <li> The entire name is converted to upper case341* using {@code String.toUpperCase(Locale.US)}342* <li> The entire name is converted to lower case343* 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 #15346* </ol>347*348* <p> Additional standard formats may be introduced in the future.349*350* @param format the format to use351*352* @return a string representation of this {@code X500Principal}353* using the specified format354* @throws IllegalArgumentException if the specified format is invalid355* or null356*/357public String getName(String format) {358if (format != null) {359if (format.equalsIgnoreCase(RFC1779)) {360return thisX500Name.getRFC1779Name();361} else if (format.equalsIgnoreCase(RFC2253)) {362return thisX500Name.getRFC2253Name();363} else if (format.equalsIgnoreCase(CANONICAL)) {364return thisX500Name.getRFC2253CanonicalName();365}366}367throw new IllegalArgumentException("invalid format specified");368}369370/**371* Returns a string representation of the X.500 distinguished name372* using the specified format. Valid values for the format are373* "RFC1779" and "RFC2253" (case insensitive). "CANONICAL" is not374* permitted and an {@code IllegalArgumentException} will be thrown.375*376* <p>This method returns Strings in the format as specified in377* {@link #getName(String)} and also emits additional attribute type378* keywords for OIDs that have entries in the {@code oidMap}379* parameter. OID entries in the oidMap take precedence over the default380* OIDs recognized by {@code getName(String)}.381* Improperly specified OIDs are ignored; however if an OID382* in the name maps to an improperly specified keyword, an383* {@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 recognized388* by other implementations; therefore do not use this method if389* you are unsure if these keywords will be recognized by other390* implementations.391*392* @param format the format to use393* @param oidMap an OID map, where each key is an object identifier in394* 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 format399* @throws IllegalArgumentException if the specified format is invalid,400* null, or an OID in the name maps to an improperly specified keyword401* @throws NullPointerException if {@code oidMap} is {@code null}402* @since 1.6403*/404public String getName(String format, Map<String, String> oidMap) {405if (oidMap == null) {406throw new NullPointerException407(sun.security.util.ResourcesMgr.getString408("provided.null.OID.map"));409}410if (format != null) {411if (format.equalsIgnoreCase(RFC1779)) {412return thisX500Name.getRFC1779Name(oidMap);413} else if (format.equalsIgnoreCase(RFC2253)) {414return thisX500Name.getRFC2253Name(oidMap);415}416}417throw new IllegalArgumentException("invalid format specified");418}419420/**421* Returns the distinguished name in ASN.1 DER encoded form. The ASN.1422* notation for this structure is supplied in the documentation for423* {@link #X500Principal(byte[] name) X500Principal(byte[] name)}.424*425* <p>Note that the byte array returned is cloned to protect against426* subsequent modifications.427*428* @return a byte array containing the distinguished name in ASN.1 DER429* encoded form430*/431public byte[] getEncoded() {432try {433return thisX500Name.getEncoded();434} catch (IOException e) {435throw new RuntimeException("unable to get encoding", e);436}437}438439/**440* Return a user-friendly string representation of this441* {@code X500Principal}.442*443* @return a string representation of this {@code X500Principal}444*/445public String toString() {446return thisX500Name.toString();447}448449/**450* Compares the specified {@code Object} with this451* {@code X500Principal} for equality.452*453* <p> Specifically, this method returns {@code true} if454* the {@code Object} <i>o</i> is an {@code X500Principal}455* and if the respective canonical string representations456* (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 5280.460*461* @param o Object to be compared for equality with this462* {@code X500Principal}463*464* @return {@code true} if the specified {@code Object} is equal465* to this {@code X500Principal}, {@code false} otherwise466*/467public boolean equals(Object o) {468if (this == o) {469return true;470}471if (o instanceof X500Principal == false) {472return false;473}474X500Principal other = (X500Principal)o;475return this.thisX500Name.equals(other.thisX500Name);476}477478/**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*/486public int hashCode() {487return thisX500Name.hashCode();488}489490/**491* Save the X500Principal object to a stream.492*493* @serialData this {@code X500Principal} is serialized494* by writing out its DER-encoded form495* (the value of {@code getEncoded} is serialized).496*497* @param s the {@code ObjectOutputStream} to which data is written498* @throws IOException if an I/O error occurs499*/500@java.io.Serial501private void writeObject(java.io.ObjectOutputStream s)502throws IOException {503s.writeObject(thisX500Name.getEncodedInternal());504}505506/**507* Reads this object from a stream (i.e., deserializes it).508*509* @param s the {@code ObjectInputStream} from which data is read510* @throws IOException if an I/O error occurs511* @throws NotActiveException if serialization is not active512* @throws ClassNotFoundException if a serialized class cannot be loaded513*/514@java.io.Serial515private void readObject(java.io.ObjectInputStream s)516throws java.io.IOException,517java.io.NotActiveException,518ClassNotFoundException {519520// re-create thisX500Name521thisX500Name = new X500Name((byte[])s.readObject());522}523}524525526