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 18 /** 19 * @author Alexander V. Esin 20 * @version $Revision$ 21 */ 22 23 package org.apache.harmony.security.x501; 24 25 import java.io.IOException; 26 import java.util.Collection; 27 import java.util.Collections; 28 import java.util.Iterator; 29 import java.util.LinkedList; 30 import java.util.List; 31 import java.util.Locale; 32 import javax.security.auth.x500.X500Principal; 33 import org.apache.harmony.security.asn1.ASN1SequenceOf; 34 import org.apache.harmony.security.asn1.ASN1SetOf; 35 import org.apache.harmony.security.asn1.BerInputStream; 36 import org.apache.harmony.security.asn1.DerInputStream; 37 import org.apache.harmony.security.x509.DNParser; 38 39 40 /** 41 * X.501 Name 42 */ 43 public class Name { 44 45 //ASN.1 DER encoding of Name 46 private volatile byte[] encoded; 47 48 // RFC1779 string 49 private String rfc1779String; 50 51 // RFC2253 string 52 private String rfc2253String; 53 54 //CANONICAL string 55 private String canonicalString; 56 57 //Collection of RDNs 58 private List rdn; 59 60 /** 61 * Creates new <code>Name</code> instance from its DER encoding 62 * 63 * @param encoding - ASN.1 DER encoding 64 * @throws IOException - if encoding is wrong 65 */ 66 public Name(byte[] encoding) throws IOException { 67 68 DerInputStream in = new DerInputStream(encoding); 69 70 if (in.getEndOffset() != encoding.length) { 71 throw new IOException("Wrong content length"); 72 } 73 74 ASN1.decode(in); 75 76 this.rdn = (List) in.content; 77 } 78 79 /** 80 * Creates new <code>Name</code> instance 81 * 82 * @param name - Name as String 83 * @throws IOException - if string is wrong 84 */ 85 public Name(String name) throws IOException { 86 rdn = new DNParser(name).parse(); 87 } 88 89 // Creates Name instance 90 private Name(List rdn) { 91 this.rdn = rdn; 92 } 93 94 /** 95 * Returns <code>X500Principal</code> instance corresponding to this 96 * <code>Name</code> instance 97 * 98 * @return equivalent X500Principal object 99 */ 100 public X500Principal getX500Principal(){ 101 return new X500Principal(getName0(X500Principal.RFC2253)); 102 } 103 104 /** 105 * Returns Relative Distinguished Name as <code>String</code> according 106 * the format requested 107 * 108 * @param format 109 * Name format requested 110 * @return Relative Distinguished Name as <code>String</code> according 111 * the format requested 112 */ 113 public String getName(String format) { 114 115 // 116 // check X500Principal constants first 117 // 118 if (X500Principal.RFC1779.equals(format)) { 119 120 if (rfc1779String == null) { 121 rfc1779String = getName0(format); 122 } 123 return rfc1779String; 124 125 } else if (X500Principal.RFC2253.equals(format)) { 126 127 if (rfc2253String == null) { 128 rfc2253String = getName0(format); 129 } 130 return rfc2253String; 131 132 } else if (X500Principal.CANONICAL.equals(format)) { 133 134 if (canonicalString == null) { 135 canonicalString = getName0(format); 136 } 137 return canonicalString; 138 139 } 140 // 141 // compare ignore case 142 // 143 else if (X500Principal.RFC1779.equalsIgnoreCase(format)) { 144 145 if (rfc1779String == null) { 146 rfc1779String = getName0(X500Principal.RFC1779); 147 } 148 return rfc1779String; 149 150 } else if (X500Principal.RFC2253.equalsIgnoreCase(format)) { 151 152 if (rfc2253String == null) { 153 rfc2253String = getName0(X500Principal.RFC2253); 154 } 155 return rfc2253String; 156 157 } else if (X500Principal.CANONICAL.equalsIgnoreCase(format)) { 158 159 if (canonicalString == null) { 160 canonicalString = getName0(X500Principal.CANONICAL); 161 } 162 return canonicalString; 163 164 } else { 165 throw new IllegalArgumentException("Illegal format: " + format); 166 } 167 } 168 169 /** 170 * Returns Relative Distinguished Name as <code>String</code> according 171 * the format requested, format is int value 172 * 173 * @param format 174 * Name format requested 175 * @return Relative Distinguished Name as <code>String</code> according 176 * the format requested 177 */ 178 private String getName0(String format) { 179 180 StringBuffer name = new StringBuffer(); 181 182 // starting with the last element and moving to the first. 183 for (int i = rdn.size() - 1; i >= 0; i--) { 184 List atavList = (List) rdn.get(i); 185 186 if (X500Principal.CANONICAL == format) { 187 List sortedList = new LinkedList(atavList); 188 Collections.sort(sortedList, 189 new AttributeTypeAndValueComparator()); 190 atavList = sortedList; 191 } 192 193 // Relative Distinguished Name to string 194 Iterator it = atavList.iterator(); 195 while (it.hasNext()) { 196 AttributeTypeAndValue _ava = (AttributeTypeAndValue) it.next(); 197 _ava.appendName(format, name); 198 if (it.hasNext()) { 199 // multi-valued RDN 200 if (X500Principal.RFC1779 == format) { 201 name.append(" + "); 202 } else { 203 name.append('+'); 204 } 205 } 206 } 207 208 if (i != 0) { 209 name.append(','); 210 if (format == X500Principal.RFC1779) { 211 name.append(' '); 212 } 213 } 214 } 215 216 String sName = name.toString(); 217 if (X500Principal.CANONICAL.equals(format)) { 218 sName = sName.toLowerCase(Locale.US); 219 } 220 return sName; 221 } 222 223 /** 224 * Gets encoded form of DN 225 * 226 * @return return encoding, no copying is performed 227 */ 228 public byte[] getEncoded() { 229 if (encoded == null) { 230 encoded = ASN1.encode(this); 231 } 232 return encoded; 233 } 234 235 /** 236 * According to RFC 3280 (http://www.ietf.org/rfc/rfc3280.txt) 237 * X.501 Name structure is defined as follows: 238 * 239 * Name ::= CHOICE { 240 * RDNSequence } 241 * 242 * RDNSequence ::= SEQUENCE OF RelativeDistinguishedName 243 * 244 * RelativeDistinguishedName ::= 245 * SET OF AttributeTypeAndValue 246 * 247 */ 248 249 public static final ASN1SetOf ASN1_RDN = new ASN1SetOf( 250 AttributeTypeAndValue.ASN1); 251 252 public static final ASN1SequenceOf ASN1 = new ASN1SequenceOf(ASN1_RDN) { 253 254 public Object getDecodedObject(BerInputStream in) { 255 return new Name((List) in.content); 256 } 257 258 public Collection getValues(Object object) { 259 return ((Name) object).rdn; //FIXME what about get method? 260 } 261 }; 262 } 263