Home | History | Annotate | Download | only in x500
      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      */
     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      */
     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 
    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      */
    124     public X500Principal(String name) {
    125         if (name == null) {
    126             throw new NullPointerException("Name cannot be null");
    127         }
    128         try {
    129             dn = new Name(name);
    130         } catch (IOException e) {
    131             throw incorrectInputName(e);
    132         }
    133     }
    134 
    135     public X500Principal(String name, Map<String,String> keywordMap){
    136         if (name == null) {
    137             throw new NullPointerException("Name cannot be null");
    138         }
    139         try {
    140             dn = new Name(substituteNameFromMap(name, keywordMap));
    141         } catch (IOException e) {
    142             throw incorrectInputName(e);
    143         }
    144     }
    145 
    146     private IllegalArgumentException incorrectInputName(IOException e) {
    147         IllegalArgumentException iae = new IllegalArgumentException("Incorrect input name");
    148         iae.initCause(e);
    149         throw iae;
    150     }
    151 
    152     private transient String canonicalName;
    153     private synchronized String getCanonicalName() {
    154         if (canonicalName == null) {
    155             canonicalName = dn.getName(CANONICAL);
    156         }
    157         return canonicalName;
    158     }
    159 
    160     @Override
    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      */
    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      */
    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      */
    214     public String getName(String format) {
    215         if (CANONICAL.equals(format)) {
    216             return getCanonicalName();
    217         }
    218 
    219         return dn.getName(format);
    220     }
    221 
    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.toUpperCase().equals("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.toUpperCase().equals("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");
    281         }
    282     }
    283 
    284     @Override
    285     public int hashCode() {
    286         return getCanonicalName().hashCode();
    287     }
    288 
    289     @Override
    290     public String toString() {
    291         return dn.getName(RFC1779);
    292     }
    293 
    294     private void writeObject(ObjectOutputStream out) throws IOException {
    295         out.writeObject(dn.getEncoded());
    296     }
    297 
    298     private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
    299         dn = (Name) Name.ASN1.decode((byte[]) in.readObject());
    300     }
    301 
    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