Home | History | Annotate | Download | only in x509
      1 /*
      2  * Copyright (c) 1997, 2011, 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 
     26 package sun.security.x509;
     27 
     28 import java.io.IOException;
     29 import java.util.Locale;
     30 
     31 import sun.security.util.*;
     32 
     33 /**
     34  * This class implements the RFC822Name as required by the GeneralNames
     35  * ASN.1 object.
     36  *
     37  * @author Amit Kapoor
     38  * @author Hemma Prafullchandra
     39  * @see GeneralName
     40  * @see GeneralNames
     41  * @see GeneralNameInterface
     42  */
     43 public class RFC822Name implements GeneralNameInterface
     44 {
     45     private String name;
     46 
     47     /**
     48      * Create the RFC822Name object from the passed encoded Der value.
     49      *
     50      * @param derValue the encoded DER RFC822Name.
     51      * @exception IOException on error.
     52      */
     53     public RFC822Name(DerValue derValue) throws IOException {
     54         name = derValue.getIA5String();
     55         parseName(name);
     56     }
     57 
     58     /**
     59      * Create the RFC822Name object with the specified name.
     60      *
     61      * @param name the RFC822Name.
     62      * @throws IOException on invalid input name
     63      */
     64     public RFC822Name(String name) throws IOException {
     65         parseName(name);
     66         this.name = name;
     67     }
     68 
     69     /**
     70      * Parse an RFC822Name string to see if it is a valid
     71      * addr-spec according to IETF RFC822 and RFC2459:
     72      * [local-part@]domain
     73      * <p>
     74      * local-part@ could be empty for an RFC822Name NameConstraint,
     75      * but the domain at least must be non-empty.  Case is not
     76      * significant.
     77      *
     78      * @param name the RFC822Name string
     79      * @throws IOException if name is not valid
     80      */
     81     public void parseName(String name) throws IOException {
     82         if (name == null || name.length() == 0) {
     83             throw new IOException("RFC822Name may not be null or empty");
     84         }
     85         // See if domain is a valid domain name
     86         String domain = name.substring(name.indexOf('@')+1);
     87         if (domain.length() == 0) {
     88             throw new IOException("RFC822Name may not end with @");
     89         } else {
     90             //An RFC822 NameConstraint could start with a ., although
     91             //a DNSName may not
     92             if (domain.startsWith(".")) {
     93                 if (domain.length() == 1)
     94                     throw new IOException("RFC822Name domain may not be just .");
     95             }
     96         }
     97     }
     98 
     99     /**
    100      * Return the type of the GeneralName.
    101      */
    102     public int getType() {
    103         return (GeneralNameInterface.NAME_RFC822);
    104     }
    105 
    106     /**
    107      * Return the actual name value of the GeneralName.
    108      */
    109     public String getName() {
    110         return name;
    111     }
    112 
    113     /**
    114      * Encode the RFC822 name into the DerOutputStream.
    115      *
    116      * @param out the DER stream to encode the RFC822Name to.
    117      * @exception IOException on encoding errors.
    118      */
    119     public void encode(DerOutputStream out) throws IOException {
    120         out.putIA5String(name);
    121     }
    122 
    123     /**
    124      * Convert the name into user readable string.
    125      */
    126     public String toString() {
    127         return ("RFC822Name: " + name);
    128     }
    129 
    130     /**
    131      * Compares this name with another, for equality.
    132      *
    133      * @return true iff the names are equivalent
    134      * according to RFC2459.
    135      */
    136     public boolean equals(Object obj) {
    137         if (this == obj)
    138             return true;
    139 
    140         if (!(obj instanceof RFC822Name))
    141             return false;
    142 
    143         RFC822Name other = (RFC822Name)obj;
    144 
    145         // RFC2459 mandates that these names are
    146         // not case-sensitive
    147         return name.equalsIgnoreCase(other.name);
    148     }
    149 
    150     /**
    151      * Returns the hash code value for this object.
    152      *
    153      * @return a hash code value for this object.
    154      */
    155     public int hashCode() {
    156         return name.toUpperCase(Locale.ENGLISH).hashCode();
    157     }
    158 
    159     /**
    160      * Return constraint type:<ul>
    161      *   <li>NAME_DIFF_TYPE = -1: input name is different type from name (i.e. does not constrain)
    162      *   <li>NAME_MATCH = 0: input name matches name
    163      *   <li>NAME_NARROWS = 1: input name narrows name
    164      *   <li>NAME_WIDENS = 2: input name widens name
    165      *   <li>NAME_SAME_TYPE = 3: input name does not match or narrow name, but is same type
    166      * </ul>.  These results are used in checking NameConstraints during
    167      * certification path verification.
    168      * <p>
    169      * [RFC2459]    When the subjectAltName extension contains an Internet mail address,
    170      * the address MUST be included as an rfc822Name. The format of an
    171      * rfc822Name is an "addr-spec" as defined in RFC 822 [RFC 822]. An
    172      * addr-spec has the form "local-part@domain". Note that an addr-spec
    173      * has no phrase (such as a common name) before it, has no comment (text
    174      * surrounded in parentheses) after it, and is not surrounded by "&lt;" and
    175      * "&gt;". Note that while upper and lower case letters are allowed in an
    176      * RFC 822 addr-spec, no significance is attached to the case.
    177      * <p>
    178      * @param inputName to be checked for being constrained
    179      * @returns constraint type above
    180      * @throws UnsupportedOperationException if name is not exact match, but narrowing and widening are
    181      *          not supported for this name type.
    182      */
    183     public int constrains(GeneralNameInterface inputName) throws UnsupportedOperationException {
    184         int constraintType;
    185         if (inputName == null)
    186             constraintType = NAME_DIFF_TYPE;
    187         else if (inputName.getType() != (GeneralNameInterface.NAME_RFC822)) {
    188             constraintType = NAME_DIFF_TYPE;
    189         } else {
    190             //RFC2459 specifies that case is not significant in RFC822Names
    191             String inName =
    192                 (((RFC822Name)inputName).getName()).toLowerCase(Locale.ENGLISH);
    193             String thisName = name.toLowerCase(Locale.ENGLISH);
    194             if (inName.equals(thisName)) {
    195                 constraintType = NAME_MATCH;
    196             } else if (thisName.endsWith(inName)) {
    197                 /* if both names contain @, then they had to match exactly */
    198                 if (inName.indexOf('@') != -1) {
    199                     constraintType = NAME_SAME_TYPE;
    200                 } else if (inName.startsWith(".")) {
    201                     constraintType = NAME_WIDENS;
    202                 } else {
    203                     int inNdx = thisName.lastIndexOf(inName);
    204                     if (thisName.charAt(inNdx-1) == '@' ) {
    205                         constraintType = NAME_WIDENS;
    206                     } else {
    207                         constraintType = NAME_SAME_TYPE;
    208                     }
    209                 }
    210             } else if (inName.endsWith(thisName)) {
    211                 /* if thisName contains @, then they had to match exactly */
    212                 if (thisName.indexOf('@') != -1) {
    213                     constraintType = NAME_SAME_TYPE;
    214                 } else if (thisName.startsWith(".")) {
    215                     constraintType = NAME_NARROWS;
    216                 } else {
    217                     int ndx = inName.lastIndexOf(thisName);
    218                     if (inName.charAt(ndx-1) == '@') {
    219                         constraintType = NAME_NARROWS;
    220                     } else {
    221                         constraintType = NAME_SAME_TYPE;
    222                     }
    223                 }
    224             } else {
    225                 constraintType = NAME_SAME_TYPE;
    226             }
    227         }
    228         return constraintType;
    229     }
    230 
    231     /**
    232      * Return subtree depth of this name for purposes of determining
    233      * NameConstraints minimum and maximum bounds.
    234      *
    235      * @returns distance of name from root
    236      * @throws UnsupportedOperationException if not supported for this name type
    237      */
    238     public int subtreeDepth() throws UnsupportedOperationException {
    239         String subtree=name;
    240         int i=1;
    241 
    242         /* strip off name@ portion */
    243         int atNdx = subtree.lastIndexOf('@');
    244         if (atNdx >= 0) {
    245             i++;
    246             subtree=subtree.substring(atNdx+1);
    247         }
    248 
    249         /* count dots in dnsname, adding one if dnsname preceded by @ */
    250         for (; subtree.lastIndexOf('.') >= 0; i++) {
    251             subtree=subtree.substring(0,subtree.lastIndexOf('.'));
    252         }
    253 
    254         return i;
    255     }
    256 }
    257