Home | History | Annotate | Download | only in style
      1 package org.bouncycastle.asn1.x500.style;
      2 
      3 import java.io.IOException;
      4 import java.util.Enumeration;
      5 import java.util.Hashtable;
      6 
      7 import org.bouncycastle.asn1.ASN1Encodable;
      8 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
      9 import org.bouncycastle.asn1.ASN1ParsingException;
     10 import org.bouncycastle.asn1.DERUTF8String;
     11 import org.bouncycastle.asn1.x500.AttributeTypeAndValue;
     12 import org.bouncycastle.asn1.x500.RDN;
     13 import org.bouncycastle.asn1.x500.X500Name;
     14 import org.bouncycastle.asn1.x500.X500NameStyle;
     15 
     16 /**
     17  * This class provides some default behavior and common implementation for a
     18  * X500NameStyle. It should be easily extendable to support implementing the
     19  * desired X500NameStyle.
     20  */
     21 public abstract class AbstractX500NameStyle
     22     implements X500NameStyle
     23 {
     24 
     25     /**
     26      * Tool function to shallow copy a Hashtable.
     27      *
     28      * @param paramsMap table to copy
     29      * @return the copy of the table
     30      */
     31     public static Hashtable copyHashTable(Hashtable paramsMap)
     32     {
     33         Hashtable newTable = new Hashtable();
     34 
     35         Enumeration keys = paramsMap.keys();
     36         while (keys.hasMoreElements())
     37         {
     38             Object key = keys.nextElement();
     39             newTable.put(key, paramsMap.get(key));
     40         }
     41 
     42         return newTable;
     43     }
     44 
     45     private int calcHashCode(ASN1Encodable enc)
     46     {
     47         String value = IETFUtils.valueToString(enc);
     48         value = IETFUtils.canonicalize(value);
     49         return value.hashCode();
     50     }
     51 
     52     public int calculateHashCode(X500Name name)
     53     {
     54         int hashCodeValue = 0;
     55         RDN[] rdns = name.getRDNs();
     56 
     57         // this needs to be order independent, like equals
     58         for (int i = 0; i != rdns.length; i++)
     59         {
     60             if (rdns[i].isMultiValued())
     61             {
     62                 AttributeTypeAndValue[] atv = rdns[i].getTypesAndValues();
     63 
     64                 for (int j = 0; j != atv.length; j++)
     65                 {
     66                     hashCodeValue ^= atv[j].getType().hashCode();
     67                     hashCodeValue ^= calcHashCode(atv[j].getValue());
     68                 }
     69             }
     70             else
     71             {
     72                 hashCodeValue ^= rdns[i].getFirst().getType().hashCode();
     73                 hashCodeValue ^= calcHashCode(rdns[i].getFirst().getValue());
     74             }
     75         }
     76 
     77         return hashCodeValue;
     78     }
     79 
     80 
     81     /**
     82      * For all string values starting with '#' is assumed, that these are
     83      * already valid ASN.1 objects encoded in hex.
     84      * <p>
     85      * All other string values are send to
     86      * {@link AbstractX500NameStyle#encodeStringValue(ASN1ObjectIdentifier, String)}.
     87      * </p>
     88      * Subclasses should overwrite
     89      * {@link AbstractX500NameStyle#encodeStringValue(ASN1ObjectIdentifier, String)}
     90      * to change the encoding of specific types.
     91      *
     92      * @param oid the DN name of the value.
     93      * @param value the String representation of the value.
     94      */
     95     public ASN1Encodable stringToValue(ASN1ObjectIdentifier oid, String value)
     96     {
     97         if (value.length() != 0 && value.charAt(0) == '#')
     98         {
     99             try
    100             {
    101                 return IETFUtils.valueFromHexString(value, 1);
    102             }
    103             catch (IOException e)
    104             {
    105                 throw new ASN1ParsingException("can't recode value for oid " + oid.getId());
    106             }
    107         }
    108 
    109         if (value.length() != 0 && value.charAt(0) == '\\')
    110         {
    111             value = value.substring(1);
    112         }
    113 
    114         return encodeStringValue(oid, value);
    115     }
    116 
    117     /**
    118      * Encoded every value into a UTF8String.
    119      * <p>
    120      * Subclasses should overwrite
    121      * this method to change the encoding of specific types.
    122      * </p>
    123      *
    124      * @param oid the DN oid of the value
    125      * @param value the String representation of the value
    126      * @return a the value encoded into a ASN.1 object. Never returns <code>null</code>.
    127      */
    128     protected ASN1Encodable encodeStringValue(ASN1ObjectIdentifier oid, String value)
    129     {
    130         return new DERUTF8String(value);
    131     }
    132 
    133     public boolean areEqual(X500Name name1, X500Name name2)
    134     {
    135         RDN[] rdns1 = name1.getRDNs();
    136         RDN[] rdns2 = name2.getRDNs();
    137 
    138         if (rdns1.length != rdns2.length)
    139         {
    140             return false;
    141         }
    142 
    143         boolean reverse = false;
    144 
    145         if (rdns1[0].getFirst() != null && rdns2[0].getFirst() != null)
    146         {
    147             reverse = !rdns1[0].getFirst().getType().equals(rdns2[0].getFirst().getType());  // guess forward
    148         }
    149 
    150         for (int i = 0; i != rdns1.length; i++)
    151         {
    152             if (!foundMatch(reverse, rdns1[i], rdns2))
    153             {
    154                 return false;
    155             }
    156         }
    157 
    158         return true;
    159     }
    160 
    161     private boolean foundMatch(boolean reverse, RDN rdn, RDN[] possRDNs)
    162     {
    163         if (reverse)
    164         {
    165             for (int i = possRDNs.length - 1; i >= 0; i--)
    166             {
    167                 if (possRDNs[i] != null && rdnAreEqual(rdn, possRDNs[i]))
    168                 {
    169                     possRDNs[i] = null;
    170                     return true;
    171                 }
    172             }
    173         }
    174         else
    175         {
    176             for (int i = 0; i != possRDNs.length; i++)
    177             {
    178                 if (possRDNs[i] != null && rdnAreEqual(rdn, possRDNs[i]))
    179                 {
    180                     possRDNs[i] = null;
    181                     return true;
    182                 }
    183             }
    184         }
    185 
    186         return false;
    187     }
    188 
    189     protected boolean rdnAreEqual(RDN rdn1, RDN rdn2)
    190     {
    191         return IETFUtils.rDNAreEqual(rdn1, rdn2);
    192     }
    193 }
    194