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.ArrayList;
     27 import java.util.Collection;
     28 import java.util.Collections;
     29 import java.util.Iterator;
     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 final 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<List<AttributeTypeAndValue>> 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         DerInputStream in = new DerInputStream(encoding);
     68 
     69         if (in.getEndOffset() != encoding.length) {
     70             throw new IOException("Wrong content length");
     71         }
     72 
     73         ASN1.decode(in);
     74 
     75         this.rdn = (List<List<AttributeTypeAndValue>>) in.content;
     76     }
     77 
     78     /**
     79      * Creates new <code>Name</code> instance
     80      *
     81      * @param name - Name as String
     82      * @throws IOException - if string is wrong
     83      */
     84     public Name(String name) throws IOException {
     85         rdn = new DNParser(name).parse();
     86     }
     87 
     88     private Name(List<List<AttributeTypeAndValue>> rdn) {
     89         this.rdn = rdn;
     90     }
     91 
     92     /**
     93      * Returns <code>X500Principal</code> instance corresponding to this
     94      * <code>Name</code> instance
     95      *
     96      * @return equivalent X500Principal object
     97      */
     98     public X500Principal getX500Principal(){
     99         return new X500Principal(getEncoded());
    100     }
    101 
    102     /**
    103      * Returns Relative Distinguished Name as <code>String</code> according
    104      * the format requested
    105      *
    106      * @param format one of X500Principal.CANONICAL, X500Principal.RFC1779, or
    107      *     X500Principal.RFC2253, case insensitive
    108      */
    109     public String getName(String format) {
    110         //
    111         // check X500Principal constants first
    112         //
    113         if (X500Principal.RFC1779.equals(format)) {
    114 
    115             if (rfc1779String == null) {
    116                 rfc1779String = getName0(format);
    117             }
    118             return rfc1779String;
    119 
    120         } else if (X500Principal.RFC2253.equals(format)) {
    121 
    122             if (rfc2253String == null) {
    123                 rfc2253String = getName0(format);
    124             }
    125             return rfc2253String;
    126 
    127         } else if (X500Principal.CANONICAL.equals(format)) {
    128 
    129             if (canonicalString == null) {
    130                 canonicalString = getName0(format);
    131             }
    132             return canonicalString;
    133 
    134         }
    135         //
    136         // compare ignore case
    137         //
    138         else if (X500Principal.RFC1779.equalsIgnoreCase(format)) {
    139 
    140             if (rfc1779String == null) {
    141                 rfc1779String = getName0(X500Principal.RFC1779);
    142             }
    143             return rfc1779String;
    144 
    145         } else if (X500Principal.RFC2253.equalsIgnoreCase(format)) {
    146 
    147             if (rfc2253String == null) {
    148                 rfc2253String = getName0(X500Principal.RFC2253);
    149             }
    150             return rfc2253String;
    151 
    152         } else if (X500Principal.CANONICAL.equalsIgnoreCase(format)) {
    153 
    154             if (canonicalString == null) {
    155                 canonicalString = getName0(X500Principal.CANONICAL);
    156             }
    157             return canonicalString;
    158 
    159         } else {
    160             throw new IllegalArgumentException("Illegal format: " + format);
    161         }
    162     }
    163 
    164     /**
    165      * Returns Relative Distinguished Name as <code>String</code> according
    166      * the format requested, format is int value
    167      */
    168     private String getName0(String format) {
    169         StringBuilder name = new StringBuilder();
    170 
    171         // starting with the last element and moving to the first.
    172         for (int i = rdn.size() - 1; i >= 0; i--) {
    173             List<AttributeTypeAndValue> atavList = rdn.get(i);
    174 
    175             if (X500Principal.CANONICAL == format) {
    176                 atavList = new ArrayList<AttributeTypeAndValue>(atavList);
    177                 Collections.sort(atavList, new AttributeTypeAndValueComparator());
    178             }
    179 
    180             // Relative Distinguished Name to string
    181             Iterator<AttributeTypeAndValue> it = atavList.iterator();
    182             while (it.hasNext()) {
    183                 AttributeTypeAndValue attributeTypeAndValue = it.next();
    184                 attributeTypeAndValue.appendName(format, name);
    185                 if (it.hasNext()) {
    186                     // multi-valued RDN
    187                     if (X500Principal.RFC1779 == format) {
    188                         name.append(" + ");
    189                     } else {
    190                         name.append('+');
    191                     }
    192                 }
    193             }
    194 
    195             if (i != 0) {
    196                 name.append(',');
    197                 if (format == X500Principal.RFC1779) {
    198                     name.append(' ');
    199                 }
    200             }
    201         }
    202 
    203         String sName = name.toString();
    204         if (X500Principal.CANONICAL.equals(format)) {
    205             sName = sName.toLowerCase(Locale.US);
    206         }
    207         return sName;
    208     }
    209 
    210     /**
    211      * Gets encoded form of DN
    212      *
    213      * @return return encoding, no copying is performed
    214      */
    215     public byte[] getEncoded() {
    216         if (encoded == null) {
    217             encoded = ASN1.encode(this);
    218         }
    219         return encoded;
    220     }
    221 
    222     /**
    223      * According to RFC 3280 (http://www.ietf.org/rfc/rfc3280.txt)
    224      * X.501 Name structure is defined as follows:
    225      *
    226      * Name ::= CHOICE {
    227      *     RDNSequence }
    228      *
    229      * RDNSequence ::= SEQUENCE OF RelativeDistinguishedName
    230      *
    231      * RelativeDistinguishedName ::=
    232      *     SET OF AttributeTypeAndValue
    233      *
    234      */
    235     public static final ASN1SetOf ASN1_RDN = new ASN1SetOf(
    236             AttributeTypeAndValue.ASN1);
    237 
    238     public static final ASN1SequenceOf ASN1 = new ASN1SequenceOf(ASN1_RDN) {
    239 
    240         public Object getDecodedObject(BerInputStream in) {
    241             return new Name((List<List<AttributeTypeAndValue>>) in.content);
    242         }
    243 
    244         public Collection getValues(Object object) {
    245             return ((Name) object).rdn;
    246         }
    247     };
    248 }
    249