Home | History | Annotate | Download | only in x501
      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 /**
     19 * @author Alexander V. Esin
     20 * @version $Revision$
     21 */
     22 
     23 package org.apache.harmony.security.x501;
     24 
     25 import java.io.IOException;
     26 import java.util.Collection;
     27 import java.util.Collections;
     28 import java.util.Iterator;
     29 import java.util.LinkedList;
     30 import java.util.List;
     31 import java.util.Locale;
     32 import javax.security.auth.x500.X500Principal;
     33 import org.apache.harmony.security.asn1.ASN1SequenceOf;
     34 import org.apache.harmony.security.asn1.ASN1SetOf;
     35 import org.apache.harmony.security.asn1.BerInputStream;
     36 import org.apache.harmony.security.asn1.DerInputStream;
     37 import org.apache.harmony.security.x509.DNParser;
     38 
     39 
     40 /**
     41  * X.501 Name
     42  */
     43 public class Name {
     44 
     45     //ASN.1 DER encoding of Name
     46     private volatile byte[] encoded;
     47 
     48     // RFC1779 string
     49     private String rfc1779String;
     50 
     51     // RFC2253 string
     52     private String rfc2253String;
     53 
     54     //CANONICAL string
     55     private String canonicalString;
     56 
     57     //Collection of RDNs
     58     private List rdn;
     59 
     60     /**
     61      * Creates new <code>Name</code> instance from its DER encoding
     62      *
     63      * @param encoding - ASN.1 DER encoding
     64      * @throws IOException - if encoding is wrong
     65      */
     66     public Name(byte[] encoding) throws IOException {
     67 
     68         DerInputStream in = new DerInputStream(encoding);
     69 
     70         if (in.getEndOffset() != encoding.length) {
     71             throw new IOException("Wrong content length");
     72         }
     73 
     74         ASN1.decode(in);
     75 
     76         this.rdn = (List) in.content;
     77     }
     78 
     79     /**
     80      * Creates new <code>Name</code> instance
     81      *
     82      * @param name - Name as String
     83      * @throws IOException - if string is wrong
     84      */
     85     public Name(String name) throws IOException {
     86         rdn = new DNParser(name).parse();
     87     }
     88 
     89     // Creates Name instance
     90     private Name(List rdn) {
     91         this.rdn = rdn;
     92     }
     93 
     94     /**
     95      * Returns <code>X500Principal</code> instance corresponding to this
     96      * <code>Name</code> instance
     97      *
     98      * @return equivalent X500Principal object
     99      */
    100     public X500Principal getX500Principal(){
    101         return new X500Principal(getName0(X500Principal.RFC2253));
    102     }
    103 
    104     /**
    105      * Returns Relative Distinguished Name as <code>String</code> according
    106      * the format requested
    107      *
    108      * @param format
    109      *            Name format requested
    110      * @return Relative Distinguished Name as <code>String</code> according
    111      *         the format requested
    112      */
    113     public String getName(String format) {
    114 
    115         //
    116         // check X500Principal constants first
    117         //
    118         if (X500Principal.RFC1779.equals(format)) {
    119 
    120             if (rfc1779String == null) {
    121                 rfc1779String = getName0(format);
    122             }
    123             return rfc1779String;
    124 
    125         } else if (X500Principal.RFC2253.equals(format)) {
    126 
    127             if (rfc2253String == null) {
    128                 rfc2253String = getName0(format);
    129             }
    130             return rfc2253String;
    131 
    132         } else if (X500Principal.CANONICAL.equals(format)) {
    133 
    134             if (canonicalString == null) {
    135                 canonicalString = getName0(format);
    136             }
    137             return canonicalString;
    138 
    139         }
    140         //
    141         // compare ignore case
    142         //
    143         else if (X500Principal.RFC1779.equalsIgnoreCase(format)) {
    144 
    145             if (rfc1779String == null) {
    146                 rfc1779String = getName0(X500Principal.RFC1779);
    147             }
    148             return rfc1779String;
    149 
    150         } else if (X500Principal.RFC2253.equalsIgnoreCase(format)) {
    151 
    152             if (rfc2253String == null) {
    153                 rfc2253String = getName0(X500Principal.RFC2253);
    154             }
    155             return rfc2253String;
    156 
    157         } else if (X500Principal.CANONICAL.equalsIgnoreCase(format)) {
    158 
    159             if (canonicalString == null) {
    160                 canonicalString = getName0(X500Principal.CANONICAL);
    161             }
    162             return canonicalString;
    163 
    164         } else {
    165             throw new IllegalArgumentException("Illegal format: " + format);
    166         }
    167     }
    168 
    169     /**
    170      * Returns Relative Distinguished Name as <code>String</code> according
    171      * the format requested, format is int value
    172      *
    173      * @param format
    174      *            Name format requested
    175      * @return Relative Distinguished Name as <code>String</code> according
    176      *         the format requested
    177      */
    178     private String getName0(String format) {
    179 
    180         StringBuffer name = new StringBuffer();
    181 
    182         // starting with the last element and moving to the first.
    183         for (int i = rdn.size() - 1; i >= 0; i--) {
    184             List atavList = (List) rdn.get(i);
    185 
    186             if (X500Principal.CANONICAL == format) {
    187                 List sortedList = new LinkedList(atavList);
    188                 Collections.sort(sortedList,
    189                         new AttributeTypeAndValueComparator());
    190                 atavList = sortedList;
    191             }
    192 
    193             // Relative Distinguished Name to string
    194             Iterator it = atavList.iterator();
    195             while (it.hasNext()) {
    196                 AttributeTypeAndValue _ava = (AttributeTypeAndValue) it.next();
    197                 _ava.appendName(format, name);
    198                 if (it.hasNext()) {
    199                     // multi-valued RDN
    200                     if (X500Principal.RFC1779 == format) {
    201                         name.append(" + ");
    202                     } else {
    203                         name.append('+');
    204                     }
    205                 }
    206             }
    207 
    208             if (i != 0) {
    209                 name.append(',');
    210                 if (format == X500Principal.RFC1779) {
    211                     name.append(' ');
    212                 }
    213             }
    214         }
    215 
    216         String sName = name.toString();
    217         if (X500Principal.CANONICAL.equals(format)) {
    218             sName = sName.toLowerCase(Locale.US);
    219         }
    220         return sName;
    221     }
    222 
    223     /**
    224      * Gets encoded form of DN
    225      *
    226      * @return return encoding, no copying is performed
    227      */
    228     public byte[] getEncoded() {
    229         if (encoded == null) {
    230             encoded = ASN1.encode(this);
    231         }
    232         return encoded;
    233     }
    234 
    235     /**
    236      * According to RFC 3280 (http://www.ietf.org/rfc/rfc3280.txt)
    237      * X.501 Name structure is defined as follows:
    238      *
    239      * Name ::= CHOICE {
    240      *     RDNSequence }
    241      *
    242      * RDNSequence ::= SEQUENCE OF RelativeDistinguishedName
    243      *
    244      * RelativeDistinguishedName ::=
    245      *     SET OF AttributeTypeAndValue
    246      *
    247      */
    248 
    249     public static final ASN1SetOf ASN1_RDN = new ASN1SetOf(
    250             AttributeTypeAndValue.ASN1);
    251 
    252     public static final ASN1SequenceOf ASN1 = new ASN1SequenceOf(ASN1_RDN) {
    253 
    254         public Object getDecodedObject(BerInputStream in) {
    255             return new Name((List) in.content);
    256         }
    257 
    258         public Collection getValues(Object object) {
    259             return ((Name) object).rdn; //FIXME what about get method?
    260         }
    261     };
    262 }
    263