/* * 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. */ package javax.security.auth.x500; import java.io.IOException; import java.io.InputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.security.Principal; import java.util.Map; import org.apache.harmony.security.x501.Name; /** * Represents an X.500 principal, which holds the distinguished name of some * network entity. An example of a distinguished name is {@code "O=SomeOrg, * OU=SomeOrgUnit, C=US"}. The class can be instantiated from a byte representation * of an object identifier (OID), an ASN.1 DER-encoded version, or a simple * string holding the distinguished name. The representations must follow either * RFC 2253, RFC 1779, or RFC2459. */ public final class X500Principal implements Serializable, Principal { private static final long serialVersionUID = -500463348111345721L; /** * Defines a constant for the canonical string format of distinguished * names. */ public static final String CANONICAL = "CANONICAL"; /** * Defines a constant for the RFC 1779 string format of distinguished * names. */ public static final String RFC1779 = "RFC1779"; /** * Defines a constant for the RFC 2253 string format of distinguished * names. */ public static final String RFC2253 = "RFC2253"; //Distinguished Name private transient Name dn; /** * Creates a new X500Principal from a given ASN.1 DER encoding of a * distinguished name. * * @param name * the ASN.1 DER-encoded distinguished name * * @throws IllegalArgumentException * if the ASN.1 DER-encoded distinguished name is incorrect */ public X500Principal(byte[] name) { if (name == null) { throw new IllegalArgumentException("Name cannot be null"); } try { // FIXME dn = new Name(name); dn = (Name) Name.ASN1.decode(name); } catch (IOException e) { throw incorrectInputEncoding(e); } } /** * Creates a new X500Principal from a given ASN.1 DER encoding of a * distinguished name. * * @param in * an {@code InputStream} holding the ASN.1 DER-encoded * distinguished name * * @throws IllegalArgumentException * if the ASN.1 DER-encoded distinguished name is incorrect */ public X500Principal(InputStream in) { if (in == null) { throw new NullPointerException("in == null"); } try { // FIXME dn = new Name(is); dn = (Name) Name.ASN1.decode(in); } catch (IOException e) { throw incorrectInputEncoding(e); } } private IllegalArgumentException incorrectInputEncoding(IOException e) { IllegalArgumentException iae = new IllegalArgumentException("Incorrect input encoding"); iae.initCause(e); throw iae; } /** * Creates a new X500Principal from a string representation of a * distinguished name. * * @param name * the string representation of the distinguished name * * @throws IllegalArgumentException * if the string representation of the distinguished name is * incorrect */ public X500Principal(String name) { if (name == null) { throw new NullPointerException("name == null"); } try { dn = new Name(name); } catch (IOException e) { throw incorrectInputName(e, name); } } public X500Principal(String name, Map<String,String> keywordMap){ if (name == null) { throw new NullPointerException("name == null"); } try { dn = new Name(substituteNameFromMap(name, keywordMap)); } catch (IOException e) { throw incorrectInputName(e, name); } } private IllegalArgumentException incorrectInputName(IOException e, String name) { IllegalArgumentException iae = new IllegalArgumentException("Incorrect input name:" + name); iae.initCause(e); throw iae; } private transient String canonicalName; private synchronized String getCanonicalName() { if (canonicalName == null) { canonicalName = dn.getName(CANONICAL); } return canonicalName; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || this.getClass() != o.getClass()) { return false; } X500Principal principal = (X500Principal) o; return getCanonicalName().equals(principal.getCanonicalName()); } /** * Returns an ASN.1 DER-encoded representation of the distinguished name * contained in this X.500 principal. * * @return the ASN.1 DER-encoded representation */ public byte[] getEncoded() { byte[] src = dn.getEncoded(); byte[] dst = new byte[src.length]; System.arraycopy(src, 0, dst, 0, dst.length); return dst; } /** * Returns a human-readable string representation of the distinguished name * contained in this X.500 principal. * * @return the string representation */ public String getName() { return dn.getName(RFC2253); } /** * Returns a string representation of the distinguished name contained in * this X.500 principal. The format of the representation can be chosen. * Valid arguments are {@link #RFC1779}, {@link #RFC2253}, and * {@link #CANONICAL}. The representations are specified in RFC 1779 and RFC * 2253, respectively. The canonical form is based on RFC 2253, but adds * some canonicalizing operations like removing leading and trailing * whitespace, lower-casing the whole name, and bringing it into a * normalized Unicode representation. * * @param format * the name of the format to use for the representation * * @return the string representation * * @throws IllegalArgumentException * if the {@code format} argument is not one of the three * mentioned above */ public String getName(String format) { if (CANONICAL.equals(format)) { return getCanonicalName(); } return dn.getName(format); } public String getName(String format, Map<String, String> oidMap) { String rfc1779Name = dn.getName(RFC1779); String rfc2253Name = dn.getName(RFC2253); if (format.equalsIgnoreCase("RFC1779")) { StringBuilder resultName = new StringBuilder(rfc1779Name); int fromIndex = resultName.length(); int equalIndex = -1; while (-1 != (equalIndex = resultName.lastIndexOf("=", fromIndex))) { int commaIndex = resultName.lastIndexOf(",", equalIndex); String subName = resultName.substring(commaIndex + 1, equalIndex).trim(); if (subName.length() > 4 && subName.substring(0, 4).equals("OID.")) { String subSubName = subName.substring(4); if (oidMap.containsKey(subSubName)) { String replaceName = oidMap.get(subSubName); if(commaIndex > 0) replaceName = " " + replaceName; resultName.replace(commaIndex + 1, equalIndex, replaceName); } } fromIndex = commaIndex; } return resultName.toString(); } else if (format.equalsIgnoreCase("RFC2253")) { StringBuilder resultName = new StringBuilder(rfc2253Name); StringBuilder subsidyName = new StringBuilder(rfc1779Name); int fromIndex = resultName.length(); int subsidyFromIndex = subsidyName.length(); int equalIndex = -1; int subsidyEqualIndex = -1; while (-1 != (equalIndex = resultName.lastIndexOf("=", fromIndex))) { subsidyEqualIndex = subsidyName.lastIndexOf("=", subsidyFromIndex); int commaIndex = resultName.lastIndexOf(",", equalIndex); String subName = resultName.substring(commaIndex + 1, equalIndex).trim(); if (oidMap.containsKey(subName)) { int subOrignalEndIndex = resultName .indexOf(",", equalIndex); if (subOrignalEndIndex == -1) subOrignalEndIndex = resultName.length(); int subGoalEndIndex = subsidyName.indexOf(",", subsidyEqualIndex); if (subGoalEndIndex == -1) subGoalEndIndex = subsidyName.length(); resultName.replace(equalIndex + 1, subOrignalEndIndex, subsidyName.substring(subsidyEqualIndex + 1, subGoalEndIndex)); resultName.replace(commaIndex + 1, equalIndex, oidMap .get(subName)); } fromIndex = commaIndex; subsidyFromIndex = subsidyEqualIndex - 1; } return resultName.toString(); } else { throw new IllegalArgumentException("invalid format specified: " + format); } } @Override public int hashCode() { return getCanonicalName().hashCode(); } @Override public String toString() { return dn.getName(RFC1779); } private void writeObject(ObjectOutputStream out) throws IOException { out.writeObject(dn.getEncoded()); } private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { dn = (Name) Name.ASN1.decode((byte[]) in.readObject()); } private String substituteNameFromMap(String name, Map<String, String> keywordMap) { StringBuilder sbName = new StringBuilder(name); int fromIndex = sbName.length(); int equalIndex; while (-1 != (equalIndex = sbName.lastIndexOf("=", fromIndex))) { int commaIndex = sbName.lastIndexOf(",", equalIndex); String subName = sbName.substring(commaIndex + 1, equalIndex).trim(); if (keywordMap.containsKey(subName)) { sbName.replace(commaIndex + 1, equalIndex, keywordMap.get(subName)); } fromIndex = commaIndex; } return sbName.toString(); } }