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.ArrayList; 27 import java.util.Collection; 28 import java.util.Collections; 29 import java.util.Iterator; 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 final 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<List<AttributeTypeAndValue>> 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 DerInputStream in = new DerInputStream(encoding); 68 69 if (in.getEndOffset() != encoding.length) { 70 throw new IOException("Wrong content length"); 71 } 72 73 ASN1.decode(in); 74 75 this.rdn = (List<List<AttributeTypeAndValue>>) in.content; 76 } 77 78 /** 79 * Creates new <code>Name</code> instance 80 * 81 * @param name - Name as String 82 * @throws IOException - if string is wrong 83 */ 84 public Name(String name) throws IOException { 85 rdn = new DNParser(name).parse(); 86 } 87 88 private Name(List<List<AttributeTypeAndValue>> rdn) { 89 this.rdn = rdn; 90 } 91 92 /** 93 * Returns <code>X500Principal</code> instance corresponding to this 94 * <code>Name</code> instance 95 * 96 * @return equivalent X500Principal object 97 */ 98 public X500Principal getX500Principal(){ 99 return new X500Principal(getEncoded()); 100 } 101 102 /** 103 * Returns Relative Distinguished Name as <code>String</code> according 104 * the format requested 105 * 106 * @param format one of X500Principal.CANONICAL, X500Principal.RFC1779, or 107 * X500Principal.RFC2253, case insensitive 108 */ 109 public String getName(String format) { 110 // 111 // check X500Principal constants first 112 // 113 if (X500Principal.RFC1779.equals(format)) { 114 115 if (rfc1779String == null) { 116 rfc1779String = getName0(format); 117 } 118 return rfc1779String; 119 120 } else if (X500Principal.RFC2253.equals(format)) { 121 122 if (rfc2253String == null) { 123 rfc2253String = getName0(format); 124 } 125 return rfc2253String; 126 127 } else if (X500Principal.CANONICAL.equals(format)) { 128 129 if (canonicalString == null) { 130 canonicalString = getName0(format); 131 } 132 return canonicalString; 133 134 } 135 // 136 // compare ignore case 137 // 138 else if (X500Principal.RFC1779.equalsIgnoreCase(format)) { 139 140 if (rfc1779String == null) { 141 rfc1779String = getName0(X500Principal.RFC1779); 142 } 143 return rfc1779String; 144 145 } else if (X500Principal.RFC2253.equalsIgnoreCase(format)) { 146 147 if (rfc2253String == null) { 148 rfc2253String = getName0(X500Principal.RFC2253); 149 } 150 return rfc2253String; 151 152 } else if (X500Principal.CANONICAL.equalsIgnoreCase(format)) { 153 154 if (canonicalString == null) { 155 canonicalString = getName0(X500Principal.CANONICAL); 156 } 157 return canonicalString; 158 159 } else { 160 throw new IllegalArgumentException("Illegal format: " + format); 161 } 162 } 163 164 /** 165 * Returns Relative Distinguished Name as <code>String</code> according 166 * the format requested, format is int value 167 */ 168 private String getName0(String format) { 169 StringBuilder name = new StringBuilder(); 170 171 // starting with the last element and moving to the first. 172 for (int i = rdn.size() - 1; i >= 0; i--) { 173 List<AttributeTypeAndValue> atavList = rdn.get(i); 174 175 if (X500Principal.CANONICAL == format) { 176 atavList = new ArrayList<AttributeTypeAndValue>(atavList); 177 Collections.sort(atavList, new AttributeTypeAndValueComparator()); 178 } 179 180 // Relative Distinguished Name to string 181 Iterator<AttributeTypeAndValue> it = atavList.iterator(); 182 while (it.hasNext()) { 183 AttributeTypeAndValue attributeTypeAndValue = it.next(); 184 attributeTypeAndValue.appendName(format, name); 185 if (it.hasNext()) { 186 // multi-valued RDN 187 if (X500Principal.RFC1779 == format) { 188 name.append(" + "); 189 } else { 190 name.append('+'); 191 } 192 } 193 } 194 195 if (i != 0) { 196 name.append(','); 197 if (format == X500Principal.RFC1779) { 198 name.append(' '); 199 } 200 } 201 } 202 203 String sName = name.toString(); 204 if (X500Principal.CANONICAL.equals(format)) { 205 sName = sName.toLowerCase(Locale.US); 206 } 207 return sName; 208 } 209 210 /** 211 * Gets encoded form of DN 212 * 213 * @return return encoding, no copying is performed 214 */ 215 public byte[] getEncoded() { 216 if (encoded == null) { 217 encoded = ASN1.encode(this); 218 } 219 return encoded; 220 } 221 222 /** 223 * According to RFC 3280 (http://www.ietf.org/rfc/rfc3280.txt) 224 * X.501 Name structure is defined as follows: 225 * 226 * Name ::= CHOICE { 227 * RDNSequence } 228 * 229 * RDNSequence ::= SEQUENCE OF RelativeDistinguishedName 230 * 231 * RelativeDistinguishedName ::= 232 * SET OF AttributeTypeAndValue 233 * 234 */ 235 public static final ASN1SetOf ASN1_RDN = new ASN1SetOf( 236 AttributeTypeAndValue.ASN1); 237 238 public static final ASN1SequenceOf ASN1 = new ASN1SequenceOf(ASN1_RDN) { 239 240 public Object getDecodedObject(BerInputStream in) { 241 return new Name((List<List<AttributeTypeAndValue>>) in.content); 242 } 243 244 public Collection getValues(Object object) { 245 return ((Name) object).rdn; 246 } 247 }; 248 } 249