org.bouncycastle.asn1.x509.PKIXNameConstraintValidator Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of bcprov-ext-debug-jdk18on Show documentation
Show all versions of bcprov-ext-debug-jdk18on Show documentation
The Bouncy Castle Crypto package is a Java implementation of cryptographic algorithms. This jar contains JCE provider and lightweight API for the Bouncy Castle Cryptography APIs for Java 1.8 and later with debug enabled.
The newest version!
package org.bouncycastle.asn1.x509;
import java.io.IOException;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import org.bouncycastle.asn1.ASN1IA5String;
import org.bouncycastle.asn1.ASN1OctetString;
import org.bouncycastle.asn1.ASN1Sequence;
import org.bouncycastle.asn1.x500.RDN;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x500.style.IETFUtils;
import org.bouncycastle.asn1.x500.style.RFC4519Style;
import org.bouncycastle.util.Arrays;
import org.bouncycastle.util.Integers;
import org.bouncycastle.util.Strings;
import org.bouncycastle.util.encoders.Hex;
public class PKIXNameConstraintValidator
implements NameConstraintValidator
{
private Set excludedSubtreesDN = new HashSet();
private Set excludedSubtreesDNS = new HashSet();
private Set excludedSubtreesEmail = new HashSet();
private Set excludedSubtreesURI = new HashSet();
private Set excludedSubtreesIP = new HashSet();
private Set excludedSubtreesOtherName = new HashSet();
private Set permittedSubtreesDN;
private Set permittedSubtreesDNS;
private Set permittedSubtreesEmail;
private Set permittedSubtreesURI;
private Set permittedSubtreesIP;
private Set permittedSubtreesOtherName;
public PKIXNameConstraintValidator()
{
}
/**
* Checks if the given GeneralName is in the permitted set.
*
* @param name The GeneralName
* @throws NameConstraintValidatorException If the name
*/
public void checkPermitted(GeneralName name)
throws NameConstraintValidatorException
{
switch (name.getTagNo())
{
case GeneralName.otherName:
checkPermittedOtherName(permittedSubtreesOtherName, OtherName.getInstance(name.getName()));
break;
case GeneralName.rfc822Name:
checkPermittedEmail(permittedSubtreesEmail, extractNameAsString(name));
break;
case GeneralName.dNSName:
checkPermittedDNS(permittedSubtreesDNS, extractNameAsString(name));
break;
case GeneralName.directoryName:
checkPermittedDN(X500Name.getInstance(name.getName()));
break;
case GeneralName.uniformResourceIdentifier:
checkPermittedURI(permittedSubtreesURI, extractNameAsString(name));
break;
case GeneralName.iPAddress:
checkPermittedIP(permittedSubtreesIP, ASN1OctetString.getInstance(name.getName()).getOctets());
break;
default:
// other tags to be ignored.
}
}
/**
* Check if the given GeneralName is contained in the excluded set.
*
* @param name The GeneralName.
* @throws NameConstraintValidatorException If the name
is
* excluded.
*/
public void checkExcluded(GeneralName name)
throws NameConstraintValidatorException
{
switch (name.getTagNo())
{
case GeneralName.otherName:
checkExcludedOtherName(excludedSubtreesOtherName, OtherName.getInstance(name.getName()));
break;
case GeneralName.rfc822Name:
checkExcludedEmail(excludedSubtreesEmail, extractNameAsString(name));
break;
case GeneralName.dNSName:
checkExcludedDNS(excludedSubtreesDNS, extractNameAsString(name));
break;
case GeneralName.directoryName:
checkExcludedDN(X500Name.getInstance(name.getName()));
break;
case GeneralName.uniformResourceIdentifier:
checkExcludedURI(excludedSubtreesURI, extractNameAsString(name));
break;
case GeneralName.iPAddress:
checkExcludedIP(excludedSubtreesIP, ASN1OctetString.getInstance(name.getName()).getOctets());
break;
default:
// other tags to be ignored.
}
}
public void intersectPermittedSubtree(GeneralSubtree permitted)
{
intersectPermittedSubtree(new GeneralSubtree[]{permitted});
}
/**
* Updates the permitted set of these name constraints with the intersection
* with the given subtree.
*
* @param permitted The permitted subtrees
*/
public void intersectPermittedSubtree(GeneralSubtree[] permitted)
{
Map subtreesMap = new HashMap();
// group in sets in a map ordered by tag no.
for (int i = 0; i != permitted.length; i++)
{
GeneralSubtree subtree = permitted[i];
Integer tagNo = Integers.valueOf(subtree.getBase().getTagNo());
if (subtreesMap.get(tagNo) == null)
{
subtreesMap.put(tagNo, new HashSet());
}
((Set)subtreesMap.get(tagNo)).add(subtree);
}
for (Iterator it = subtreesMap.entrySet().iterator(); it.hasNext();)
{
Map.Entry entry = (Map.Entry)it.next();
// go through all subtree groups
int nameType = ((Integer)entry.getKey()).intValue();
switch (nameType)
{
case GeneralName.otherName:
permittedSubtreesOtherName = intersectOtherName(permittedSubtreesOtherName,
(Set)entry.getValue());
break;
case GeneralName.rfc822Name:
permittedSubtreesEmail = intersectEmail(permittedSubtreesEmail,
(Set)entry.getValue());
break;
case GeneralName.dNSName:
permittedSubtreesDNS = intersectDNS(permittedSubtreesDNS,
(Set)entry.getValue());
break;
case GeneralName.directoryName:
permittedSubtreesDN = intersectDN(permittedSubtreesDN,
(Set)entry.getValue());
break;
case GeneralName.uniformResourceIdentifier:
permittedSubtreesURI = intersectURI(permittedSubtreesURI,
(Set)entry.getValue());
break;
case GeneralName.iPAddress:
permittedSubtreesIP = intersectIP(permittedSubtreesIP,
(Set)entry.getValue());
break;
default:
throw new IllegalStateException("Unknown tag encountered: " + nameType);
}
}
}
public void intersectEmptyPermittedSubtree(int nameType)
{
switch (nameType)
{
case GeneralName.otherName:
permittedSubtreesOtherName = new HashSet();
break;
case GeneralName.rfc822Name:
permittedSubtreesEmail = new HashSet();
break;
case GeneralName.dNSName:
permittedSubtreesDNS = new HashSet();
break;
case GeneralName.directoryName:
permittedSubtreesDN = new HashSet();
break;
case GeneralName.uniformResourceIdentifier:
permittedSubtreesURI = new HashSet();
break;
case GeneralName.iPAddress:
permittedSubtreesIP = new HashSet();
break;
default:
throw new IllegalStateException("Unknown tag encountered: " + nameType);
}
}
/**
* Adds a subtree to the excluded set of these name constraints.
*
* @param subtree A subtree with an excluded GeneralName.
*/
public void addExcludedSubtree(GeneralSubtree subtree)
{
GeneralName base = subtree.getBase();
switch (base.getTagNo())
{
case GeneralName.otherName:
excludedSubtreesOtherName = unionOtherName(excludedSubtreesOtherName,
OtherName.getInstance(base.getName()));
break;
case GeneralName.rfc822Name:
excludedSubtreesEmail = unionEmail(excludedSubtreesEmail,
extractNameAsString(base));
break;
case GeneralName.dNSName:
excludedSubtreesDNS = unionDNS(excludedSubtreesDNS,
extractNameAsString(base));
break;
case GeneralName.directoryName:
excludedSubtreesDN = unionDN(excludedSubtreesDN,
(ASN1Sequence)base.getName().toASN1Primitive());
break;
case GeneralName.uniformResourceIdentifier:
excludedSubtreesURI = unionURI(excludedSubtreesURI,
extractNameAsString(base));
break;
case GeneralName.iPAddress:
excludedSubtreesIP = unionIP(excludedSubtreesIP,
ASN1OctetString.getInstance(base.getName()).getOctets());
break;
default:
throw new IllegalStateException("Unknown tag encountered: " + base.getTagNo());
}
}
public int hashCode()
{
return hashCollection(excludedSubtreesDN)
+ hashCollection(excludedSubtreesDNS)
+ hashCollection(excludedSubtreesEmail)
+ hashCollection(excludedSubtreesIP)
+ hashCollection(excludedSubtreesURI)
+ hashCollection(excludedSubtreesOtherName)
+ hashCollection(permittedSubtreesDN)
+ hashCollection(permittedSubtreesDNS)
+ hashCollection(permittedSubtreesEmail)
+ hashCollection(permittedSubtreesIP)
+ hashCollection(permittedSubtreesURI)
+ hashCollection(permittedSubtreesOtherName);
}
public boolean equals(Object o)
{
if (!(o instanceof PKIXNameConstraintValidator))
{
return false;
}
PKIXNameConstraintValidator constraintValidator = (PKIXNameConstraintValidator)o;
return collectionsAreEqual(constraintValidator.excludedSubtreesDN, excludedSubtreesDN)
&& collectionsAreEqual(constraintValidator.excludedSubtreesDNS, excludedSubtreesDNS)
&& collectionsAreEqual(constraintValidator.excludedSubtreesEmail, excludedSubtreesEmail)
&& collectionsAreEqual(constraintValidator.excludedSubtreesIP, excludedSubtreesIP)
&& collectionsAreEqual(constraintValidator.excludedSubtreesURI, excludedSubtreesURI)
&& collectionsAreEqual(constraintValidator.excludedSubtreesOtherName, excludedSubtreesOtherName)
&& collectionsAreEqual(constraintValidator.permittedSubtreesDN, permittedSubtreesDN)
&& collectionsAreEqual(constraintValidator.permittedSubtreesDNS, permittedSubtreesDNS)
&& collectionsAreEqual(constraintValidator.permittedSubtreesEmail, permittedSubtreesEmail)
&& collectionsAreEqual(constraintValidator.permittedSubtreesIP, permittedSubtreesIP)
&& collectionsAreEqual(constraintValidator.permittedSubtreesURI, permittedSubtreesURI)
&& collectionsAreEqual(constraintValidator.permittedSubtreesOtherName, permittedSubtreesOtherName);
}
public void checkPermittedDN(X500Name dns)
throws NameConstraintValidatorException
{
checkPermittedDN(permittedSubtreesDN, ASN1Sequence.getInstance(dns.toASN1Primitive()));
}
public void checkExcludedDN(X500Name dns)
throws NameConstraintValidatorException
{
checkExcludedDN(excludedSubtreesDN, ASN1Sequence.getInstance(dns));
}
private static boolean withinDNSubtree(
ASN1Sequence dns,
ASN1Sequence subtree)
{
if (subtree.size() < 1)
{
return false;
}
if (subtree.size() > dns.size())
{
return false;
}
int start = 0;
RDN subtreeRdnStart = RDN.getInstance(subtree.getObjectAt(0));
for (int j = 0; j < dns.size(); j++)
{
start = j;
RDN dnsRdn = RDN.getInstance(dns.getObjectAt(j));
if (IETFUtils.rDNAreEqual(subtreeRdnStart, dnsRdn))
{
break;
}
}
if (subtree.size() > dns.size() - start)
{
return false;
}
for (int j = 0; j < subtree.size(); j++)
{
// both subtree and dns are a ASN.1 Name and the elements are a RDN
RDN subtreeRdn = RDN.getInstance(subtree.getObjectAt(j));
RDN dnsRdn = RDN.getInstance(dns.getObjectAt(start + j));
// check if types and values of all naming attributes are matching, other types which are not restricted are allowed, see https://tools.ietf.org/html/rfc5280#section-7.1
if (subtreeRdn.size() == dnsRdn.size())
{
// Two relative distinguished names
// RDN1 and RDN2 match if they have the same number of naming attributes
// and for each naming attribute in RDN1 there is a matching naming attribute in RDN2.
// NOTE: this is checking the attributes in the same order, which might be not necessary, if this is a problem also IETFUtils.rDNAreEqual mus tbe changed.
// use new RFC 5280 comparison, NOTE: this is now different from with RFC 3280, where only binary comparison is used
// obey RFC 5280 7.1
// special treatment of serialNumber for GSMA SGP.22 RSP specification
if (!subtreeRdn.getFirst().getType().equals(dnsRdn.getFirst().getType()))
{
return false;
}
if (subtreeRdn.size() == 1 && subtreeRdn.getFirst().getType().equals(RFC4519Style.serialNumber))
{
if (!dnsRdn.getFirst().getValue().toString().startsWith(subtreeRdn.getFirst().getValue().toString()))
{
return false;
}
}
else if (!IETFUtils.rDNAreEqual(subtreeRdn, dnsRdn))
{
return false;
}
}
else
{
return false;
}
}
return true;
}
private void checkPermittedDN(Set permitted, ASN1Sequence dns)
throws NameConstraintValidatorException
{
if (permitted == null)
{
return;
}
if (permitted.isEmpty() && dns.size() == 0)
{
return;
}
Iterator it = permitted.iterator();
while (it.hasNext())
{
ASN1Sequence subtree = (ASN1Sequence)it.next();
if (withinDNSubtree(dns, subtree))
{
return;
}
}
throw new NameConstraintValidatorException(
"Subject distinguished name is not from a permitted subtree");
}
private void checkExcludedDN(Set excluded, ASN1Sequence dns)
throws NameConstraintValidatorException
{
if (excluded.isEmpty())
{
return;
}
Iterator it = excluded.iterator();
while (it.hasNext())
{
ASN1Sequence subtree = (ASN1Sequence)it.next();
if (withinDNSubtree(dns, subtree))
{
throw new NameConstraintValidatorException(
"Subject distinguished name is from an excluded subtree");
}
}
}
private Set intersectDN(Set permitted, Set dns)
{
Set intersect = new HashSet();
for (Iterator it = dns.iterator(); it.hasNext();)
{
ASN1Sequence dn = ASN1Sequence.getInstance(((GeneralSubtree)it
.next()).getBase().getName().toASN1Primitive());
if (permitted == null)
{
if (dn != null)
{
intersect.add(dn);
}
}
else
{
Iterator _iter = permitted.iterator();
while (_iter.hasNext())
{
ASN1Sequence subtree = (ASN1Sequence)_iter.next();
if (withinDNSubtree(dn, subtree))
{
intersect.add(dn);
}
else if (withinDNSubtree(subtree, dn))
{
intersect.add(subtree);
}
}
}
}
return intersect;
}
private Set unionDN(Set excluded, ASN1Sequence dn)
{
if (excluded.isEmpty())
{
if (dn == null)
{
return excluded;
}
excluded.add(dn);
return excluded;
}
else
{
Set intersect = new HashSet();
Iterator it = excluded.iterator();
while (it.hasNext())
{
ASN1Sequence subtree = ASN1Sequence.getInstance(it.next());
if (withinDNSubtree(dn, subtree))
{
intersect.add(subtree);
}
else if (withinDNSubtree(subtree, dn))
{
intersect.add(dn);
}
else
{
intersect.add(subtree);
intersect.add(dn);
}
}
return intersect;
}
}
private Set intersectOtherName(Set permitted, Set otherNames)
{
Set intersect = new HashSet();
for (Iterator it = otherNames.iterator(); it.hasNext();)
{
OtherName otName1 = OtherName.getInstance(((GeneralSubtree)it.next()).getBase().getName());
if (permitted == null)
{
if (otName1 != null)
{
intersect.add(otName1);
}
}
else
{
Iterator it2 = permitted.iterator();
while (it2.hasNext())
{
OtherName otName2 = OtherName.getInstance(it2.next());
intersectOtherName(otName1, otName2, intersect);
}
}
}
return intersect;
}
private void intersectOtherName(OtherName otName1, OtherName otName2, Set intersect)
{
if (otName1.equals(otName2))
{
intersect.add(otName1);
}
}
private Set unionOtherName(Set permitted, OtherName otherName)
{
Set union = permitted != null ? new HashSet(permitted) : new HashSet();
union.add(otherName);
return union;
}
private Set intersectEmail(Set permitted, Set emails)
{
Set intersect = new HashSet();
for (Iterator it = emails.iterator(); it.hasNext();)
{
String email = extractNameAsString(((GeneralSubtree)it.next())
.getBase());
if (permitted == null)
{
if (email != null)
{
intersect.add(email);
}
}
else
{
Iterator it2 = permitted.iterator();
while (it2.hasNext())
{
String _permitted = (String)it2.next();
intersectEmail(email, _permitted, intersect);
}
}
}
return intersect;
}
private Set unionEmail(Set excluded, String email)
{
if (excluded.isEmpty())
{
if (email == null)
{
return excluded;
}
excluded.add(email);
return excluded;
}
else
{
Set union = new HashSet();
Iterator it = excluded.iterator();
while (it.hasNext())
{
String _excluded = (String)it.next();
unionEmail(_excluded, email, union);
}
return union;
}
}
/**
* Returns the intersection of the permitted IP ranges in
* permitted
with ip
.
*
* @param permitted A Set
of permitted IP addresses with
* their subnet mask as byte arrays.
* @param ips The IP address with its subnet mask.
* @return The Set
of permitted IP ranges intersected with
* ip
.
*/
private Set intersectIP(Set permitted, Set ips)
{
Set intersect = new HashSet();
for (Iterator it = ips.iterator(); it.hasNext();)
{
byte[] ip = ASN1OctetString.getInstance(
((GeneralSubtree)it.next()).getBase().getName()).getOctets();
if (permitted == null)
{
if (ip != null)
{
intersect.add(ip);
}
}
else
{
Iterator it2 = permitted.iterator();
while (it2.hasNext())
{
byte[] _permitted = (byte[])it2.next();
intersect.addAll(intersectIPRange(_permitted, ip));
}
}
}
return intersect;
}
/**
* Returns the union of the excluded IP ranges in excluded
* with ip
.
*
* @param excluded A Set
of excluded IP addresses with their
* subnet mask as byte arrays.
* @param ip The IP address with its subnet mask.
* @return The Set
of excluded IP ranges unified with
* ip
as byte arrays.
*/
private Set unionIP(Set excluded, byte[] ip)
{
if (excluded.isEmpty())
{
if (ip == null)
{
return excluded;
}
excluded.add(ip);
return excluded;
}
else
{
Set union = new HashSet();
Iterator it = excluded.iterator();
while (it.hasNext())
{
byte[] _excluded = (byte[])it.next();
union.addAll(unionIPRange(_excluded, ip));
}
return union;
}
}
/**
* Calculates the union if two IP ranges.
*
* @param ipWithSubmask1 The first IP address with its subnet mask.
* @param ipWithSubmask2 The second IP address with its subnet mask.
* @return A Set
with the union of both addresses.
*/
private Set unionIPRange(byte[] ipWithSubmask1, byte[] ipWithSubmask2)
{
Set set = new HashSet();
// difficult, adding always all IPs is not wrong
if (Arrays.areEqual(ipWithSubmask1, ipWithSubmask2))
{
set.add(ipWithSubmask1);
}
else
{
set.add(ipWithSubmask1);
set.add(ipWithSubmask2);
}
return set;
}
/**
* Calculates the intersection if two IP ranges.
*
* @param ipWithSubmask1 The first IP address with its subnet mask.
* @param ipWithSubmask2 The second IP address with its subnet mask.
* @return A Set
with the single IP address with its subnet
* mask as a byte array or an empty Set
.
*/
private Set intersectIPRange(byte[] ipWithSubmask1, byte[] ipWithSubmask2)
{
if (ipWithSubmask1.length != ipWithSubmask2.length)
{
return Collections.EMPTY_SET;
}
byte[][] temp = extractIPsAndSubnetMasks(ipWithSubmask1, ipWithSubmask2);
byte ip1[] = temp[0];
byte subnetmask1[] = temp[1];
byte ip2[] = temp[2];
byte subnetmask2[] = temp[3];
byte minMax[][] = minMaxIPs(ip1, subnetmask1, ip2, subnetmask2);
byte[] min;
byte[] max;
max = min(minMax[1], minMax[3]);
min = max(minMax[0], minMax[2]);
// minimum IP address must be bigger than max
if (compareTo(min, max) == 1)
{
return Collections.EMPTY_SET;
}
// OR keeps all significant bits
byte[] ip = or(minMax[0], minMax[2]);
byte[] subnetmask = or(subnetmask1, subnetmask2);
return Collections.singleton(ipWithSubnetMask(ip, subnetmask));
}
/**
* Concatenates the IP address with its subnet mask.
*
* @param ip The IP address.
* @param subnetMask Its subnet mask.
* @return The concatenated IP address with its subnet mask.
*/
private byte[] ipWithSubnetMask(byte[] ip, byte[] subnetMask)
{
int ipLength = ip.length;
byte[] temp = new byte[ipLength * 2];
System.arraycopy(ip, 0, temp, 0, ipLength);
System.arraycopy(subnetMask, 0, temp, ipLength, ipLength);
return temp;
}
/**
* Splits the IP addresses and their subnet mask.
*
* @param ipWithSubmask1 The first IP address with the subnet mask.
* @param ipWithSubmask2 The second IP address with the subnet mask.
* @return An array with two elements. Each element contains the IP address
* and the subnet mask in this order.
*/
private byte[][] extractIPsAndSubnetMasks(
byte[] ipWithSubmask1,
byte[] ipWithSubmask2)
{
int ipLength = ipWithSubmask1.length / 2;
byte ip1[] = new byte[ipLength];
byte subnetmask1[] = new byte[ipLength];
System.arraycopy(ipWithSubmask1, 0, ip1, 0, ipLength);
System.arraycopy(ipWithSubmask1, ipLength, subnetmask1, 0, ipLength);
byte ip2[] = new byte[ipLength];
byte subnetmask2[] = new byte[ipLength];
System.arraycopy(ipWithSubmask2, 0, ip2, 0, ipLength);
System.arraycopy(ipWithSubmask2, ipLength, subnetmask2, 0, ipLength);
return new byte[][]
{ip1, subnetmask1, ip2, subnetmask2};
}
/**
* Based on the two IP addresses and their subnet masks the IP range is
* computed for each IP address - subnet mask pair and returned as the
* minimum IP address and the maximum address of the range.
*
* @param ip1 The first IP address.
* @param subnetmask1 The subnet mask of the first IP address.
* @param ip2 The second IP address.
* @param subnetmask2 The subnet mask of the second IP address.
* @return A array with two elements. The first/second element contains the
* min and max IP address of the first/second IP address and its
* subnet mask.
*/
private byte[][] minMaxIPs(
byte[] ip1,
byte[] subnetmask1,
byte[] ip2,
byte[] subnetmask2)
{
int ipLength = ip1.length;
byte[] min1 = new byte[ipLength];
byte[] max1 = new byte[ipLength];
byte[] min2 = new byte[ipLength];
byte[] max2 = new byte[ipLength];
for (int i = 0; i < ipLength; i++)
{
min1[i] = (byte)(ip1[i] & subnetmask1[i]);
max1[i] = (byte)(ip1[i] & subnetmask1[i] | ~subnetmask1[i]);
min2[i] = (byte)(ip2[i] & subnetmask2[i]);
max2[i] = (byte)(ip2[i] & subnetmask2[i] | ~subnetmask2[i]);
}
return new byte[][]{min1, max1, min2, max2};
}
private void checkPermittedEmail(Set permitted, String email)
throws NameConstraintValidatorException
{
if (permitted == null)
{
return;
}
Iterator it = permitted.iterator();
while (it.hasNext())
{
String str = ((String)it.next());
if (emailIsConstrained(email, str))
{
return;
}
}
if (email.length() == 0 && permitted.size() == 0)
{
return;
}
throw new NameConstraintValidatorException(
"Subject email address is not from a permitted subtree.");
}
private void checkPermittedOtherName(Set permitted, OtherName name)
throws NameConstraintValidatorException
{
if (permitted == null)
{
return;
}
Iterator it = permitted.iterator();
while (it.hasNext())
{
OtherName str = OtherName.getInstance(it.next());
if (otherNameIsConstrained(name, str))
{
return;
}
}
throw new NameConstraintValidatorException(
"Subject OtherName is not from a permitted subtree.");
}
private void checkExcludedOtherName(Set excluded, OtherName name)
throws NameConstraintValidatorException
{
if (excluded.isEmpty())
{
return;
}
Iterator it = excluded.iterator();
while (it.hasNext())
{
OtherName str = OtherName.getInstance(it.next());
if (otherNameIsConstrained(name, str))
{
throw new NameConstraintValidatorException(
"OtherName is from an excluded subtree.");
}
}
}
private void checkExcludedEmail(Set excluded, String email)
throws NameConstraintValidatorException
{
if (excluded.isEmpty())
{
return;
}
Iterator it = excluded.iterator();
while (it.hasNext())
{
String str = (String)it.next();
if (emailIsConstrained(email, str))
{
throw new NameConstraintValidatorException(
"Email address is from an excluded subtree.");
}
}
}
/**
* Checks if the IP ip
is included in the permitted set
* permitted
.
*
* @param permitted A Set
of permitted IP addresses with
* their subnet mask as byte arrays.
* @param ip The IP address.
* @throws NameConstraintValidatorException if the IP is not permitted.
*/
private void checkPermittedIP(Set permitted, byte[] ip)
throws NameConstraintValidatorException
{
if (permitted == null)
{
return;
}
Iterator it = permitted.iterator();
while (it.hasNext())
{
byte[] ipWithSubnet = (byte[])it.next();
if (isIPConstrained(ip, ipWithSubnet))
{
return;
}
}
if (ip.length == 0 && permitted.size() == 0)
{
return;
}
throw new NameConstraintValidatorException(
"IP is not from a permitted subtree.");
}
/**
* Checks if the IP ip
is included in the excluded set
* excluded
.
*
* @param excluded A Set
of excluded IP addresses with their
* subnet mask as byte arrays.
* @param ip The IP address.
* @throws NameConstraintValidatorException if the IP is excluded.
*/
private void checkExcludedIP(Set excluded, byte[] ip)
throws NameConstraintValidatorException
{
if (excluded.isEmpty())
{
return;
}
Iterator it = excluded.iterator();
while (it.hasNext())
{
byte[] ipWithSubnet = (byte[])it.next();
if (isIPConstrained(ip, ipWithSubnet))
{
throw new NameConstraintValidatorException(
"IP is from an excluded subtree.");
}
}
}
/**
* Checks if the IP address ip
is constrained by
* constraint
.
*
* @param ip The IP address.
* @param constraint The constraint. This is an IP address concatenated with
* its subnetmask.
* @return true
if constrained, false
* otherwise.
*/
private boolean isIPConstrained(byte ip[], byte[] constraint)
{
int ipLength = ip.length;
if (ipLength != (constraint.length / 2))
{
return false;
}
byte[] subnetMask = new byte[ipLength];
System.arraycopy(constraint, ipLength, subnetMask, 0, ipLength);
byte[] permittedSubnetAddress = new byte[ipLength];
byte[] ipSubnetAddress = new byte[ipLength];
// the resulting IP address by applying the subnet mask
for (int i = 0; i < ipLength; i++)
{
permittedSubnetAddress[i] = (byte)(constraint[i] & subnetMask[i]);
ipSubnetAddress[i] = (byte)(ip[i] & subnetMask[i]);
}
return Arrays.areEqual(permittedSubnetAddress, ipSubnetAddress);
}
private boolean otherNameIsConstrained(OtherName name, OtherName constraint)
{
if (constraint.equals(name))
{
return true;
}
return false;
}
private boolean emailIsConstrained(String email, String constraint)
{
String sub = email.substring(email.indexOf('@') + 1);
// a particular mailbox
if (constraint.indexOf('@') != -1)
{
if (email.equalsIgnoreCase(constraint))
{
return true;
}
if (sub.equalsIgnoreCase(constraint.substring(1)))
{
return true;
}
}
// on particular host
else if (!(constraint.charAt(0) == '.'))
{
if (sub.equalsIgnoreCase(constraint))
{
return true;
}
}
// address in sub domain
else if (withinDomain(sub, constraint))
{
return true;
}
return false;
}
private boolean withinDomain(String testDomain, String domain)
{
String tempDomain = domain;
if (tempDomain.startsWith("."))
{
tempDomain = tempDomain.substring(1);
}
String[] domainParts = Strings.split(tempDomain, '.');
String[] testDomainParts = Strings.split(testDomain, '.');
// must have at least one subdomain
if (testDomainParts.length <= domainParts.length)
{
return false;
}
int d = testDomainParts.length - domainParts.length;
for (int i = -1; i < domainParts.length; i++)
{
if (i == -1)
{
if (testDomainParts[i + d].equals(""))
{
return false;
}
}
else if (!domainParts[i].equalsIgnoreCase(testDomainParts[i + d]))
{
return false;
}
}
return true;
}
private void checkPermittedDNS(Set permitted, String dns)
throws NameConstraintValidatorException
{
if (permitted == null)
{
return;
}
Iterator it = permitted.iterator();
while (it.hasNext())
{
String str = ((String)it.next());
// is sub domain
if (withinDomain(dns, str) || dns.equalsIgnoreCase(str))
{
return;
}
}
if (dns.length() == 0 && permitted.size() == 0)
{
return;
}
throw new NameConstraintValidatorException(
"DNS is not from a permitted subtree.");
}
private void checkExcludedDNS(Set excluded, String dns)
throws NameConstraintValidatorException
{
if (excluded.isEmpty())
{
return;
}
Iterator it = excluded.iterator();
while (it.hasNext())
{
String str = ((String)it.next());
// is sub domain or the same
if (withinDomain(dns, str) || dns.equalsIgnoreCase(str))
{
throw new NameConstraintValidatorException(
"DNS is from an excluded subtree.");
}
}
}
/**
* The common part of email1
and email2
is
* added to the union union
. If email1
and
* email2
have nothing in common they are added both.
*
* @param email1 Email address constraint 1.
* @param email2 Email address constraint 2.
* @param union The union.
*/
private void unionEmail(String email1, String email2, Set union)
{
// email1 is a particular address
if (email1.indexOf('@') != -1)
{
String _sub = email1.substring(email1.indexOf('@') + 1);
// both are a particular mailbox
if (email2.indexOf('@') != -1)
{
if (email1.equalsIgnoreCase(email2))
{
union.add(email1);
}
else
{
union.add(email1);
union.add(email2);
}
}
// email2 specifies a domain
else if (email2.startsWith("."))
{
if (withinDomain(_sub, email2))
{
union.add(email2);
}
else
{
union.add(email1);
union.add(email2);
}
}
// email2 specifies a particular host
else
{
if (_sub.equalsIgnoreCase(email2))
{
union.add(email2);
}
else
{
union.add(email1);
union.add(email2);
}
}
}
// email1 specifies a domain
else if (email1.startsWith("."))
{
if (email2.indexOf('@') != -1)
{
String _sub = email2.substring(email1.indexOf('@') + 1);
if (withinDomain(_sub, email1))
{
union.add(email1);
}
else
{
union.add(email1);
union.add(email2);
}
}
// email2 specifies a domain
else if (email2.startsWith("."))
{
if (withinDomain(email1, email2)
|| email1.equalsIgnoreCase(email2))
{
union.add(email2);
}
else if (withinDomain(email2, email1))
{
union.add(email1);
}
else
{
union.add(email1);
union.add(email2);
}
}
else
{
if (withinDomain(email2, email1))
{
union.add(email1);
}
else
{
union.add(email1);
union.add(email2);
}
}
}
// email specifies a host
else
{
if (email2.indexOf('@') != -1)
{
String _sub = email2.substring(email1.indexOf('@') + 1);
if (_sub.equalsIgnoreCase(email1))
{
union.add(email1);
}
else
{
union.add(email1);
union.add(email2);
}
}
// email2 specifies a domain
else if (email2.startsWith("."))
{
if (withinDomain(email1, email2))
{
union.add(email2);
}
else
{
union.add(email1);
union.add(email2);
}
}
// email2 specifies a particular host
else
{
if (email1.equalsIgnoreCase(email2))
{
union.add(email1);
}
else
{
union.add(email1);
union.add(email2);
}
}
}
}
private void unionURI(String email1, String email2, Set union)
{
// email1 is a particular address
if (email1.indexOf('@') != -1)
{
String _sub = email1.substring(email1.indexOf('@') + 1);
// both are a particular mailbox
if (email2.indexOf('@') != -1)
{
if (email1.equalsIgnoreCase(email2))
{
union.add(email1);
}
else
{
union.add(email1);
union.add(email2);
}
}
// email2 specifies a domain
else if (email2.startsWith("."))
{
if (withinDomain(_sub, email2))
{
union.add(email2);
}
else
{
union.add(email1);
union.add(email2);
}
}
// email2 specifies a particular host
else
{
if (_sub.equalsIgnoreCase(email2))
{
union.add(email2);
}
else
{
union.add(email1);
union.add(email2);
}
}
}
// email1 specifies a domain
else if (email1.startsWith("."))
{
if (email2.indexOf('@') != -1)
{
String _sub = email2.substring(email1.indexOf('@') + 1);
if (withinDomain(_sub, email1))
{
union.add(email1);
}
else
{
union.add(email1);
union.add(email2);
}
}
// email2 specifies a domain
else if (email2.startsWith("."))
{
if (withinDomain(email1, email2)
|| email1.equalsIgnoreCase(email2))
{
union.add(email2);
}
else if (withinDomain(email2, email1))
{
union.add(email1);
}
else
{
union.add(email1);
union.add(email2);
}
}
else
{
if (withinDomain(email2, email1))
{
union.add(email1);
}
else
{
union.add(email1);
union.add(email2);
}
}
}
// email specifies a host
else
{
if (email2.indexOf('@') != -1)
{
String _sub = email2.substring(email1.indexOf('@') + 1);
if (_sub.equalsIgnoreCase(email1))
{
union.add(email1);
}
else
{
union.add(email1);
union.add(email2);
}
}
// email2 specifies a domain
else if (email2.startsWith("."))
{
if (withinDomain(email1, email2))
{
union.add(email2);
}
else
{
union.add(email1);
union.add(email2);
}
}
// email2 specifies a particular host
else
{
if (email1.equalsIgnoreCase(email2))
{
union.add(email1);
}
else
{
union.add(email1);
union.add(email2);
}
}
}
}
private Set intersectDNS(Set permitted, Set dnss)
{
Set intersect = new HashSet();
for (Iterator it = dnss.iterator(); it.hasNext();)
{
String dns = extractNameAsString(((GeneralSubtree)it.next())
.getBase());
if (permitted == null)
{
if (dns != null)
{
intersect.add(dns);
}
}
else
{
Iterator _iter = permitted.iterator();
while (_iter.hasNext())
{
String _permitted = (String)_iter.next();
if (withinDomain(_permitted, dns))
{
intersect.add(_permitted);
}
else if (withinDomain(dns, _permitted))
{
intersect.add(dns);
}
}
}
}
return intersect;
}
private Set unionDNS(Set excluded, String dns)
{
if (excluded.isEmpty())
{
if (dns == null)
{
return excluded;
}
excluded.add(dns);
return excluded;
}
else
{
Set union = new HashSet();
Iterator _iter = excluded.iterator();
while (_iter.hasNext())
{
String _permitted = (String)_iter.next();
if (withinDomain(_permitted, dns))
{
union.add(dns);
}
else if (withinDomain(dns, _permitted))
{
union.add(_permitted);
}
else
{
union.add(_permitted);
union.add(dns);
}
}
return union;
}
}
/**
* The most restricting part from email1
and
* email2
is added to the intersection intersect
.
*
* @param email1 Email address constraint 1.
* @param email2 Email address constraint 2.
* @param intersect The intersection.
*/
private void intersectEmail(String email1, String email2, Set intersect)
{
// email1 is a particular address
if (email1.indexOf('@') != -1)
{
String _sub = email1.substring(email1.indexOf('@') + 1);
// both are a particular mailbox
if (email2.indexOf('@') != -1)
{
if (email1.equalsIgnoreCase(email2))
{
intersect.add(email1);
}
}
// email2 specifies a domain
else if (email2.startsWith("."))
{
if (withinDomain(_sub, email2))
{
intersect.add(email1);
}
}
// email2 specifies a particular host
else
{
if (_sub.equalsIgnoreCase(email2))
{
intersect.add(email1);
}
}
}
// email specifies a domain
else if (email1.startsWith("."))
{
if (email2.indexOf('@') != -1)
{
String _sub = email2.substring(email1.indexOf('@') + 1);
if (withinDomain(_sub, email1))
{
intersect.add(email2);
}
}
// email2 specifies a domain
else if (email2.startsWith("."))
{
if (withinDomain(email1, email2)
|| email1.equalsIgnoreCase(email2))
{
intersect.add(email1);
}
else if (withinDomain(email2, email1))
{
intersect.add(email2);
}
}
else
{
if (withinDomain(email2, email1))
{
intersect.add(email2);
}
}
}
// email1 specifies a host
else
{
if (email2.indexOf('@') != -1)
{
String _sub = email2.substring(email2.indexOf('@') + 1);
if (_sub.equalsIgnoreCase(email1))
{
intersect.add(email2);
}
}
// email2 specifies a domain
else if (email2.startsWith("."))
{
if (withinDomain(email1, email2))
{
intersect.add(email1);
}
}
// email2 specifies a particular host
else
{
if (email1.equalsIgnoreCase(email2))
{
intersect.add(email1);
}
}
}
}
private void checkExcludedURI(Set excluded, String uri)
throws NameConstraintValidatorException
{
if (excluded.isEmpty())
{
return;
}
Iterator it = excluded.iterator();
while (it.hasNext())
{
String str = ((String)it.next());
if (isUriConstrained(uri, str))
{
throw new NameConstraintValidatorException(
"URI is from an excluded subtree.");
}
}
}
private Set intersectURI(Set permitted, Set uris)
{
Set intersect = new HashSet();
for (Iterator it = uris.iterator(); it.hasNext();)
{
String uri = extractNameAsString(((GeneralSubtree)it.next())
.getBase());
if (permitted == null)
{
if (uri != null)
{
intersect.add(uri);
}
}
else
{
Iterator _iter = permitted.iterator();
while (_iter.hasNext())
{
String _permitted = (String)_iter.next();
intersectURI(_permitted, uri, intersect);
}
}
}
return intersect;
}
private Set unionURI(Set excluded, String uri)
{
if (excluded.isEmpty())
{
if (uri == null)
{
return excluded;
}
excluded.add(uri);
return excluded;
}
else
{
Set union = new HashSet();
Iterator _iter = excluded.iterator();
while (_iter.hasNext())
{
String _excluded = (String)_iter.next();
unionURI(_excluded, uri, union);
}
return union;
}
}
private void intersectURI(String email1, String email2, Set intersect)
{
// email1 is a particular address
if (email1.indexOf('@') != -1)
{
String _sub = email1.substring(email1.indexOf('@') + 1);
// both are a particular mailbox
if (email2.indexOf('@') != -1)
{
if (email1.equalsIgnoreCase(email2))
{
intersect.add(email1);
}
}
// email2 specifies a domain
else if (email2.startsWith("."))
{
if (withinDomain(_sub, email2))
{
intersect.add(email1);
}
}
// email2 specifies a particular host
else
{
if (_sub.equalsIgnoreCase(email2))
{
intersect.add(email1);
}
}
}
// email specifies a domain
else if (email1.startsWith("."))
{
if (email2.indexOf('@') != -1)
{
String _sub = email2.substring(email1.indexOf('@') + 1);
if (withinDomain(_sub, email1))
{
intersect.add(email2);
}
}
// email2 specifies a domain
else if (email2.startsWith("."))
{
if (withinDomain(email1, email2)
|| email1.equalsIgnoreCase(email2))
{
intersect.add(email1);
}
else if (withinDomain(email2, email1))
{
intersect.add(email2);
}
}
else
{
if (withinDomain(email2, email1))
{
intersect.add(email2);
}
}
}
// email1 specifies a host
else
{
if (email2.indexOf('@') != -1)
{
String _sub = email2.substring(email2.indexOf('@') + 1);
if (_sub.equalsIgnoreCase(email1))
{
intersect.add(email2);
}
}
// email2 specifies a domain
else if (email2.startsWith("."))
{
if (withinDomain(email1, email2))
{
intersect.add(email1);
}
}
// email2 specifies a particular host
else
{
if (email1.equalsIgnoreCase(email2))
{
intersect.add(email1);
}
}
}
}
private void checkPermittedURI(Set permitted, String uri)
throws NameConstraintValidatorException
{
if (permitted == null)
{
return;
}
Iterator it = permitted.iterator();
while (it.hasNext())
{
String str = ((String)it.next());
if (isUriConstrained(uri, str))
{
return;
}
}
if (uri.length() == 0 && permitted.size() == 0)
{
return;
}
throw new NameConstraintValidatorException(
"URI is not from a permitted subtree.");
}
private boolean isUriConstrained(String uri, String constraint)
{
String host = extractHostFromURL(uri);
// a host
if (!constraint.startsWith("."))
{
if (host.equalsIgnoreCase(constraint))
{
return true;
}
}
// in sub domain or domain
else if (withinDomain(host, constraint))
{
return true;
}
return false;
}
private static String extractHostFromURL(String url)
{
// see RFC 1738
// remove ':' after protocol, e.g. https:
String sub = url.substring(url.indexOf(':') + 1);
// extract host from Common Internet Scheme Syntax, e.g. https://
if (sub.indexOf("//") != -1)
{
sub = sub.substring(sub.indexOf("//") + 2);
}
// first remove port, e.g. https://test.com:21
if (sub.lastIndexOf(':') != -1)
{
sub = sub.substring(0, sub.lastIndexOf(':'));
}
// remove user and password, e.g. https://john:[email protected]
sub = sub.substring(sub.indexOf(':') + 1);
sub = sub.substring(sub.indexOf('@') + 1);
// remove local parts, e.g. https://test.com/bla
if (sub.indexOf('/') != -1)
{
sub = sub.substring(0, sub.indexOf('/'));
}
return sub;
}
private String extractNameAsString(GeneralName name)
{
return ASN1IA5String.getInstance(name.getName()).getString();
}
/**
* Returns the maximum IP address.
*
* @param ip1 The first IP address.
* @param ip2 The second IP address.
* @return The maximum IP address.
*/
private static byte[] max(byte[] ip1, byte[] ip2)
{
for (int i = 0; i < ip1.length; i++)
{
if ((ip1[i] & 0xFFFF) > (ip2[i] & 0xFFFF))
{
return ip1;
}
}
return ip2;
}
/**
* Returns the minimum IP address.
*
* @param ip1 The first IP address.
* @param ip2 The second IP address.
* @return The minimum IP address.
*/
private static byte[] min(byte[] ip1, byte[] ip2)
{
for (int i = 0; i < ip1.length; i++)
{
if ((ip1[i] & 0xFFFF) < (ip2[i] & 0xFFFF))
{
return ip1;
}
}
return ip2;
}
/**
* Compares IP address ip1
with ip2
. If ip1
* is equal to ip2 0 is returned. If ip1 is bigger 1 is returned, -1
* otherwise.
*
* @param ip1 The first IP address.
* @param ip2 The second IP address.
* @return 0 if ip1 is equal to ip2, 1 if ip1 is bigger, -1 otherwise.
*/
private static int compareTo(byte[] ip1, byte[] ip2)
{
if (Arrays.areEqual(ip1, ip2))
{
return 0;
}
if (Arrays.areEqual(max(ip1, ip2), ip1))
{
return 1;
}
return -1;
}
/**
* Returns the logical OR of the IP addresses ip1
and
* ip2
.
*
* @param ip1 The first IP address.
* @param ip2 The second IP address.
* @return The OR of ip1
and ip2
.
*/
private static byte[] or(byte[] ip1, byte[] ip2)
{
byte[] temp = new byte[ip1.length];
for (int i = 0; i < ip1.length; i++)
{
temp[i] = (byte)(ip1[i] | ip2[i]);
}
return temp;
}
private int hashCollection(Collection coll)
{
if (coll == null)
{
return 0;
}
int hash = 0;
Iterator it1 = coll.iterator();
while (it1.hasNext())
{
Object o = it1.next();
if (o instanceof byte[])
{
hash += Arrays.hashCode((byte[])o);
}
else
{
hash += o.hashCode();
}
}
return hash;
}
private boolean collectionsAreEqual(Collection coll1, Collection coll2)
{
if (coll1 == coll2)
{
return true;
}
if (coll1 == null || coll2 == null)
{
return false;
}
if (coll1.size() != coll2.size())
{
return false;
}
Iterator it1 = coll1.iterator();
while (it1.hasNext())
{
Object a = it1.next();
Iterator it2 = coll2.iterator();
boolean found = false;
while (it2.hasNext())
{
Object b = it2.next();
if (equals(a, b))
{
found = true;
break;
}
}
if (!found)
{
return false;
}
}
return true;
}
private boolean equals(Object o1, Object o2)
{
if (o1 == o2)
{
return true;
}
if (o1 == null || o2 == null)
{
return false;
}
if (o1 instanceof byte[] && o2 instanceof byte[])
{
return Arrays.areEqual((byte[])o1, (byte[])o2);
}
else
{
return o1.equals(o2);
}
}
/**
* Stringifies an IPv4 or v6 address with subnet mask.
*
* @param ip The IP with subnet mask.
* @return The stringified IP address.
*/
private String stringifyIP(byte[] ip)
{
StringBuilder temp = new StringBuilder();
for (int i = 0; i < ip.length / 2; i++)
{
if (temp.length() > 0)
{
temp.append(".");
}
temp.append(Integer.toString(ip[i] & 0x00FF));
}
temp.append("/");
boolean first = true;
for (int i = ip.length / 2; i < ip.length; i++)
{
if (first)
{
first = false;
}
else
{
temp.append(".");
}
temp.append(Integer.toString(ip[i] & 0x00FF));
}
return temp.toString();
}
private String stringifyIPCollection(Set ips)
{
StringBuilder temp = new StringBuilder();
temp.append("[");
for (Iterator it = ips.iterator(); it.hasNext();)
{
if (temp.length() > 1)
{
temp.append(",");
}
temp.append(stringifyIP((byte[])it.next()));
}
temp.append("]");
return temp.toString();
}
private String stringifyOtherNameCollection(Set otherNames)
{
StringBuilder temp = new StringBuilder();
temp.append("[");
for (Iterator it = otherNames.iterator(); it.hasNext();)
{
if (temp.length() > 1)
{
temp.append(",");
}
OtherName name = OtherName.getInstance(it.next());
temp.append(name.getTypeID().getId());
temp.append(":");
try
{
// -DM Hex.toHexString
temp.append(Hex.toHexString(name.getValue().toASN1Primitive().getEncoded()));
}
catch (IOException e)
{
temp.append(e.toString());
}
}
temp.append("]");
return temp.toString();
}
private final void addLine(StringBuilder sb, String str)
{
sb.append(str).append(Strings.lineSeparator());
}
public String toString()
{
StringBuilder temp = new StringBuilder();
addLine(temp, "permitted:");
if (permittedSubtreesDN != null)
{
addLine(temp, "DN:");
addLine(temp, permittedSubtreesDN.toString());
}
if (permittedSubtreesDNS != null)
{
addLine(temp, "DNS:");
addLine(temp, permittedSubtreesDNS.toString());
}
if (permittedSubtreesEmail != null)
{
addLine(temp, "Email:");
addLine(temp, permittedSubtreesEmail.toString());
}
if (permittedSubtreesURI != null)
{
addLine(temp, "URI:");
addLine(temp, permittedSubtreesURI.toString());
}
if (permittedSubtreesIP != null)
{
addLine(temp, "IP:");
addLine(temp, stringifyIPCollection(permittedSubtreesIP));
}
if (permittedSubtreesOtherName != null)
{
addLine(temp, "OtherName:");
addLine(temp, stringifyOtherNameCollection(permittedSubtreesOtherName));
}
addLine(temp, "excluded:");
if (!excludedSubtreesDN.isEmpty())
{
addLine(temp, "DN:");
addLine(temp, excludedSubtreesDN.toString());
}
if (!excludedSubtreesDNS.isEmpty())
{
addLine(temp, "DNS:");
addLine(temp, excludedSubtreesDNS.toString());
}
if (!excludedSubtreesEmail.isEmpty())
{
addLine(temp, "Email:");
addLine(temp, excludedSubtreesEmail.toString());
}
if (!excludedSubtreesURI.isEmpty())
{
addLine(temp, "URI:");
addLine(temp, excludedSubtreesURI.toString());
}
if (!excludedSubtreesIP.isEmpty())
{
addLine(temp, "IP:");
addLine(temp, stringifyIPCollection(excludedSubtreesIP));
}
if (!excludedSubtreesOtherName.isEmpty())
{
addLine(temp, "OtherName:");
addLine(temp, stringifyOtherNameCollection(excludedSubtreesOtherName));
}
return temp.toString();
}
}