001package org.apache.commons.ssl.org.bouncycastle.asn1.x500.style; 002 003import java.io.IOException; 004import java.util.Enumeration; 005import java.util.Hashtable; 006 007import org.apache.commons.ssl.org.bouncycastle.asn1.ASN1Encodable; 008import org.apache.commons.ssl.org.bouncycastle.asn1.ASN1ObjectIdentifier; 009import org.apache.commons.ssl.org.bouncycastle.asn1.DERUTF8String; 010import org.apache.commons.ssl.org.bouncycastle.asn1.x500.AttributeTypeAndValue; 011import org.apache.commons.ssl.org.bouncycastle.asn1.x500.RDN; 012import org.apache.commons.ssl.org.bouncycastle.asn1.x500.X500Name; 013import org.apache.commons.ssl.org.bouncycastle.asn1.x500.X500NameStyle; 014 015/** 016 * This class provides some default behavior and common implementation for a 017 * X500NameStyle. It should be easily extendable to support implementing the 018 * desired X500NameStyle. 019 */ 020public abstract class AbstractX500NameStyle 021 implements X500NameStyle 022{ 023 024 /** 025 * Tool function to shallow copy a Hashtable. 026 * 027 * @param paramsMap table to copy 028 * @return the copy of the table 029 */ 030 public static Hashtable copyHashTable(Hashtable paramsMap) 031 { 032 Hashtable newTable = new Hashtable(); 033 034 Enumeration keys = paramsMap.keys(); 035 while (keys.hasMoreElements()) 036 { 037 Object key = keys.nextElement(); 038 newTable.put(key, paramsMap.get(key)); 039 } 040 041 return newTable; 042 } 043 044 private int calcHashCode(ASN1Encodable enc) 045 { 046 String value = IETFUtils.valueToString(enc); 047 value = IETFUtils.canonicalize(value); 048 return value.hashCode(); 049 } 050 051 public int calculateHashCode(X500Name name) 052 { 053 int hashCodeValue = 0; 054 RDN[] rdns = name.getRDNs(); 055 056 // this needs to be order independent, like equals 057 for (int i = 0; i != rdns.length; i++) 058 { 059 if (rdns[i].isMultiValued()) 060 { 061 AttributeTypeAndValue[] atv = rdns[i].getTypesAndValues(); 062 063 for (int j = 0; j != atv.length; j++) 064 { 065 hashCodeValue ^= atv[j].getType().hashCode(); 066 hashCodeValue ^= calcHashCode(atv[j].getValue()); 067 } 068 } 069 else 070 { 071 hashCodeValue ^= rdns[i].getFirst().getType().hashCode(); 072 hashCodeValue ^= calcHashCode(rdns[i].getFirst().getValue()); 073 } 074 } 075 076 return hashCodeValue; 077 } 078 079 080 /** 081 * For all string values starting with '#' is assumed, that these are 082 * already valid ASN.1 objects encoded in hex. 083 * <p> 084 * All other string values are send to 085 * {@link AbstractX500NameStyle#encodeStringValue(ASN1ObjectIdentifier, String)}. 086 * </p> 087 * Subclasses should overwrite 088 * {@link AbstractX500NameStyle#encodeStringValue(ASN1ObjectIdentifier, String)} 089 * to change the encoding of specific types. 090 * 091 * @param oid the DN name of the value. 092 * @param value the String representation of the value. 093 */ 094 public ASN1Encodable stringToValue(ASN1ObjectIdentifier oid, String value) 095 { 096 if (value.length() != 0 && value.charAt(0) == '#') 097 { 098 try 099 { 100 return IETFUtils.valueFromHexString(value, 1); 101 } 102 catch (IOException e) 103 { 104 throw new RuntimeException("can't recode value for oid " + oid.getId()); 105 } 106 } 107 108 if (value.length() != 0 && value.charAt(0) == '\\') 109 { 110 value = value.substring(1); 111 } 112 113 return encodeStringValue(oid, value); 114 } 115 116 /** 117 * Encoded every value into a UTF8String. 118 * <p> 119 * Subclasses should overwrite 120 * this method to change the encoding of specific types. 121 * </p> 122 * 123 * @param oid the DN oid of the value 124 * @param value the String representation of the value 125 * @return a the value encoded into a ASN.1 object. Never returns <code>null</code>. 126 */ 127 protected ASN1Encodable encodeStringValue(ASN1ObjectIdentifier oid, String value) 128 { 129 return new DERUTF8String(value); 130 } 131 132 public boolean areEqual(X500Name name1, X500Name name2) 133 { 134 RDN[] rdns1 = name1.getRDNs(); 135 RDN[] rdns2 = name2.getRDNs(); 136 137 if (rdns1.length != rdns2.length) 138 { 139 return false; 140 } 141 142 boolean reverse = false; 143 144 if (rdns1[0].getFirst() != null && rdns2[0].getFirst() != null) 145 { 146 reverse = !rdns1[0].getFirst().getType().equals(rdns2[0].getFirst().getType()); // guess forward 147 } 148 149 for (int i = 0; i != rdns1.length; i++) 150 { 151 if (!foundMatch(reverse, rdns1[i], rdns2)) 152 { 153 return false; 154 } 155 } 156 157 return true; 158 } 159 160 private boolean foundMatch(boolean reverse, RDN rdn, RDN[] possRDNs) 161 { 162 if (reverse) 163 { 164 for (int i = possRDNs.length - 1; i >= 0; i--) 165 { 166 if (possRDNs[i] != null && rdnAreEqual(rdn, possRDNs[i])) 167 { 168 possRDNs[i] = null; 169 return true; 170 } 171 } 172 } 173 else 174 { 175 for (int i = 0; i != possRDNs.length; i++) 176 { 177 if (possRDNs[i] != null && rdnAreEqual(rdn, possRDNs[i])) 178 { 179 possRDNs[i] = null; 180 return true; 181 } 182 } 183 } 184 185 return false; 186 } 187 188 protected boolean rdnAreEqual(RDN rdn1, RDN rdn2) 189 { 190 return IETFUtils.rDNAreEqual(rdn1, rdn2); 191 } 192}