/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * @author Alexander V. Esin * @version $Revision$ */ package org.apache.harmony.security.x501; import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Locale; import javax.security.auth.x500.X500Principal; import org.apache.harmony.security.asn1.ASN1SequenceOf; import org.apache.harmony.security.asn1.ASN1SetOf; import org.apache.harmony.security.asn1.BerInputStream; import org.apache.harmony.security.asn1.DerInputStream; import org.apache.harmony.security.x509.DNParser; /** * X.501 Name */ public final class Name { /** ASN.1 DER encoding of Name */ private volatile byte[] encoded; /** RFC1779 string */ private String rfc1779String; /** RFC2253 string */ private String rfc2253String; /** CANONICAL string */ private String canonicalString; /** Collection of RDNs */ private List<List<AttributeTypeAndValue>> rdn; /** * Creates new <code>Name</code> instance from its DER encoding * * @param encoding - ASN.1 DER encoding * @throws IOException - if encoding is wrong */ public Name(byte[] encoding) throws IOException { DerInputStream in = new DerInputStream(encoding); if (in.getEndOffset() != encoding.length) { throw new IOException("Wrong content length"); } ASN1.decode(in); this.rdn = (List<List<AttributeTypeAndValue>>) in.content; } /** * Creates new <code>Name</code> instance * * @param name - Name as String * @throws IOException - if string is wrong */ public Name(String name) throws IOException { rdn = new DNParser(name).parse(); } private Name(List<List<AttributeTypeAndValue>> rdn) { this.rdn = rdn; } /** * Returns <code>X500Principal</code> instance corresponding to this * <code>Name</code> instance * * @return equivalent X500Principal object */ public X500Principal getX500Principal(){ return new X500Principal(getEncoded()); } /** * Returns Relative Distinguished Name as <code>String</code> according * the format requested * * @param format one of X500Principal.CANONICAL, X500Principal.RFC1779, or * X500Principal.RFC2253, case insensitive */ public String getName(String format) { // // check X500Principal constants first // if (X500Principal.RFC1779.equals(format)) { if (rfc1779String == null) { rfc1779String = getName0(format); } return rfc1779String; } else if (X500Principal.RFC2253.equals(format)) { if (rfc2253String == null) { rfc2253String = getName0(format); } return rfc2253String; } else if (X500Principal.CANONICAL.equals(format)) { if (canonicalString == null) { canonicalString = getName0(format); } return canonicalString; } // // compare ignore case // else if (X500Principal.RFC1779.equalsIgnoreCase(format)) { if (rfc1779String == null) { rfc1779String = getName0(X500Principal.RFC1779); } return rfc1779String; } else if (X500Principal.RFC2253.equalsIgnoreCase(format)) { if (rfc2253String == null) { rfc2253String = getName0(X500Principal.RFC2253); } return rfc2253String; } else if (X500Principal.CANONICAL.equalsIgnoreCase(format)) { if (canonicalString == null) { canonicalString = getName0(X500Principal.CANONICAL); } return canonicalString; } else { throw new IllegalArgumentException("Illegal format: " + format); } } /** * Returns Relative Distinguished Name as <code>String</code> according * the format requested, format is int value */ private String getName0(String format) { StringBuilder name = new StringBuilder(); // starting with the last element and moving to the first. for (int i = rdn.size() - 1; i >= 0; i--) { List<AttributeTypeAndValue> atavList = rdn.get(i); if (X500Principal.CANONICAL == format) { atavList = new ArrayList<AttributeTypeAndValue>(atavList); Collections.sort(atavList, new AttributeTypeAndValueComparator()); } // Relative Distinguished Name to string Iterator<AttributeTypeAndValue> it = atavList.iterator(); while (it.hasNext()) { AttributeTypeAndValue attributeTypeAndValue = it.next(); attributeTypeAndValue.appendName(format, name); if (it.hasNext()) { // multi-valued RDN if (X500Principal.RFC1779 == format) { name.append(" + "); } else { name.append('+'); } } } if (i != 0) { name.append(','); if (format == X500Principal.RFC1779) { name.append(' '); } } } String sName = name.toString(); if (X500Principal.CANONICAL.equals(format)) { sName = sName.toLowerCase(Locale.US); } return sName; } /** * Gets encoded form of DN * * @return return encoding, no copying is performed */ public byte[] getEncoded() { if (encoded == null) { encoded = ASN1.encode(this); } return encoded; } /** * According to RFC 3280 (http://www.ietf.org/rfc/rfc3280.txt) * X.501 Name structure is defined as follows: * * Name ::= CHOICE { * RDNSequence } * * RDNSequence ::= SEQUENCE OF RelativeDistinguishedName * * RelativeDistinguishedName ::= * SET OF AttributeTypeAndValue * */ public static final ASN1SetOf ASN1_RDN = new ASN1SetOf( AttributeTypeAndValue.ASN1); public static final ASN1SequenceOf ASN1 = new ASN1SequenceOf(ASN1_RDN) { public Object getDecodedObject(BerInputStream in) { return new Name((List<List<AttributeTypeAndValue>>) in.content); } public Collection getValues(Object object) { return ((Name) object).rdn; } }; }