1package org.bouncycastle.jce.provider;
2
3import java.util.Collection;
4import java.util.Collections;
5import java.util.HashMap;
6import java.util.HashSet;
7import java.util.Iterator;
8import java.util.Map;
9import java.util.Set;
10
11import org.bouncycastle.asn1.ASN1OctetString;
12import org.bouncycastle.asn1.ASN1Sequence;
13import org.bouncycastle.asn1.DERIA5String;
14import org.bouncycastle.asn1.x509.GeneralName;
15import org.bouncycastle.asn1.x509.GeneralSubtree;
16import org.bouncycastle.util.Arrays;
17import org.bouncycastle.util.Integers;
18import org.bouncycastle.util.Strings;
19
20public 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