1/* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18package javax.security.auth.x500; 19 20import java.io.IOException; 21import java.io.InputStream; 22import java.io.ObjectInputStream; 23import java.io.ObjectOutputStream; 24import java.io.Serializable; 25import java.security.Principal; 26import java.util.Map; 27import org.apache.harmony.security.x501.Name; 28 29/** 30 * Represents an X.500 principal, which holds the distinguished name of some 31 * network entity. An example of a distinguished name is {@code "O=SomeOrg, 32 * OU=SomeOrgUnit, C=US"}. The class can be instantiated from a byte representation 33 * of an object identifier (OID), an ASN.1 DER-encoded version, or a simple 34 * string holding the distinguished name. The representations must follow either 35 * RFC 2253, RFC 1779, or RFC2459. 36 */ 37public final class X500Principal implements Serializable, Principal { 38 39 private static final long serialVersionUID = -500463348111345721L; 40 41 /** 42 * Defines a constant for the canonical string format of distinguished 43 * names. 44 */ 45 public static final String CANONICAL = "CANONICAL"; 46 47 /** 48 * Defines a constant for the RFC 1779 string format of distinguished 49 * names. 50 */ 51 public static final String RFC1779 = "RFC1779"; 52 53 /** 54 * Defines a constant for the RFC 2253 string format of distinguished 55 * names. 56 */ 57 public static final String RFC2253 = "RFC2253"; 58 59 //Distinguished Name 60 private transient Name dn; 61 62 /** 63 * Creates a new X500Principal from a given ASN.1 DER encoding of a 64 * distinguished name. 65 * 66 * @param name 67 * the ASN.1 DER-encoded distinguished name 68 * 69 * @throws IllegalArgumentException 70 * if the ASN.1 DER-encoded distinguished name is incorrect 71 */ 72 public X500Principal(byte[] name) { 73 if (name == null) { 74 throw new IllegalArgumentException("Name cannot be null"); 75 } 76 try { 77 // FIXME dn = new Name(name); 78 dn = (Name) Name.ASN1.decode(name); 79 } catch (IOException e) { 80 throw incorrectInputEncoding(e); 81 } 82 } 83 84 /** 85 * Creates a new X500Principal from a given ASN.1 DER encoding of a 86 * distinguished name. 87 * 88 * @param in 89 * an {@code InputStream} holding the ASN.1 DER-encoded 90 * distinguished name 91 * 92 * @throws IllegalArgumentException 93 * if the ASN.1 DER-encoded distinguished name is incorrect 94 */ 95 public X500Principal(InputStream in) { 96 if (in == null) { 97 throw new NullPointerException("in == null"); 98 } 99 try { 100 // FIXME dn = new Name(is); 101 dn = (Name) Name.ASN1.decode(in); 102 } catch (IOException e) { 103 throw incorrectInputEncoding(e); 104 } 105 } 106 107 private IllegalArgumentException incorrectInputEncoding(IOException e) { 108 IllegalArgumentException iae = new IllegalArgumentException("Incorrect input encoding"); 109 iae.initCause(e); 110 throw iae; 111 } 112 113 /** 114 * Creates a new X500Principal from a string representation of a 115 * distinguished name. 116 * 117 * @param name 118 * the string representation of the distinguished name 119 * 120 * @throws IllegalArgumentException 121 * if the string representation of the distinguished name is 122 * incorrect 123 */ 124 public X500Principal(String name) { 125 if (name == null) { 126 throw new NullPointerException("name == null"); 127 } 128 try { 129 dn = new Name(name); 130 } catch (IOException e) { 131 throw incorrectInputName(e, name); 132 } 133 } 134 135 public X500Principal(String name, Map<String,String> keywordMap){ 136 if (name == null) { 137 throw new NullPointerException("name == null"); 138 } 139 try { 140 dn = new Name(substituteNameFromMap(name, keywordMap)); 141 } catch (IOException e) { 142 throw incorrectInputName(e, name); 143 } 144 } 145 146 private IllegalArgumentException incorrectInputName(IOException e, String name) { 147 IllegalArgumentException iae = new IllegalArgumentException("Incorrect input name:" + name); 148 iae.initCause(e); 149 throw iae; 150 } 151 152 private transient String canonicalName; 153 private synchronized String getCanonicalName() { 154 if (canonicalName == null) { 155 canonicalName = dn.getName(CANONICAL); 156 } 157 return canonicalName; 158 } 159 160 @Override 161 public boolean equals(Object o) { 162 if (this == o) { 163 return true; 164 } 165 if (o == null || this.getClass() != o.getClass()) { 166 return false; 167 } 168 X500Principal principal = (X500Principal) o; 169 return getCanonicalName().equals(principal.getCanonicalName()); 170 } 171 172 /** 173 * Returns an ASN.1 DER-encoded representation of the distinguished name 174 * contained in this X.500 principal. 175 * 176 * @return the ASN.1 DER-encoded representation 177 */ 178 public byte[] getEncoded() { 179 byte[] src = dn.getEncoded(); 180 byte[] dst = new byte[src.length]; 181 System.arraycopy(src, 0, dst, 0, dst.length); 182 return dst; 183 } 184 185 /** 186 * Returns a human-readable string representation of the distinguished name 187 * contained in this X.500 principal. 188 * 189 * @return the string representation 190 */ 191 public String getName() { 192 return dn.getName(RFC2253); 193 } 194 195 /** 196 * Returns a string representation of the distinguished name contained in 197 * this X.500 principal. The format of the representation can be chosen. 198 * Valid arguments are {@link #RFC1779}, {@link #RFC2253}, and 199 * {@link #CANONICAL}. The representations are specified in RFC 1779 and RFC 200 * 2253, respectively. The canonical form is based on RFC 2253, but adds 201 * some canonicalizing operations like removing leading and trailing 202 * whitespace, lower-casing the whole name, and bringing it into a 203 * normalized Unicode representation. 204 * 205 * @param format 206 * the name of the format to use for the representation 207 * 208 * @return the string representation 209 * 210 * @throws IllegalArgumentException 211 * if the {@code format} argument is not one of the three 212 * mentioned above 213 */ 214 public String getName(String format) { 215 if (CANONICAL.equals(format)) { 216 return getCanonicalName(); 217 } 218 219 return dn.getName(format); 220 } 221 222 public String getName(String format, Map<String, String> oidMap) { 223 String rfc1779Name = dn.getName(RFC1779); 224 String rfc2253Name = dn.getName(RFC2253); 225 226 if (format.equalsIgnoreCase("RFC1779")) { 227 StringBuilder resultName = new StringBuilder(rfc1779Name); 228 int fromIndex = resultName.length(); 229 int equalIndex = -1; 230 while (-1 != (equalIndex = resultName.lastIndexOf("=", fromIndex))) { 231 int commaIndex = resultName.lastIndexOf(",", equalIndex); 232 String subName = resultName.substring(commaIndex + 1, 233 equalIndex).trim(); 234 if (subName.length() > 4 235 && subName.substring(0, 4).equals("OID.")) { 236 String subSubName = subName.substring(4); 237 if (oidMap.containsKey(subSubName)) { 238 String replaceName = oidMap.get(subSubName); 239 if(commaIndex > 0) replaceName = " " + replaceName; 240 resultName.replace(commaIndex + 1, equalIndex, replaceName); 241 } 242 } 243 fromIndex = commaIndex; 244 } 245 return resultName.toString(); 246 } else if (format.equalsIgnoreCase("RFC2253")) { 247 StringBuilder resultName = new StringBuilder(rfc2253Name); 248 StringBuilder subsidyName = new StringBuilder(rfc1779Name); 249 250 int fromIndex = resultName.length(); 251 int subsidyFromIndex = subsidyName.length(); 252 int equalIndex = -1; 253 int subsidyEqualIndex = -1; 254 while (-1 != (equalIndex = resultName.lastIndexOf("=", fromIndex))) { 255 subsidyEqualIndex = subsidyName.lastIndexOf("=", 256 subsidyFromIndex); 257 int commaIndex = resultName.lastIndexOf(",", equalIndex); 258 String subName = resultName.substring(commaIndex + 1, 259 equalIndex).trim(); 260 if (oidMap.containsKey(subName)) { 261 int subOrignalEndIndex = resultName 262 .indexOf(",", equalIndex); 263 if (subOrignalEndIndex == -1) 264 subOrignalEndIndex = resultName.length(); 265 int subGoalEndIndex = subsidyName.indexOf(",", 266 subsidyEqualIndex); 267 if (subGoalEndIndex == -1) 268 subGoalEndIndex = subsidyName.length(); 269 resultName.replace(equalIndex + 1, subOrignalEndIndex, 270 subsidyName.substring(subsidyEqualIndex + 1, 271 subGoalEndIndex)); 272 resultName.replace(commaIndex + 1, equalIndex, oidMap 273 .get(subName)); 274 } 275 fromIndex = commaIndex; 276 subsidyFromIndex = subsidyEqualIndex - 1; 277 } 278 return resultName.toString(); 279 } else { 280 throw new IllegalArgumentException("invalid format specified: " + format); 281 } 282 } 283 284 @Override 285 public int hashCode() { 286 return getCanonicalName().hashCode(); 287 } 288 289 @Override 290 public String toString() { 291 return dn.getName(RFC1779); 292 } 293 294 private void writeObject(ObjectOutputStream out) throws IOException { 295 out.writeObject(dn.getEncoded()); 296 } 297 298 private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { 299 dn = (Name) Name.ASN1.decode((byte[]) in.readObject()); 300 } 301 302 private String substituteNameFromMap(String name, Map<String, String> keywordMap) { 303 StringBuilder sbName = new StringBuilder(name); 304 int fromIndex = sbName.length(); 305 int equalIndex; 306 while (-1 != (equalIndex = sbName.lastIndexOf("=", fromIndex))) { 307 int commaIndex = sbName.lastIndexOf(",", equalIndex); 308 String subName = sbName.substring(commaIndex + 1, equalIndex).trim(); 309 if (keywordMap.containsKey(subName)) { 310 sbName.replace(commaIndex + 1, equalIndex, keywordMap.get(subName)); 311 } 312 fromIndex = commaIndex; 313 } 314 return sbName.toString(); 315 } 316} 317