Home | History | Annotate | Download | only in provider
      1 package org.bouncycastle.jce.provider;
      2 
      3 import java.util.Collection;
      4 import java.util.Collections;
      5 import java.util.HashMap;
      6 import java.util.HashSet;
      7 import java.util.Iterator;
      8 import java.util.Map;
      9 import java.util.Set;
     10 
     11 import org.bouncycastle.asn1.ASN1OctetString;
     12 import org.bouncycastle.asn1.ASN1Sequence;
     13 import org.bouncycastle.asn1.DERIA5String;
     14 import org.bouncycastle.asn1.x509.GeneralName;
     15 import org.bouncycastle.asn1.x509.GeneralSubtree;
     16 import org.bouncycastle.util.Arrays;
     17 import org.bouncycastle.util.Integers;
     18 import org.bouncycastle.util.Strings;
     19 
     20 public class PKIXNameConstraintValidator
     21 {
     22     private Set excludedSubtreesDN = new HashSet();
     23 
     24     private Set excludedSubtreesDNS = new HashSet();
     25 
     26     private Set excludedSubtreesEmail = new HashSet();
     27 
     28     private Set excludedSubtreesURI = new HashSet();
     29 
     30     private Set excludedSubtreesIP = new HashSet();
     31 
     32     private Set permittedSubtreesDN;
     33 
     34     private Set permittedSubtreesDNS;
     35 
     36     private Set permittedSubtreesEmail;
     37 
     38     private Set permittedSubtreesURI;
     39 
     40     private Set permittedSubtreesIP;
     41 
     42     public PKIXNameConstraintValidator()
     43     {
     44     }
     45 
     46     private static boolean withinDNSubtree(
     47         ASN1Sequence dns,
     48         ASN1Sequence subtree)
     49     {
     50         if (subtree.size() < 1)
     51         {
     52             return false;
     53         }
     54 
     55         if (subtree.size() > dns.size())
     56         {
     57             return false;
     58         }
     59 
     60         for (int j = subtree.size() - 1; j >= 0; j--)
     61         {
     62             if (!subtree.getObjectAt(j).equals(dns.getObjectAt(j)))
     63             {
     64                 return false;
     65             }
     66         }
     67 
     68         return true;
     69     }
     70 
     71     public void checkPermittedDN(ASN1Sequence dns)
     72         throws PKIXNameConstraintValidatorException
     73     {
     74         checkPermittedDN(permittedSubtreesDN, dns);
     75     }
     76 
     77     public void checkExcludedDN(ASN1Sequence dns)
     78         throws PKIXNameConstraintValidatorException
     79     {
     80         checkExcludedDN(excludedSubtreesDN, dns);
     81     }
     82 
     83     private void checkPermittedDN(Set permitted, ASN1Sequence dns)
     84         throws PKIXNameConstraintValidatorException
     85     {
     86         if (permitted == null)
     87         {
     88             return;
     89         }
     90 
     91         if (permitted.isEmpty() && dns.size() == 0)
     92         {
     93             return;
     94         }
     95         Iterator it = permitted.iterator();
     96 
     97         while (it.hasNext())
     98         {
     99             ASN1Sequence subtree = (ASN1Sequence)it.next();
    100 
    101             if (withinDNSubtree(dns, subtree))
    102             {
    103                 return;
    104             }
    105         }
    106 
    107         throw new PKIXNameConstraintValidatorException(
    108             "Subject distinguished name is not from a permitted subtree");
    109     }
    110 
    111     private void checkExcludedDN(Set excluded, ASN1Sequence dns)
    112         throws PKIXNameConstraintValidatorException
    113     {
    114         if (excluded.isEmpty())
    115         {
    116             return;
    117         }
    118 
    119         Iterator it = excluded.iterator();
    120 
    121         while (it.hasNext())
    122         {
    123             ASN1Sequence subtree = (ASN1Sequence)it.next();
    124 
    125             if (withinDNSubtree(dns, subtree))
    126             {
    127                 throw new PKIXNameConstraintValidatorException(
    128                     "Subject distinguished name is from an excluded subtree");
    129             }
    130         }
    131     }
    132 
    133     private Set intersectDN(Set permitted, Set dns)
    134     {
    135         Set intersect = new HashSet();
    136         for (Iterator it = dns.iterator(); it.hasNext();)
    137         {
    138             ASN1Sequence dn = ASN1Sequence.getInstance(((GeneralSubtree)it
    139                 .next()).getBase().getName().toASN1Primitive());
    140             if (permitted == null)
    141             {
    142                 if (dn != null)
    143                 {
    144                     intersect.add(dn);
    145                 }
    146             }
    147             else
    148             {
    149                 Iterator _iter = permitted.iterator();
    150                 while (_iter.hasNext())
    151                 {
    152                     ASN1Sequence subtree = (ASN1Sequence)_iter.next();
    153 
    154                     if (withinDNSubtree(dn, subtree))
    155                     {
    156                         intersect.add(dn);
    157                     }
    158                     else if (withinDNSubtree(subtree, dn))
    159                     {
    160                         intersect.add(subtree);
    161                     }
    162                 }
    163             }
    164         }
    165         return intersect;
    166     }
    167 
    168     private Set unionDN(Set excluded, ASN1Sequence dn)
    169     {
    170         if (excluded.isEmpty())
    171         {
    172             if (dn == null)
    173             {
    174                 return excluded;
    175             }
    176             excluded.add(dn);
    177 
    178             return excluded;
    179         }
    180         else
    181         {
    182             Set intersect = new HashSet();
    183 
    184             Iterator it = excluded.iterator();
    185             while (it.hasNext())
    186             {
    187                 ASN1Sequence subtree = (ASN1Sequence)it.next();
    188 
    189                 if (withinDNSubtree(dn, subtree))
    190                 {
    191                     intersect.add(subtree);
    192                 }
    193                 else if (withinDNSubtree(subtree, dn))
    194                 {
    195                     intersect.add(dn);
    196                 }
    197                 else
    198                 {
    199                     intersect.add(subtree);
    200                     intersect.add(dn);
    201                 }
    202             }
    203 
    204             return intersect;
    205         }
    206     }
    207 
    208     private Set intersectEmail(Set permitted, Set emails)
    209     {
    210         Set intersect = new HashSet();
    211         for (Iterator it = emails.iterator(); it.hasNext();)
    212         {
    213             String email = extractNameAsString(((GeneralSubtree)it.next())
    214                 .getBase());
    215 
    216             if (permitted == null)
    217             {
    218                 if (email != null)
    219                 {
    220                     intersect.add(email);
    221                 }
    222             }
    223             else
    224             {
    225                 Iterator it2 = permitted.iterator();
    226                 while (it2.hasNext())
    227                 {
    228                     String _permitted = (String)it2.next();
    229 
    230                     intersectEmail(email, _permitted, intersect);
    231                 }
    232             }
    233         }
    234         return intersect;
    235     }
    236 
    237     private Set unionEmail(Set excluded, String email)
    238     {
    239         if (excluded.isEmpty())
    240         {
    241             if (email == null)
    242             {
    243                 return excluded;
    244             }
    245             excluded.add(email);
    246             return excluded;
    247         }
    248         else
    249         {
    250             Set union = new HashSet();
    251 
    252             Iterator it = excluded.iterator();
    253             while (it.hasNext())
    254             {
    255                 String _excluded = (String)it.next();
    256 
    257                 unionEmail(_excluded, email, union);
    258             }
    259 
    260             return union;
    261         }
    262     }
    263 
    264     /**
    265      * Returns the intersection of the permitted IP ranges in
    266      * <code>permitted</code> with <code>ip</code>.
    267      *
    268      * @param permitted A <code>Set</code> of permitted IP addresses with
    269      *                  their subnet mask as byte arrays.
    270      * @param ips       The IP address with its subnet mask.
    271      * @return The <code>Set</code> of permitted IP ranges intersected with
    272      *         <code>ip</code>.
    273      */
    274     private Set intersectIP(Set permitted, Set ips)
    275     {
    276         Set intersect = new HashSet();
    277         for (Iterator it = ips.iterator(); it.hasNext();)
    278         {
    279             byte[] ip = ASN1OctetString.getInstance(
    280                 ((GeneralSubtree)it.next()).getBase().getName()).getOctets();
    281             if (permitted == null)
    282             {
    283                 if (ip != null)
    284                 {
    285                     intersect.add(ip);
    286                 }
    287             }
    288             else
    289             {
    290                 Iterator it2 = permitted.iterator();
    291                 while (it2.hasNext())
    292                 {
    293                     byte[] _permitted = (byte[])it2.next();
    294                     intersect.addAll(intersectIPRange(_permitted, ip));
    295                 }
    296             }
    297         }
    298         return intersect;
    299     }
    300 
    301     /**
    302      * Returns the union of the excluded IP ranges in <code>excluded</code>
    303      * with <code>ip</code>.
    304      *
    305      * @param excluded A <code>Set</code> of excluded IP addresses with their
    306      *                 subnet mask as byte arrays.
    307      * @param ip       The IP address with its subnet mask.
    308      * @return The <code>Set</code> of excluded IP ranges unified with
    309      *         <code>ip</code> as byte arrays.
    310      */
    311     private Set unionIP(Set excluded, byte[] ip)
    312     {
    313         if (excluded.isEmpty())
    314         {
    315             if (ip == null)
    316             {
    317                 return excluded;
    318             }
    319             excluded.add(ip);
    320 
    321             return excluded;
    322         }
    323         else
    324         {
    325             Set union = new HashSet();
    326 
    327             Iterator it = excluded.iterator();
    328             while (it.hasNext())
    329             {
    330                 byte[] _excluded = (byte[])it.next();
    331                 union.addAll(unionIPRange(_excluded, ip));
    332             }
    333 
    334             return union;
    335         }
    336     }
    337 
    338     /**
    339      * Calculates the union if two IP ranges.
    340      *
    341      * @param ipWithSubmask1 The first IP address with its subnet mask.
    342      * @param ipWithSubmask2 The second IP address with its subnet mask.
    343      * @return A <code>Set</code> with the union of both addresses.
    344      */
    345     private Set unionIPRange(byte[] ipWithSubmask1, byte[] ipWithSubmask2)
    346     {
    347         Set set = new HashSet();
    348 
    349         // difficult, adding always all IPs is not wrong
    350         if (Arrays.areEqual(ipWithSubmask1, ipWithSubmask2))
    351         {
    352             set.add(ipWithSubmask1);
    353         }
    354         else
    355         {
    356             set.add(ipWithSubmask1);
    357             set.add(ipWithSubmask2);
    358         }
    359         return set;
    360     }
    361 
    362     /**
    363      * Calculates the interesction if two IP ranges.
    364      *
    365      * @param ipWithSubmask1 The first IP address with its subnet mask.
    366      * @param ipWithSubmask2 The second IP address with its subnet mask.
    367      * @return A <code>Set</code> with the single IP address with its subnet
    368      *         mask as a byte array or an empty <code>Set</code>.
    369      */
    370     private Set intersectIPRange(byte[] ipWithSubmask1, byte[] ipWithSubmask2)
    371     {
    372         if (ipWithSubmask1.length != ipWithSubmask2.length)
    373         {
    374             return Collections.EMPTY_SET;
    375         }
    376         byte[][] temp = extractIPsAndSubnetMasks(ipWithSubmask1, ipWithSubmask2);
    377         byte ip1[] = temp[0];
    378         byte subnetmask1[] = temp[1];
    379         byte ip2[] = temp[2];
    380         byte subnetmask2[] = temp[3];
    381 
    382         byte minMax[][] = minMaxIPs(ip1, subnetmask1, ip2, subnetmask2);
    383         byte[] min;
    384         byte[] max;
    385         max = min(minMax[1], minMax[3]);
    386         min = max(minMax[0], minMax[2]);
    387 
    388         // minimum IP address must be bigger than max
    389         if (compareTo(min, max) == 1)
    390         {
    391             return Collections.EMPTY_SET;
    392         }
    393         // OR keeps all significant bits
    394         byte[] ip = or(minMax[0], minMax[2]);
    395         byte[] subnetmask = or(subnetmask1, subnetmask2);
    396         return Collections.singleton(ipWithSubnetMask(ip, subnetmask));
    397     }
    398 
    399     /**
    400      * Concatenates the IP address with its subnet mask.
    401      *
    402      * @param ip         The IP address.
    403      * @param subnetMask Its subnet mask.
    404      * @return The concatenated IP address with its subnet mask.
    405      */
    406     private byte[] ipWithSubnetMask(byte[] ip, byte[] subnetMask)
    407     {
    408         int ipLength = ip.length;
    409         byte[] temp = new byte[ipLength * 2];
    410         System.arraycopy(ip, 0, temp, 0, ipLength);
    411         System.arraycopy(subnetMask, 0, temp, ipLength, ipLength);
    412         return temp;
    413     }
    414 
    415     /**
    416      * Splits the IP addresses and their subnet mask.
    417      *
    418      * @param ipWithSubmask1 The first IP address with the subnet mask.
    419      * @param ipWithSubmask2 The second IP address with the subnet mask.
    420      * @return An array with two elements. Each element contains the IP address
    421      *         and the subnet mask in this order.
    422      */
    423     private byte[][] extractIPsAndSubnetMasks(
    424         byte[] ipWithSubmask1,
    425         byte[] ipWithSubmask2)
    426     {
    427         int ipLength = ipWithSubmask1.length / 2;
    428         byte ip1[] = new byte[ipLength];
    429         byte subnetmask1[] = new byte[ipLength];
    430         System.arraycopy(ipWithSubmask1, 0, ip1, 0, ipLength);
    431         System.arraycopy(ipWithSubmask1, ipLength, subnetmask1, 0, ipLength);
    432 
    433         byte ip2[] = new byte[ipLength];
    434         byte subnetmask2[] = new byte[ipLength];
    435         System.arraycopy(ipWithSubmask2, 0, ip2, 0, ipLength);
    436         System.arraycopy(ipWithSubmask2, ipLength, subnetmask2, 0, ipLength);
    437         return new byte[][]
    438             {ip1, subnetmask1, ip2, subnetmask2};
    439     }
    440 
    441     /**
    442      * Based on the two IP addresses and their subnet masks the IP range is
    443      * computed for each IP address - subnet mask pair and returned as the
    444      * minimum IP address and the maximum address of the range.
    445      *
    446      * @param ip1         The first IP address.
    447      * @param subnetmask1 The subnet mask of the first IP address.
    448      * @param ip2         The second IP address.
    449      * @param subnetmask2 The subnet mask of the second IP address.
    450      * @return A array with two elements. The first/second element contains the
    451      *         min and max IP address of the first/second IP address and its
    452      *         subnet mask.
    453      */
    454     private byte[][] minMaxIPs(
    455         byte[] ip1,
    456         byte[] subnetmask1,
    457         byte[] ip2,
    458         byte[] subnetmask2)
    459     {
    460         int ipLength = ip1.length;
    461         byte[] min1 = new byte[ipLength];
    462         byte[] max1 = new byte[ipLength];
    463 
    464         byte[] min2 = new byte[ipLength];
    465         byte[] max2 = new byte[ipLength];
    466 
    467         for (int i = 0; i < ipLength; i++)
    468         {
    469             min1[i] = (byte)(ip1[i] & subnetmask1[i]);
    470             max1[i] = (byte)(ip1[i] & subnetmask1[i] | ~subnetmask1[i]);
    471 
    472             min2[i] = (byte)(ip2[i] & subnetmask2[i]);
    473             max2[i] = (byte)(ip2[i] & subnetmask2[i] | ~subnetmask2[i]);
    474         }
    475 
    476         return new byte[][]{min1, max1, min2, max2};
    477     }
    478 
    479     private void checkPermittedEmail(Set permitted, String email)
    480         throws PKIXNameConstraintValidatorException
    481     {
    482         if (permitted == null)
    483         {
    484             return;
    485         }
    486 
    487         Iterator it = permitted.iterator();
    488 
    489         while (it.hasNext())
    490         {
    491             String str = ((String)it.next());
    492 
    493             if (emailIsConstrained(email, str))
    494             {
    495                 return;
    496             }
    497         }
    498 
    499         if (email.length() == 0 && permitted.size() == 0)
    500         {
    501             return;
    502         }
    503 
    504         throw new PKIXNameConstraintValidatorException(
    505             "Subject email address is not from a permitted subtree.");
    506     }
    507 
    508     private void checkExcludedEmail(Set excluded, String email)
    509         throws PKIXNameConstraintValidatorException
    510     {
    511         if (excluded.isEmpty())
    512         {
    513             return;
    514         }
    515 
    516         Iterator it = excluded.iterator();
    517 
    518         while (it.hasNext())
    519         {
    520             String str = (String)it.next();
    521 
    522             if (emailIsConstrained(email, str))
    523             {
    524                 throw new PKIXNameConstraintValidatorException(
    525                     "Email address is from an excluded subtree.");
    526             }
    527         }
    528     }
    529 
    530     /**
    531      * Checks if the IP <code>ip</code> is included in the permitted set
    532      * <code>permitted</code>.
    533      *
    534      * @param permitted A <code>Set</code> of permitted IP addresses with
    535      *                  their subnet mask as byte arrays.
    536      * @param ip        The IP address.
    537      * @throws PKIXNameConstraintValidatorException
    538      *          if the IP is not permitted.
    539      */
    540     private void checkPermittedIP(Set permitted, byte[] ip)
    541         throws PKIXNameConstraintValidatorException
    542     {
    543         if (permitted == null)
    544         {
    545             return;
    546         }
    547 
    548         Iterator it = permitted.iterator();
    549 
    550         while (it.hasNext())
    551         {
    552             byte[] ipWithSubnet = (byte[])it.next();
    553 
    554             if (isIPConstrained(ip, ipWithSubnet))
    555             {
    556                 return;
    557             }
    558         }
    559         if (ip.length == 0 && permitted.size() == 0)
    560         {
    561             return;
    562         }
    563         throw new PKIXNameConstraintValidatorException(
    564             "IP is not from a permitted subtree.");
    565     }
    566 
    567     /**
    568      * Checks if the IP <code>ip</code> is included in the excluded set
    569      * <code>excluded</code>.
    570      *
    571      * @param excluded A <code>Set</code> of excluded IP addresses with their
    572      *                 subnet mask as byte arrays.
    573      * @param ip       The IP address.
    574      * @throws PKIXNameConstraintValidatorException
    575      *          if the IP is excluded.
    576      */
    577     private void checkExcludedIP(Set excluded, byte[] ip)
    578         throws PKIXNameConstraintValidatorException
    579     {
    580         if (excluded.isEmpty())
    581         {
    582             return;
    583         }
    584 
    585         Iterator it = excluded.iterator();
    586 
    587         while (it.hasNext())
    588         {
    589             byte[] ipWithSubnet = (byte[])it.next();
    590 
    591             if (isIPConstrained(ip, ipWithSubnet))
    592             {
    593                 throw new PKIXNameConstraintValidatorException(
    594                     "IP is from an excluded subtree.");
    595             }
    596         }
    597     }
    598 
    599     /**
    600      * Checks if the IP address <code>ip</code> is constrained by
    601      * <code>constraint</code>.
    602      *
    603      * @param ip         The IP address.
    604      * @param constraint The constraint. This is an IP address concatenated with
    605      *                   its subnetmask.
    606      * @return <code>true</code> if constrained, <code>false</code>
    607      *         otherwise.
    608      */
    609     private boolean isIPConstrained(byte ip[], byte[] constraint)
    610     {
    611         int ipLength = ip.length;
    612 
    613         if (ipLength != (constraint.length / 2))
    614         {
    615             return false;
    616         }
    617 
    618         byte[] subnetMask = new byte[ipLength];
    619         System.arraycopy(constraint, ipLength, subnetMask, 0, ipLength);
    620 
    621         byte[] permittedSubnetAddress = new byte[ipLength];
    622 
    623         byte[] ipSubnetAddress = new byte[ipLength];
    624 
    625         // the resulting IP address by applying the subnet mask
    626         for (int i = 0; i < ipLength; i++)
    627         {
    628             permittedSubnetAddress[i] = (byte)(constraint[i] & subnetMask[i]);
    629             ipSubnetAddress[i] = (byte)(ip[i] & subnetMask[i]);
    630         }
    631 
    632         return Arrays.areEqual(permittedSubnetAddress, ipSubnetAddress);
    633     }
    634 
    635     private boolean emailIsConstrained(String email, String constraint)
    636     {
    637         String sub = email.substring(email.indexOf('@') + 1);
    638         // a particular mailbox or @domain
    639         if (constraint.indexOf('@') != -1)
    640         {
    641             if (email.equalsIgnoreCase(constraint))
    642             {
    643                 return true;
    644             }
    645             if (sub.equalsIgnoreCase(constraint.substring(1)))
    646             {
    647                 return true;
    648             }
    649         }
    650         // on particular host
    651         else if (!(constraint.charAt(0) == '.'))
    652         {
    653             if (sub.equalsIgnoreCase(constraint))
    654             {
    655                 return true;
    656             }
    657         }
    658         // address in sub domain
    659         else if (withinDomain(sub, constraint))
    660         {
    661             return true;
    662         }
    663         return false;
    664     }
    665 
    666     private boolean withinDomain(String testDomain, String domain)
    667     {
    668         String tempDomain = domain;
    669         if (tempDomain.startsWith("."))
    670         {
    671             tempDomain = tempDomain.substring(1);
    672         }
    673         String[] domainParts = Strings.split(tempDomain, '.');
    674         String[] testDomainParts = Strings.split(testDomain, '.');
    675         // must have at least one subdomain
    676         if (testDomainParts.length <= domainParts.length)
    677         {
    678             return false;
    679         }
    680         int d = testDomainParts.length - domainParts.length;
    681         for (int i = -1; i < domainParts.length; i++)
    682         {
    683             if (i == -1)
    684             {
    685                 if (testDomainParts[i + d].equals(""))
    686                 {
    687                     return false;
    688                 }
    689             }
    690             else if (!domainParts[i].equalsIgnoreCase(testDomainParts[i + d]))
    691             {
    692                 return false;
    693             }
    694         }
    695         return true;
    696     }
    697 
    698     private void checkPermittedDNS(Set permitted, String dns)
    699         throws PKIXNameConstraintValidatorException
    700     {
    701         if (permitted == null)
    702         {
    703             return;
    704         }
    705 
    706         Iterator it = permitted.iterator();
    707 
    708         while (it.hasNext())
    709         {
    710             String str = ((String)it.next());
    711 
    712             // is sub domain
    713             if (withinDomain(dns, str) || dns.equalsIgnoreCase(str))
    714             {
    715                 return;
    716             }
    717         }
    718         if (dns.length() == 0 && permitted.size() == 0)
    719         {
    720             return;
    721         }
    722         throw new PKIXNameConstraintValidatorException(
    723             "DNS is not from a permitted subtree.");
    724     }
    725 
    726     private void checkExcludedDNS(Set excluded, String dns)
    727         throws PKIXNameConstraintValidatorException
    728     {
    729         if (excluded.isEmpty())
    730         {
    731             return;
    732         }
    733 
    734         Iterator it = excluded.iterator();
    735 
    736         while (it.hasNext())
    737         {
    738             String str = ((String)it.next());
    739 
    740             // is sub domain or the same
    741             if (withinDomain(dns, str) || dns.equalsIgnoreCase(str))
    742             {
    743                 throw new PKIXNameConstraintValidatorException(
    744                     "DNS is from an excluded subtree.");
    745             }
    746         }
    747     }
    748 
    749     /**
    750      * The common part of <code>email1</code> and <code>email2</code> is
    751      * added to the union <code>union</code>. If <code>email1</code> and
    752      * <code>email2</code> have nothing in common they are added both.
    753      *
    754      * @param email1 Email address constraint 1.
    755      * @param email2 Email address constraint 2.
    756      * @param union  The union.
    757      */
    758     private void unionEmail(String email1, String email2, Set union)
    759     {
    760         // email1 is a particular address
    761         if (email1.indexOf('@') != -1)
    762         {
    763             String _sub = email1.substring(email1.indexOf('@') + 1);
    764             // both are a particular mailbox
    765             if (email2.indexOf('@') != -1)
    766             {
    767                 if (email1.equalsIgnoreCase(email2))
    768                 {
    769                     union.add(email1);
    770                 }
    771                 else
    772                 {
    773                     union.add(email1);
    774                     union.add(email2);
    775                 }
    776             }
    777             // email2 specifies a domain
    778             else if (email2.startsWith("."))
    779             {
    780                 if (withinDomain(_sub, email2))
    781                 {
    782                     union.add(email2);
    783                 }
    784                 else
    785                 {
    786                     union.add(email1);
    787                     union.add(email2);
    788                 }
    789             }
    790             // email2 specifies a particular host
    791             else
    792             {
    793                 if (_sub.equalsIgnoreCase(email2))
    794                 {
    795                     union.add(email2);
    796                 }
    797                 else
    798                 {
    799                     union.add(email1);
    800                     union.add(email2);
    801                 }
    802             }
    803         }
    804         // email1 specifies a domain
    805         else if (email1.startsWith("."))
    806         {
    807             if (email2.indexOf('@') != -1)
    808             {
    809                 String _sub = email2.substring(email1.indexOf('@') + 1);
    810                 if (withinDomain(_sub, email1))
    811                 {
    812                     union.add(email1);
    813                 }
    814                 else
    815                 {
    816                     union.add(email1);
    817                     union.add(email2);
    818                 }
    819             }
    820             // email2 specifies a domain
    821             else if (email2.startsWith("."))
    822             {
    823                 if (withinDomain(email1, email2)
    824                     || email1.equalsIgnoreCase(email2))
    825                 {
    826                     union.add(email2);
    827                 }
    828                 else if (withinDomain(email2, email1))
    829                 {
    830                     union.add(email1);
    831                 }
    832                 else
    833                 {
    834                     union.add(email1);
    835                     union.add(email2);
    836                 }
    837             }
    838             else
    839             {
    840                 if (withinDomain(email2, email1))
    841                 {
    842                     union.add(email1);
    843                 }
    844                 else
    845                 {
    846                     union.add(email1);
    847                     union.add(email2);
    848                 }
    849             }
    850         }
    851         // email specifies a host
    852         else
    853         {
    854             if (email2.indexOf('@') != -1)
    855             {
    856                 String _sub = email2.substring(email1.indexOf('@') + 1);
    857                 if (_sub.equalsIgnoreCase(email1))
    858                 {
    859                     union.add(email1);
    860                 }
    861                 else
    862                 {
    863                     union.add(email1);
    864                     union.add(email2);
    865                 }
    866             }
    867             // email2 specifies a domain
    868             else if (email2.startsWith("."))
    869             {
    870                 if (withinDomain(email1, email2))
    871                 {
    872                     union.add(email2);
    873                 }
    874                 else
    875                 {
    876                     union.add(email1);
    877                     union.add(email2);
    878                 }
    879             }
    880             // email2 specifies a particular host
    881             else
    882             {
    883                 if (email1.equalsIgnoreCase(email2))
    884                 {
    885                     union.add(email1);
    886                 }
    887                 else
    888                 {
    889                     union.add(email1);
    890                     union.add(email2);
    891                 }
    892             }
    893         }
    894     }
    895 
    896     private void unionURI(String email1, String email2, Set union)
    897     {
    898         // email1 is a particular address
    899         if (email1.indexOf('@') != -1)
    900         {
    901             String _sub = email1.substring(email1.indexOf('@') + 1);
    902             // both are a particular mailbox
    903             if (email2.indexOf('@') != -1)
    904             {
    905                 if (email1.equalsIgnoreCase(email2))
    906                 {
    907                     union.add(email1);
    908                 }
    909                 else
    910                 {
    911                     union.add(email1);
    912                     union.add(email2);
    913                 }
    914             }
    915             // email2 specifies a domain
    916             else if (email2.startsWith("."))
    917             {
    918                 if (withinDomain(_sub, email2))
    919                 {
    920                     union.add(email2);
    921                 }
    922                 else
    923                 {
    924                     union.add(email1);
    925                     union.add(email2);
    926                 }
    927             }
    928             // email2 specifies a particular host
    929             else
    930             {
    931                 if (_sub.equalsIgnoreCase(email2))
    932                 {
    933                     union.add(email2);
    934                 }
    935                 else
    936                 {
    937                     union.add(email1);
    938                     union.add(email2);
    939                 }
    940             }
    941         }
    942         // email1 specifies a domain
    943         else if (email1.startsWith("."))
    944         {
    945             if (email2.indexOf('@') != -1)
    946             {
    947                 String _sub = email2.substring(email1.indexOf('@') + 1);
    948                 if (withinDomain(_sub, email1))
    949                 {
    950                     union.add(email1);
    951                 }
    952                 else
    953                 {
    954                     union.add(email1);
    955                     union.add(email2);
    956                 }
    957             }
    958             // email2 specifies a domain
    959             else if (email2.startsWith("."))
    960             {
    961                 if (withinDomain(email1, email2)
    962                     || email1.equalsIgnoreCase(email2))
    963                 {
    964                     union.add(email2);
    965                 }
    966                 else if (withinDomain(email2, email1))
    967                 {
    968                     union.add(email1);
    969                 }
    970                 else
    971                 {
    972                     union.add(email1);
    973                     union.add(email2);
    974                 }
    975             }
    976             else
    977             {
    978                 if (withinDomain(email2, email1))
    979                 {
    980                     union.add(email1);
    981                 }
    982                 else
    983                 {
    984                     union.add(email1);
    985                     union.add(email2);
    986                 }
    987             }
    988         }
    989         // email specifies a host
    990         else
    991         {
    992             if (email2.indexOf('@') != -1)
    993             {
    994                 String _sub = email2.substring(email1.indexOf('@') + 1);
    995                 if (_sub.equalsIgnoreCase(email1))
    996                 {
    997                     union.add(email1);
    998                 }
    999                 else
   1000                 {
   1001                     union.add(email1);
   1002                     union.add(email2);
   1003                 }
   1004             }
   1005             // email2 specifies a domain
   1006             else if (email2.startsWith("."))
   1007             {
   1008                 if (withinDomain(email1, email2))
   1009                 {
   1010                     union.add(email2);
   1011                 }
   1012                 else
   1013                 {
   1014                     union.add(email1);
   1015                     union.add(email2);
   1016                 }
   1017             }
   1018             // email2 specifies a particular host
   1019             else
   1020             {
   1021                 if (email1.equalsIgnoreCase(email2))
   1022                 {
   1023                     union.add(email1);
   1024                 }
   1025                 else
   1026                 {
   1027                     union.add(email1);
   1028                     union.add(email2);
   1029                 }
   1030             }
   1031         }
   1032     }
   1033 
   1034     private Set intersectDNS(Set permitted, Set dnss)
   1035     {
   1036         Set intersect = new HashSet();
   1037         for (Iterator it = dnss.iterator(); it.hasNext();)
   1038         {
   1039             String dns = extractNameAsString(((GeneralSubtree)it.next())
   1040                 .getBase());
   1041             if (permitted == null)
   1042             {
   1043                 if (dns != null)
   1044                 {
   1045                     intersect.add(dns);
   1046                 }
   1047             }
   1048             else
   1049             {
   1050                 Iterator _iter = permitted.iterator();
   1051                 while (_iter.hasNext())
   1052                 {
   1053                     String _permitted = (String)_iter.next();
   1054 
   1055                     if (withinDomain(_permitted, dns))
   1056                     {
   1057                         intersect.add(_permitted);
   1058                     }
   1059                     else if (withinDomain(dns, _permitted))
   1060                     {
   1061                         intersect.add(dns);
   1062                     }
   1063                 }
   1064             }
   1065         }
   1066 
   1067         return intersect;
   1068     }
   1069 
   1070     protected Set unionDNS(Set excluded, String dns)
   1071     {
   1072         if (excluded.isEmpty())
   1073         {
   1074             if (dns == null)
   1075             {
   1076                 return excluded;
   1077             }
   1078             excluded.add(dns);
   1079 
   1080             return excluded;
   1081         }
   1082         else
   1083         {
   1084             Set union = new HashSet();
   1085 
   1086             Iterator _iter = excluded.iterator();
   1087             while (_iter.hasNext())
   1088             {
   1089                 String _permitted = (String)_iter.next();
   1090 
   1091                 if (withinDomain(_permitted, dns))
   1092                 {
   1093                     union.add(dns);
   1094                 }
   1095                 else if (withinDomain(dns, _permitted))
   1096                 {
   1097                     union.add(_permitted);
   1098                 }
   1099                 else
   1100                 {
   1101                     union.add(_permitted);
   1102                     union.add(dns);
   1103                 }
   1104             }
   1105 
   1106             return union;
   1107         }
   1108     }
   1109 
   1110     /**
   1111      * The most restricting part from <code>email1</code> and
   1112      * <code>email2</code> is added to the intersection <code>intersect</code>.
   1113      *
   1114      * @param email1    Email address constraint 1.
   1115      * @param email2    Email address constraint 2.
   1116      * @param intersect The intersection.
   1117      */
   1118     private void intersectEmail(String email1, String email2, Set intersect)
   1119     {
   1120         // email1 is a particular address
   1121         if (email1.indexOf('@') != -1)
   1122         {
   1123             String _sub = email1.substring(email1.indexOf('@') + 1);
   1124             // both are a particular mailbox
   1125             if (email2.indexOf('@') != -1)
   1126             {
   1127                 if (email1.equalsIgnoreCase(email2))
   1128                 {
   1129                     intersect.add(email1);
   1130                 }
   1131             }
   1132             // email2 specifies a domain
   1133             else if (email2.startsWith("."))
   1134             {
   1135                 if (withinDomain(_sub, email2))
   1136                 {
   1137                     intersect.add(email1);
   1138                 }
   1139             }
   1140             // email2 specifies a particular host
   1141             else
   1142             {
   1143                 if (_sub.equalsIgnoreCase(email2))
   1144                 {
   1145                     intersect.add(email1);
   1146                 }
   1147             }
   1148         }
   1149         // email specifies a domain
   1150         else if (email1.startsWith("."))
   1151         {
   1152             if (email2.indexOf('@') != -1)
   1153             {
   1154                 String _sub = email2.substring(email1.indexOf('@') + 1);
   1155                 if (withinDomain(_sub, email1))
   1156                 {
   1157                     intersect.add(email2);
   1158                 }
   1159             }
   1160             // email2 specifies a domain
   1161             else if (email2.startsWith("."))
   1162             {
   1163                 if (withinDomain(email1, email2)
   1164                     || email1.equalsIgnoreCase(email2))
   1165                 {
   1166                     intersect.add(email1);
   1167                 }
   1168                 else if (withinDomain(email2, email1))
   1169                 {
   1170                     intersect.add(email2);
   1171                 }
   1172             }
   1173             else
   1174             {
   1175                 if (withinDomain(email2, email1))
   1176                 {
   1177                     intersect.add(email2);
   1178                 }
   1179             }
   1180         }
   1181         // email1 specifies a host
   1182         else
   1183         {
   1184             if (email2.indexOf('@') != -1)
   1185             {
   1186                 String _sub = email2.substring(email2.indexOf('@') + 1);
   1187                 if (_sub.equalsIgnoreCase(email1))
   1188                 {
   1189                     intersect.add(email2);
   1190                 }
   1191             }
   1192             // email2 specifies a domain
   1193             else if (email2.startsWith("."))
   1194             {
   1195                 if (withinDomain(email1, email2))
   1196                 {
   1197                     intersect.add(email1);
   1198                 }
   1199             }
   1200             // email2 specifies a particular host
   1201             else
   1202             {
   1203                 if (email1.equalsIgnoreCase(email2))
   1204                 {
   1205                     intersect.add(email1);
   1206                 }
   1207             }
   1208         }
   1209     }
   1210 
   1211     private void checkExcludedURI(Set excluded, String uri)
   1212         throws PKIXNameConstraintValidatorException
   1213     {
   1214         if (excluded.isEmpty())
   1215         {
   1216             return;
   1217         }
   1218 
   1219         Iterator it = excluded.iterator();
   1220 
   1221         while (it.hasNext())
   1222         {
   1223             String str = ((String)it.next());
   1224 
   1225             if (isUriConstrained(uri, str))
   1226             {
   1227                 throw new PKIXNameConstraintValidatorException(
   1228                     "URI is from an excluded subtree.");
   1229             }
   1230         }
   1231     }
   1232 
   1233     private Set intersectURI(Set permitted, Set uris)
   1234     {
   1235         Set intersect = new HashSet();
   1236         for (Iterator it = uris.iterator(); it.hasNext();)
   1237         {
   1238             String uri = extractNameAsString(((GeneralSubtree)it.next())
   1239                 .getBase());
   1240             if (permitted == null)
   1241             {
   1242                 if (uri != null)
   1243                 {
   1244                     intersect.add(uri);
   1245                 }
   1246             }
   1247             else
   1248             {
   1249                 Iterator _iter = permitted.iterator();
   1250                 while (_iter.hasNext())
   1251                 {
   1252                     String _permitted = (String)_iter.next();
   1253                     intersectURI(_permitted, uri, intersect);
   1254                 }
   1255             }
   1256         }
   1257         return intersect;
   1258     }
   1259 
   1260     private Set unionURI(Set excluded, String uri)
   1261     {
   1262         if (excluded.isEmpty())
   1263         {
   1264             if (uri == null)
   1265             {
   1266                 return excluded;
   1267             }
   1268             excluded.add(uri);
   1269 
   1270             return excluded;
   1271         }
   1272         else
   1273         {
   1274             Set union = new HashSet();
   1275 
   1276             Iterator _iter = excluded.iterator();
   1277             while (_iter.hasNext())
   1278             {
   1279                 String _excluded = (String)_iter.next();
   1280 
   1281                 unionURI(_excluded, uri, union);
   1282             }
   1283 
   1284             return union;
   1285         }
   1286     }
   1287 
   1288     private void intersectURI(String email1, String email2, Set intersect)
   1289     {
   1290         // email1 is a particular address
   1291         if (email1.indexOf('@') != -1)
   1292         {
   1293             String _sub = email1.substring(email1.indexOf('@') + 1);
   1294             // both are a particular mailbox
   1295             if (email2.indexOf('@') != -1)
   1296             {
   1297                 if (email1.equalsIgnoreCase(email2))
   1298                 {
   1299                     intersect.add(email1);
   1300                 }
   1301             }
   1302             // email2 specifies a domain
   1303             else if (email2.startsWith("."))
   1304             {
   1305                 if (withinDomain(_sub, email2))
   1306                 {
   1307                     intersect.add(email1);
   1308                 }
   1309             }
   1310             // email2 specifies a particular host
   1311             else
   1312             {
   1313                 if (_sub.equalsIgnoreCase(email2))
   1314                 {
   1315                     intersect.add(email1);
   1316                 }
   1317             }
   1318         }
   1319         // email specifies a domain
   1320         else if (email1.startsWith("."))
   1321         {
   1322             if (email2.indexOf('@') != -1)
   1323             {
   1324                 String _sub = email2.substring(email1.indexOf('@') + 1);
   1325                 if (withinDomain(_sub, email1))
   1326                 {
   1327                     intersect.add(email2);
   1328                 }
   1329             }
   1330             // email2 specifies a domain
   1331             else if (email2.startsWith("."))
   1332             {
   1333                 if (withinDomain(email1, email2)
   1334                     || email1.equalsIgnoreCase(email2))
   1335                 {
   1336                     intersect.add(email1);
   1337                 }
   1338                 else if (withinDomain(email2, email1))
   1339                 {
   1340                     intersect.add(email2);
   1341                 }
   1342             }
   1343             else
   1344             {
   1345                 if (withinDomain(email2, email1))
   1346                 {
   1347                     intersect.add(email2);
   1348                 }
   1349             }
   1350         }
   1351         // email1 specifies a host
   1352         else
   1353         {
   1354             if (email2.indexOf('@') != -1)
   1355             {
   1356                 String _sub = email2.substring(email2.indexOf('@') + 1);
   1357                 if (_sub.equalsIgnoreCase(email1))
   1358                 {
   1359                     intersect.add(email2);
   1360                 }
   1361             }
   1362             // email2 specifies a domain
   1363             else if (email2.startsWith("."))
   1364             {
   1365                 if (withinDomain(email1, email2))
   1366                 {
   1367                     intersect.add(email1);
   1368                 }
   1369             }
   1370             // email2 specifies a particular host
   1371             else
   1372             {
   1373                 if (email1.equalsIgnoreCase(email2))
   1374                 {
   1375                     intersect.add(email1);
   1376                 }
   1377             }
   1378         }
   1379     }
   1380 
   1381     private void checkPermittedURI(Set permitted, String uri)
   1382         throws PKIXNameConstraintValidatorException
   1383     {
   1384         if (permitted == null)
   1385         {
   1386             return;
   1387         }
   1388 
   1389         Iterator it = permitted.iterator();
   1390 
   1391         while (it.hasNext())
   1392         {
   1393             String str = ((String)it.next());
   1394 
   1395             if (isUriConstrained(uri, str))
   1396             {
   1397                 return;
   1398             }
   1399         }
   1400         if (uri.length() == 0 && permitted.size() == 0)
   1401         {
   1402             return;
   1403         }
   1404         throw new PKIXNameConstraintValidatorException(
   1405             "URI is not from a permitted subtree.");
   1406     }
   1407 
   1408     private boolean isUriConstrained(String uri, String constraint)
   1409     {
   1410         String host = extractHostFromURL(uri);
   1411         // a host
   1412         if (!constraint.startsWith("."))
   1413         {
   1414             if (host.equalsIgnoreCase(constraint))
   1415             {
   1416                 return true;
   1417             }
   1418         }
   1419 
   1420         // in sub domain or domain
   1421         else if (withinDomain(host, constraint))
   1422         {
   1423             return true;
   1424         }
   1425 
   1426         return false;
   1427     }
   1428 
   1429     private static String extractHostFromURL(String url)
   1430     {
   1431         // see RFC 1738
   1432         // remove ':' after protocol, e.g. http:
   1433         String sub = url.substring(url.indexOf(':') + 1);
   1434         // extract host from Common Internet Scheme Syntax, e.g. http://
   1435         if (sub.indexOf("//") != -1)
   1436         {
   1437             sub = sub.substring(sub.indexOf("//") + 2);
   1438         }
   1439         // first remove port, e.g. http://test.com:21
   1440         if (sub.lastIndexOf(':') != -1)
   1441         {
   1442             sub = sub.substring(0, sub.lastIndexOf(':'));
   1443         }
   1444         // remove user and password, e.g. http://john:password@test.com
   1445         sub = sub.substring(sub.indexOf(':') + 1);
   1446         sub = sub.substring(sub.indexOf('@') + 1);
   1447         // remove local parts, e.g. http://test.com/bla
   1448         if (sub.indexOf('/') != -1)
   1449         {
   1450             sub = sub.substring(0, sub.indexOf('/'));
   1451         }
   1452         return sub;
   1453     }
   1454 
   1455     /**
   1456      * Checks if the given GeneralName is in the permitted set.
   1457      *
   1458      * @param name The GeneralName
   1459      * @throws PKIXNameConstraintValidatorException
   1460      *          If the <code>name</code>
   1461      */
   1462     public void checkPermitted(GeneralName name)
   1463         throws PKIXNameConstraintValidatorException
   1464     {
   1465         switch (name.getTagNo())
   1466         {
   1467             case 1:
   1468                 checkPermittedEmail(permittedSubtreesEmail,
   1469                     extractNameAsString(name));
   1470                 break;
   1471             case 2:
   1472                 checkPermittedDNS(permittedSubtreesDNS, DERIA5String.getInstance(
   1473                     name.getName()).getString());
   1474                 break;
   1475             case 4:
   1476                 checkPermittedDN(ASN1Sequence.getInstance(name.getName()
   1477                     .toASN1Primitive()));
   1478                 break;
   1479             case 6:
   1480                 checkPermittedURI(permittedSubtreesURI, DERIA5String.getInstance(
   1481                     name.getName()).getString());
   1482                 break;
   1483             case 7:
   1484                 byte[] ip = ASN1OctetString.getInstance(name.getName()).getOctets();
   1485 
   1486                 checkPermittedIP(permittedSubtreesIP, ip);
   1487         }
   1488     }
   1489 
   1490     /**
   1491      * Check if the given GeneralName is contained in the excluded set.
   1492      *
   1493      * @param name The GeneralName.
   1494      * @throws PKIXNameConstraintValidatorException
   1495      *          If the <code>name</code> is
   1496      *          excluded.
   1497      */
   1498     public void checkExcluded(GeneralName name)
   1499         throws PKIXNameConstraintValidatorException
   1500     {
   1501         switch (name.getTagNo())
   1502         {
   1503             case 1:
   1504                 checkExcludedEmail(excludedSubtreesEmail, extractNameAsString(name));
   1505                 break;
   1506             case 2:
   1507                 checkExcludedDNS(excludedSubtreesDNS, DERIA5String.getInstance(
   1508                     name.getName()).getString());
   1509                 break;
   1510             case 4:
   1511                 checkExcludedDN(ASN1Sequence.getInstance(name.getName()
   1512                     .toASN1Primitive()));
   1513                 break;
   1514             case 6:
   1515                 checkExcludedURI(excludedSubtreesURI, DERIA5String.getInstance(
   1516                     name.getName()).getString());
   1517                 break;
   1518             case 7:
   1519                 byte[] ip = ASN1OctetString.getInstance(name.getName()).getOctets();
   1520 
   1521                 checkExcludedIP(excludedSubtreesIP, ip);
   1522         }
   1523     }
   1524 
   1525     public void intersectPermittedSubtree(GeneralSubtree permitted)
   1526     {
   1527         intersectPermittedSubtree(new GeneralSubtree[] { permitted });
   1528     }
   1529 
   1530     /**
   1531      * Updates the permitted set of these name constraints with the intersection
   1532      * with the given subtree.
   1533      *
   1534      * @param permitted The permitted subtrees
   1535      */
   1536 
   1537     public void intersectPermittedSubtree(GeneralSubtree[] permitted)
   1538     {
   1539         Map subtreesMap = new HashMap();
   1540 
   1541         // group in sets in a map ordered by tag no.
   1542         for (int i = 0; i != permitted.length; i++)
   1543         {
   1544             GeneralSubtree subtree = permitted[i];
   1545             Integer tagNo = Integers.valueOf(subtree.getBase().getTagNo());
   1546             if (subtreesMap.get(tagNo) == null)
   1547             {
   1548                 subtreesMap.put(tagNo, new HashSet());
   1549             }
   1550             ((Set)subtreesMap.get(tagNo)).add(subtree);
   1551         }
   1552 
   1553         for (Iterator it = subtreesMap.entrySet().iterator(); it.hasNext();)
   1554         {
   1555             Map.Entry entry = (Map.Entry)it.next();
   1556 
   1557             // go through all subtree groups
   1558             switch (((Integer)entry.getKey()).intValue())
   1559             {
   1560                 case 1:
   1561                     permittedSubtreesEmail = intersectEmail(permittedSubtreesEmail,
   1562                         (Set)entry.getValue());
   1563                     break;
   1564                 case 2:
   1565                     permittedSubtreesDNS = intersectDNS(permittedSubtreesDNS,
   1566                         (Set)entry.getValue());
   1567                     break;
   1568                 case 4:
   1569                     permittedSubtreesDN = intersectDN(permittedSubtreesDN,
   1570                         (Set)entry.getValue());
   1571                     break;
   1572                 case 6:
   1573                     permittedSubtreesURI = intersectURI(permittedSubtreesURI,
   1574                         (Set)entry.getValue());
   1575                     break;
   1576                 case 7:
   1577                     permittedSubtreesIP = intersectIP(permittedSubtreesIP,
   1578                         (Set)entry.getValue());
   1579             }
   1580         }
   1581     }
   1582 
   1583     private String extractNameAsString(GeneralName name)
   1584     {
   1585         return DERIA5String.getInstance(name.getName()).getString();
   1586     }
   1587 
   1588     public void intersectEmptyPermittedSubtree(int nameType)
   1589     {
   1590         switch (nameType)
   1591         {
   1592         case 1:
   1593             permittedSubtreesEmail = new HashSet();
   1594             break;
   1595         case 2:
   1596             permittedSubtreesDNS = new HashSet();
   1597             break;
   1598         case 4:
   1599             permittedSubtreesDN = new HashSet();
   1600             break;
   1601         case 6:
   1602             permittedSubtreesURI = new HashSet();
   1603             break;
   1604         case 7:
   1605             permittedSubtreesIP = new HashSet();
   1606         }
   1607     }
   1608 
   1609     /**
   1610      * Adds a subtree to the excluded set of these name constraints.
   1611      *
   1612      * @param subtree A subtree with an excluded GeneralName.
   1613      */
   1614     public void addExcludedSubtree(GeneralSubtree subtree)
   1615     {
   1616         GeneralName base = subtree.getBase();
   1617 
   1618         switch (base.getTagNo())
   1619         {
   1620             case 1:
   1621                 excludedSubtreesEmail = unionEmail(excludedSubtreesEmail,
   1622                     extractNameAsString(base));
   1623                 break;
   1624             case 2:
   1625                 excludedSubtreesDNS = unionDNS(excludedSubtreesDNS,
   1626                     extractNameAsString(base));
   1627                 break;
   1628             case 4:
   1629                 excludedSubtreesDN = unionDN(excludedSubtreesDN,
   1630                     (ASN1Sequence)base.getName().toASN1Primitive());
   1631                 break;
   1632             case 6:
   1633                 excludedSubtreesURI = unionURI(excludedSubtreesURI,
   1634                     extractNameAsString(base));
   1635                 break;
   1636             case 7:
   1637                 excludedSubtreesIP = unionIP(excludedSubtreesIP, ASN1OctetString
   1638                     .getInstance(base.getName()).getOctets());
   1639                 break;
   1640         }
   1641     }
   1642 
   1643     /**
   1644      * Returns the maximum IP address.
   1645      *
   1646      * @param ip1 The first IP address.
   1647      * @param ip2 The second IP address.
   1648      * @return The maximum IP address.
   1649      */
   1650     private static byte[] max(byte[] ip1, byte[] ip2)
   1651     {
   1652         for (int i = 0; i < ip1.length; i++)
   1653         {
   1654             if ((ip1[i] & 0xFFFF) > (ip2[i] & 0xFFFF))
   1655             {
   1656                 return ip1;
   1657             }
   1658         }
   1659         return ip2;
   1660     }
   1661 
   1662     /**
   1663      * Returns the minimum IP address.
   1664      *
   1665      * @param ip1 The first IP address.
   1666      * @param ip2 The second IP address.
   1667      * @return The minimum IP address.
   1668      */
   1669     private static byte[] min(byte[] ip1, byte[] ip2)
   1670     {
   1671         for (int i = 0; i < ip1.length; i++)
   1672         {
   1673             if ((ip1[i] & 0xFFFF) < (ip2[i] & 0xFFFF))
   1674             {
   1675                 return ip1;
   1676             }
   1677         }
   1678         return ip2;
   1679     }
   1680 
   1681     /**
   1682      * Compares IP address <code>ip1</code> with <code>ip2</code>. If ip1
   1683      * is equal to ip2 0 is returned. If ip1 is bigger 1 is returned, -1
   1684      * otherwise.
   1685      *
   1686      * @param ip1 The first IP address.
   1687      * @param ip2 The second IP address.
   1688      * @return 0 if ip1 is equal to ip2, 1 if ip1 is bigger, -1 otherwise.
   1689      */
   1690     private static int compareTo(byte[] ip1, byte[] ip2)
   1691     {
   1692         if (Arrays.areEqual(ip1, ip2))
   1693         {
   1694             return 0;
   1695         }
   1696         if (Arrays.areEqual(max(ip1, ip2), ip1))
   1697         {
   1698             return 1;
   1699         }
   1700         return -1;
   1701     }
   1702 
   1703     /**
   1704      * Returns the logical OR of the IP addresses <code>ip1</code> and
   1705      * <code>ip2</code>.
   1706      *
   1707      * @param ip1 The first IP address.
   1708      * @param ip2 The second IP address.
   1709      * @return The OR of <code>ip1</code> and <code>ip2</code>.
   1710      */
   1711     private static byte[] or(byte[] ip1, byte[] ip2)
   1712     {
   1713         byte[] temp = new byte[ip1.length];
   1714         for (int i = 0; i < ip1.length; i++)
   1715         {
   1716             temp[i] = (byte)(ip1[i] | ip2[i]);
   1717         }
   1718         return temp;
   1719     }
   1720 
   1721     public int hashCode()
   1722     {
   1723         return hashCollection(excludedSubtreesDN)
   1724             + hashCollection(excludedSubtreesDNS)
   1725             + hashCollection(excludedSubtreesEmail)
   1726             + hashCollection(excludedSubtreesIP)
   1727             + hashCollection(excludedSubtreesURI)
   1728             + hashCollection(permittedSubtreesDN)
   1729             + hashCollection(permittedSubtreesDNS)
   1730             + hashCollection(permittedSubtreesEmail)
   1731             + hashCollection(permittedSubtreesIP)
   1732             + hashCollection(permittedSubtreesURI);
   1733     }
   1734 
   1735     private int hashCollection(Collection coll)
   1736     {
   1737         if (coll == null)
   1738         {
   1739             return 0;
   1740         }
   1741         int hash = 0;
   1742         Iterator it1 = coll.iterator();
   1743         while (it1.hasNext())
   1744         {
   1745             Object o = it1.next();
   1746             if (o instanceof byte[])
   1747             {
   1748                 hash += Arrays.hashCode((byte[])o);
   1749             }
   1750             else
   1751             {
   1752                 hash += o.hashCode();
   1753             }
   1754         }
   1755         return hash;
   1756     }
   1757 
   1758     public boolean equals(Object o)
   1759     {
   1760         if (!(o instanceof PKIXNameConstraintValidator))
   1761         {
   1762             return false;
   1763         }
   1764         PKIXNameConstraintValidator constraintValidator = (PKIXNameConstraintValidator)o;
   1765         return collectionsAreEqual(constraintValidator.excludedSubtreesDN, excludedSubtreesDN)
   1766             && collectionsAreEqual(constraintValidator.excludedSubtreesDNS, excludedSubtreesDNS)
   1767             && collectionsAreEqual(constraintValidator.excludedSubtreesEmail, excludedSubtreesEmail)
   1768             && collectionsAreEqual(constraintValidator.excludedSubtreesIP, excludedSubtreesIP)
   1769             && collectionsAreEqual(constraintValidator.excludedSubtreesURI, excludedSubtreesURI)
   1770             && collectionsAreEqual(constraintValidator.permittedSubtreesDN, permittedSubtreesDN)
   1771             && collectionsAreEqual(constraintValidator.permittedSubtreesDNS, permittedSubtreesDNS)
   1772             && collectionsAreEqual(constraintValidator.permittedSubtreesEmail, permittedSubtreesEmail)
   1773             && collectionsAreEqual(constraintValidator.permittedSubtreesIP, permittedSubtreesIP)
   1774             && collectionsAreEqual(constraintValidator.permittedSubtreesURI, permittedSubtreesURI);
   1775     }
   1776 
   1777     private boolean collectionsAreEqual(Collection coll1, Collection coll2)
   1778     {
   1779         if (coll1 == coll2)
   1780         {
   1781             return true;
   1782         }
   1783         if (coll1 == null || coll2 == null)
   1784         {
   1785             return false;
   1786         }
   1787         if (coll1.size() != coll2.size())
   1788         {
   1789             return false;
   1790         }
   1791         Iterator it1 = coll1.iterator();
   1792 
   1793         while (it1.hasNext())
   1794         {
   1795             Object a = it1.next();
   1796             Iterator it2 = coll2.iterator();
   1797             boolean found = false;
   1798             while (it2.hasNext())
   1799             {
   1800                 Object b = it2.next();
   1801                 if (equals(a, b))
   1802                 {
   1803                     found = true;
   1804                     break;
   1805                 }
   1806             }
   1807             if (!found)
   1808             {
   1809                 return false;
   1810             }
   1811         }
   1812         return true;
   1813     }
   1814 
   1815     private boolean equals(Object o1, Object o2)
   1816     {
   1817         if (o1 == o2)
   1818         {
   1819             return true;
   1820         }
   1821         if (o1 == null || o2 == null)
   1822         {
   1823             return false;
   1824         }
   1825         if (o1 instanceof byte[] && o2 instanceof byte[])
   1826         {
   1827             return Arrays.areEqual((byte[])o1, (byte[])o2);
   1828         }
   1829         else
   1830         {
   1831             return o1.equals(o2);
   1832         }
   1833     }
   1834 
   1835     /**
   1836      * Stringifies an IPv4 or v6 address with subnet mask.
   1837      *
   1838      * @param ip The IP with subnet mask.
   1839      * @return The stringified IP address.
   1840      */
   1841     private String stringifyIP(byte[] ip)
   1842     {
   1843         String temp = "";
   1844         for (int i = 0; i < ip.length / 2; i++)
   1845         {
   1846             temp += Integer.toString(ip[i] & 0x00FF) + ".";
   1847         }
   1848         temp = temp.substring(0, temp.length() - 1);
   1849         temp += "/";
   1850         for (int i = ip.length / 2; i < ip.length; i++)
   1851         {
   1852             temp += Integer.toString(ip[i] & 0x00FF) + ".";
   1853         }
   1854         temp = temp.substring(0, temp.length() - 1);
   1855         return temp;
   1856     }
   1857 
   1858     private String stringifyIPCollection(Set ips)
   1859     {
   1860         String temp = "";
   1861         temp += "[";
   1862         for (Iterator it = ips.iterator(); it.hasNext();)
   1863         {
   1864             temp += stringifyIP((byte[])it.next()) + ",";
   1865         }
   1866         if (temp.length() > 1)
   1867         {
   1868             temp = temp.substring(0, temp.length() - 1);
   1869         }
   1870         temp += "]";
   1871         return temp;
   1872     }
   1873 
   1874     public String toString()
   1875     {
   1876         String temp = "";
   1877         temp += "permitted:\n";
   1878         if (permittedSubtreesDN != null)
   1879         {
   1880             temp += "DN:\n";
   1881             temp += permittedSubtreesDN.toString() + "\n";
   1882         }
   1883         if (permittedSubtreesDNS != null)
   1884         {
   1885             temp += "DNS:\n";
   1886             temp += permittedSubtreesDNS.toString() + "\n";
   1887         }
   1888         if (permittedSubtreesEmail != null)
   1889         {
   1890             temp += "Email:\n";
   1891             temp += permittedSubtreesEmail.toString() + "\n";
   1892         }
   1893         if (permittedSubtreesURI != null)
   1894         {
   1895             temp += "URI:\n";
   1896             temp += permittedSubtreesURI.toString() + "\n";
   1897         }
   1898         if (permittedSubtreesIP != null)
   1899         {
   1900             temp += "IP:\n";
   1901             temp += stringifyIPCollection(permittedSubtreesIP) + "\n";
   1902         }
   1903         temp += "excluded:\n";
   1904         if (!excludedSubtreesDN.isEmpty())
   1905         {
   1906             temp += "DN:\n";
   1907             temp += excludedSubtreesDN.toString() + "\n";
   1908         }
   1909         if (!excludedSubtreesDNS.isEmpty())
   1910         {
   1911             temp += "DNS:\n";
   1912             temp += excludedSubtreesDNS.toString() + "\n";
   1913         }
   1914         if (!excludedSubtreesEmail.isEmpty())
   1915         {
   1916             temp += "Email:\n";
   1917             temp += excludedSubtreesEmail.toString() + "\n";
   1918         }
   1919         if (!excludedSubtreesURI.isEmpty())
   1920         {
   1921             temp += "URI:\n";
   1922             temp += excludedSubtreesURI.toString() + "\n";
   1923         }
   1924         if (!excludedSubtreesIP.isEmpty())
   1925         {
   1926             temp += "IP:\n";
   1927             temp += stringifyIPCollection(excludedSubtreesIP) + "\n";
   1928         }
   1929         return temp;
   1930     }
   1931 }
   1932