001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: /**
019: * @author Alexander V. Esin
020: * @version $Revision$
021: */package org.apache.harmony.security.x501;
022:
023: import java.io.IOException;
024: import java.util.Collection;
025: import java.util.Collections;
026: import java.util.Iterator;
027: import java.util.LinkedList;
028: import java.util.List;
029: import java.util.Locale;
030:
031: import javax.security.auth.x500.X500Principal;
032:
033: import org.apache.harmony.security.asn1.ASN1SequenceOf;
034: import org.apache.harmony.security.asn1.ASN1SetOf;
035: import org.apache.harmony.security.asn1.BerInputStream;
036: import org.apache.harmony.security.asn1.DerInputStream;
037: import org.apache.harmony.security.internal.nls.Messages;
038: import org.apache.harmony.security.x509.DNParser;
039:
040: /**
041: * X.501 Name
042: */
043: public class Name {
044:
045: //ASN.1 DER encoding of Name
046: private volatile byte[] encoded;
047:
048: // RFC1779 string
049: private String rfc1779String;
050:
051: // RFC2253 string
052: private String rfc2253String;
053:
054: //CANONICAL string
055: private String canonicalString;
056:
057: //Collection of RDNs
058: private List rdn;
059:
060: /**
061: * Creates new <code>Name</code> instance from its DER encoding
062: *
063: * @param encoding - ASN.1 DER encoding
064: * @throws IOException - if encoding is wrong
065: */
066: public Name(byte[] encoding) throws IOException {
067:
068: DerInputStream in = new DerInputStream(encoding);
069:
070: if (in.getEndOffset() != encoding.length) {
071: throw new IOException(Messages.getString("security.111")); //$NON-NLS-1$
072: }
073:
074: ASN1.decode(in);
075:
076: this .rdn = (List) in.content;
077: }
078:
079: /**
080: * Creates new <code>Name</code> instance
081: *
082: * @param name - Name as String
083: * @throws IOException - if string is wrong
084: */
085: public Name(String name) throws IOException {
086: rdn = new DNParser(name).parse();
087: }
088:
089: // Creates Name instance
090: private Name(List rdn) {
091: this .rdn = rdn;
092: }
093:
094: /**
095: * Returns <code>X500Principal</code> instance corresponding to this
096: * <code>Name</code> instance
097: *
098: * @return equivalent X500Principal object
099: */
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(Messages.getString(
166: "security.177", format)); //$NON-NLS-1$
167: }
168: }
169:
170: /**
171: * Returns Relative Distinguished Name as <code>String</code> according
172: * the format requested, format is int value
173: *
174: * @param format
175: * Name format requested
176: * @return Relative Distinguished Name as <code>String</code> according
177: * the format requested
178: */
179: private String getName0(String format) {
180:
181: StringBuffer name = new StringBuffer();
182:
183: // starting with the last element and moving to the first.
184: for (int i = rdn.size() - 1; i >= 0; i--) {
185: List atavList = (List) rdn.get(i);
186:
187: if (X500Principal.CANONICAL == format) {
188: List sortedList = new LinkedList(atavList);
189: Collections.sort(sortedList,
190: new AttributeTypeAndValueComparator());
191: atavList = sortedList;
192: }
193:
194: // Relative Distinguished Name to string
195: Iterator it = atavList.iterator();
196: while (it.hasNext()) {
197: AttributeTypeAndValue _ava = (AttributeTypeAndValue) it
198: .next();
199: _ava.appendName(format, name);
200: if (it.hasNext()) {
201: // multi-valued RDN
202: if (X500Principal.RFC1779 == format) {
203: name.append(" + "); //$NON-NLS-1$
204: } else {
205: name.append('+');
206: }
207: }
208: }
209:
210: if (i != 0) {
211: name.append(',');
212: if (format == X500Principal.RFC1779) {
213: name.append(' ');
214: }
215: }
216: }
217:
218: String sName = name.toString();
219: if (X500Principal.CANONICAL.equals(format)) {
220: sName = sName.toLowerCase(Locale.US);
221: }
222: return sName;
223: }
224:
225: /**
226: * Gets encoded form of DN
227: *
228: * @return return encoding, no copying is performed
229: */
230: public byte[] getEncoded() {
231: if (encoded == null) {
232: encoded = ASN1.encode(this );
233: }
234: return encoded;
235: }
236:
237: /**
238: * According to RFC 3280 (http://www.ietf.org/rfc/rfc3280.txt)
239: * X.501 Name structure is defined as follows:
240: *
241: * Name ::= CHOICE {
242: * RDNSequence }
243: *
244: * RDNSequence ::= SEQUENCE OF RelativeDistinguishedName
245: *
246: * RelativeDistinguishedName ::=
247: * SET OF AttributeTypeAndValue
248: *
249: */
250:
251: public static final ASN1SetOf ASN1_RDN = new ASN1SetOf(
252: AttributeTypeAndValue.ASN1);
253:
254: public static final ASN1SequenceOf ASN1 = new ASN1SequenceOf(
255: ASN1_RDN) {
256:
257: public Object getDecodedObject(BerInputStream in) {
258: return new Name((List) in.content);
259: }
260:
261: public Collection getValues(Object object) {
262: return ((Name) object).rdn; //FIXME what about get method?
263: }
264: };
265: }
|