X500Principal.java: Replaced with GNU Crypto's version.
authorAndreas Tobler <andreast@gcc.gnu.org>
Tue, 21 Sep 2004 08:35:11 +0000 (10:35 +0200)
committerAndreas Tobler <andreast@gcc.gnu.org>
Tue, 21 Sep 2004 08:35:11 +0000 (10:35 +0200)
2004-09-21  Andreas Tobler  <a.tobler@schweiz.ch>

* javax/security/auth/x500/X500Principal.java: Replaced with GNU
Crypto's version.

From-SVN: r87796

libjava/javax/security/auth/x500/X500Principal.java

index 6c7e79ee77bb16e244d01536927d052a10e1852b..95f80fc582303d0ad3e455706fa7012d482027ca 100644 (file)
@@ -38,16 +38,38 @@ exception statement from your version. */
 
 package javax.security.auth.x500;
 
-import gnu.java.security.x509.X500DistinguishedName;
+import gnu.java.security.OID;
+import gnu.java.security.der.BitString;
+import gnu.java.security.der.DER;
+import gnu.java.security.der.DEREncodingException;
+import gnu.java.security.der.DERReader;
+import gnu.java.security.der.DERValue;
 
+import java.io.ByteArrayInputStream;
 import java.io.IOException;
 import java.io.InputStream;
+import java.io.EOFException;
 import java.io.NotActiveException;
 import java.io.ObjectInputStream;
 import java.io.ObjectOutputStream;
+import java.io.Reader;
 import java.io.Serializable;
+import java.io.StringReader;
+
 import java.security.Principal;
 
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+
 public final class X500Principal implements Principal, Serializable
 {
   private static final long serialVersionUID = -500463348111345721L;
@@ -56,44 +78,66 @@ public final class X500Principal implements Principal, Serializable
   // ------------------------------------------------------------------------
 
   public static final String CANONICAL = "CANONICAL";
-
   public static final String RFC1779 = "RFC1779";
-
   public static final String RFC2253 = "RFC2253";
 
-  private transient X500DistinguishedName name;
+  private static final OID CN         = new OID("2.5.4.3");
+  private static final OID C          = new OID("2.5.4.6");
+  private static final OID L          = new OID("2.5.4.7");
+  private static final OID ST         = new OID("2.5.4.8");
+  private static final OID STREET     = new OID("2.5.4.9");
+  private static final OID O          = new OID("2.5.4.10");
+  private static final OID OU         = new OID("2.5.4.11");
+  private static final OID DC         = new OID("0.9.2342.19200300.100.1.25");
+  private static final OID UID        = new OID("0.9.2342.19200300.100.1.1");
+
+  private transient List components;
+  private transient Map currentRdn;
+  private transient boolean fixed;
+  private transient byte[] encoded;
 
   // Constructors.
   // ------------------------------------------------------------------------
 
-  public X500Principal(String name)
+  private X500Principal()
   {
-    if (name == null)
-      throw new NullPointerException();
-    this.name = new X500DistinguishedName(name);
+    components = new LinkedList();
+    currentRdn = new LinkedHashMap();
+    components.add (currentRdn);
   }
 
-  public X500Principal(byte[] encoded)
+  public X500Principal (String name)
   {
+    this();
+    if (name == null)
+      throw new NullPointerException();
     try
       {
-        name = new X500DistinguishedName(encoded);
+        parseString (name);
       }
     catch (IOException ioe)
       {
-        throw new IllegalArgumentException(ioe.toString());
+        IllegalArgumentException iae = new IllegalArgumentException("malformed name");
+        iae.initCause (ioe);
+        throw iae;
+      }
       }
+
+  public X500Principal (byte[] encoded)
+  {
+    this(new ByteArrayInputStream (encoded));
   }
 
-  public X500Principal(InputStream encoded)
+  public X500Principal (InputStream encoded)
   {
+    this();
     try
       {
-        name = new X500DistinguishedName(encoded);
+        parseDer (encoded);
       }
     catch (IOException ioe)
       {
-        throw new IllegalArgumentException(ioe.toString());
+        throw new IllegalArgumentException (ioe.toString());
       }
   }
 
@@ -102,42 +146,389 @@ public final class X500Principal implements Principal, Serializable
 
   public boolean equals(Object o)
   {
-    return ((X500Principal) o).name.equals(name);
+    if (!(o instanceof X500Principal))
+      return false;
+    if (size() != ((X500Principal) o).size())
+      return false;
+    for (int i = 0; i < size(); i++)
+      {
+        Map m = (Map) components.get (i);
+        for (Iterator it2 = m.entrySet().iterator(); it2.hasNext(); )
+          {
+            Map.Entry e = (Map.Entry) it2.next();
+            OID oid = (OID) e.getKey();
+            String v1 = (String) e.getValue();
+            String v2 = ((X500Principal) o).getComponent (oid, i);
+            if (v2 == null)
+              return false;
+            if (!compressWS (v1).equalsIgnoreCase (compressWS (v2)))
+              return false;
+          }
+      }
+    return true;
   }
 
   public byte[] getEncoded()
   {
-    return name.getEncoded();
+    if (encoded == null)
+      encodeDer();
+    return (byte[]) encoded.clone();
   }
 
   public String getName()
   {
-    return getName(RFC2253);
+    return getName (RFC2253);
+  }
+
+  public String getName (final String format)
+  {
+    boolean rfc2253 = RFC2253.equalsIgnoreCase (format) ||
+      CANONICAL.equalsIgnoreCase (format);
+    boolean rfc1779 = RFC1779.equalsIgnoreCase (format);
+    boolean canon   = CANONICAL.equalsIgnoreCase (format);
+    if (! (rfc2253 || rfc1779 || canon))
+      throw new IllegalArgumentException ("unsupported format " + format);
+    StringBuffer str = new StringBuffer();
+    for (Iterator it = components.iterator(); it.hasNext(); )
+      {
+        Map m = (Map) it.next();
+        for (Iterator it2 = m.entrySet().iterator(); it2.hasNext(); )
+          {
+            Map.Entry entry = (Map.Entry) it2.next();
+            OID oid = (OID) entry.getKey();
+            String value = (String) entry.getValue();
+            if (oid.equals (CN))
+              str.append ("CN");
+            else if (oid.equals (C))
+              str.append ("C");
+            else if (oid.equals (L))
+              str.append ("L");
+            else if (oid.equals (ST))
+              str.append ("ST");
+            else if (oid.equals (STREET))
+              str.append ("STREET");
+            else if (oid.equals (O))
+              str.append ("O");
+            else if (oid.equals (OU))
+              str.append ("OU");
+            else if (oid.equals (DC) && rfc2253)
+              str.append ("DC");
+            else if (oid.equals ("UID") && rfc2253)
+              str.append ("UID");
+            else
+              str.append (oid.toString());
+            str.append('=');
+            str.append(value);
+            if (it2.hasNext())
+              str.append('+');
+          }
+        if (it.hasNext())
+          str.append(',');
+      }
+    if (canon)
+      return str.toString().toUpperCase (Locale.US).toLowerCase (Locale.US);
+    return str.toString();
   }
 
-  public String getName(String format)
+  public String toString()
   {
-    if (format.equalsIgnoreCase(RFC2253))
-      return name.toRFC2253();
-    else if (format.equalsIgnoreCase(RFC1779))
-      return name.toRFC1779();
-    else if (format.equalsIgnoreCase(CANONICAL))
-      return name.toCanonical();
-    throw new IllegalArgumentException("unsupported format " + format);
+    return getName (RFC2253);
   }
 
   // Serialization methods.
   // ------------------------------------------------------------------------
 
-  private void writeObject(ObjectOutputStream out) throws IOException
+  private void writeObject (ObjectOutputStream out) throws IOException
   {
-    out.writeObject(name.getEncoded());
+    if (encoded != null)
+      encodeDer();
+    out.writeObject (encoded);
   }
 
-  private void readObject(ObjectInputStream in)
+  private void readObject (ObjectInputStream in)
     throws IOException, NotActiveException, ClassNotFoundException
   {
     byte[] buf = (byte[]) in.readObject();
-    name = new X500DistinguishedName(buf);
+    parseDer (new ByteArrayInputStream (buf));
+  }
+
+  // Own methods.
+  // -------------------------------------------------------------------------
+
+  private int size()
+  {
+    return components.size();
+  }
+
+  private String getComponent(OID oid, int rdn)
+  {
+    if (rdn >= size())
+      return null;
+    return (String) ((Map) components.get (rdn)).get (oid);
+  }
+
+  private void encodeDer()
+  {
+    ArrayList name = new ArrayList(components.size());
+    for (Iterator it = components.iterator(); it.hasNext(); )
+      {
+        Map m = (Map) it.next();
+        if (m.isEmpty())
+          continue;
+        Set rdn = new HashSet();
+        for (Iterator it2 = m.entrySet().iterator(); it2.hasNext(); )
+          {
+            Map.Entry e = (Map.Entry) it.next();
+            ArrayList atav = new ArrayList(2);
+            atav.add(new DERValue(DER.OBJECT_IDENTIFIER, e.getKey()));
+            atav.add(new DERValue(DER.UTF8_STRING, e.getValue()));
+            rdn.add(new DERValue(DER.SEQUENCE|DER.CONSTRUCTED, atav));
+          }
+        name.add(new DERValue(DER.SET|DER.CONSTRUCTED, rdn));
+      }
+    DERValue val = new DERValue(DER.SEQUENCE|DER.CONSTRUCTED, name);
+    encoded = val.getEncoded();
+  }
+
+  private int sep;
+
+  private void parseString(String str) throws IOException
+  {
+    Reader in = new StringReader(str);
+    while (true)
+      {
+        String key = readAttributeType(in);
+        if (key == null)
+          break;
+        String value = readAttributeValue(in);
+        putComponent(key, value);
+        if (sep == ',')
+          newRelativeDistinguishedName();
+      }
+  }
+
+  private String readAttributeType(Reader in) throws IOException
+  {
+    StringBuffer buf = new StringBuffer();
+    int ch;
+    while ((ch = in.read()) != '=')
+      {
+        if (ch == -1)
+          {
+            if (buf.length() > 0)
+              throw new EOFException();
+            return null;
+          }
+        if (ch > 127)
+          throw new IOException("Invalid char: " + (char) ch);
+        if (Character.isLetterOrDigit((char) ch) || ch == '-' || ch == '.')
+          buf.append((char) ch);
+        else
+          throw new IOException("Invalid char: " + (char) ch);
+      }
+    return buf.toString();
+  }
+
+  private String readAttributeValue(Reader in) throws IOException
+  {
+    StringBuffer buf = new StringBuffer();
+    int ch = in.read();
+    if (ch == '#')
+      {
+        while (true)
+          {
+            ch = in.read();
+            if (('a' <= ch && ch <= 'f') || ('A' <= ch && ch <= 'F')
+                || Character.isDigit((char) ch))
+              buf.append((char) ch);
+            else if (ch == '+' || ch == ',')
+              {
+                sep = ch;
+                String hex = buf.toString();
+                return new String(toByteArray(hex));
+              }
+            else
+              throw new IOException("illegal character: " + (char) ch);
+          }
+      }
+    else if (ch == '"')
+      {
+        while (true)
+          {
+            ch = in.read();
+            if (ch == '"')
+              break;
+            else if (ch == '\\')
+              {
+                ch = in.read();
+                if (ch == -1)
+                  throw new EOFException();
+                if (('a' <= ch && ch <= 'f') || ('A' <= ch && ch <= 'F')
+                    || Character.isDigit((char) ch))
+                  {
+                    int i = Character.digit((char) ch, 16) << 4;
+                    ch = in.read();
+                    if (!(('a' <= ch && ch <= 'f') || ('A' <= ch && ch <= 'F')
+                          || Character.isDigit((char) ch)))
+                      throw new IOException("illegal hex char");
+                    i |= Character.digit((char) ch, 16);
+                    buf.append((char) i);
+                  }
+                else
+                  buf.append((char) ch);
+              }
+            else
+              buf.append((char) ch);
+          }
+        sep = in.read();
+        if (sep != '+' || sep != ',')
+          throw new IOException("illegal character: " + (char) ch);
+        return buf.toString();
+      }
+    else
+      {
+        while (true)
+          {
+            switch (ch)
+              {
+              case '+':
+              case ',':
+                sep = ch;
+                return buf.toString();
+              case '\\':
+                ch = in.read();
+                if (ch == -1)
+                  throw new EOFException();
+                if (('a' <= ch && ch <= 'f') || ('A' <= ch && ch <= 'F')
+                    || Character.isDigit((char) ch))
+                  {
+                    int i = Character.digit((char) ch, 16) << 4;
+                    ch = in.read();
+                    if (!(('a' <= ch && ch <= 'f') || ('A' <= ch && ch <= 'F')
+                          || Character.isDigit((char) ch)))
+                      throw new IOException("illegal hex char");
+                    i |= Character.digit((char) ch, 16);
+                    buf.append((char) i);
+                  }
+                else
+                  buf.append((char) ch);
+                break;
+              case '=':
+              case '<':
+              case '>':
+              case '#':
+              case ';':
+                throw new IOException("illegal character: " + (char) ch);
+              case -1:
+                throw new EOFException();
+              default:
+                buf.append((char) ch);
+              }
+          }
+      }
+  }
+
+  private void parseDer (InputStream encoded) throws IOException
+  {
+    DERReader der = new DERReader (encoded);
+    DERValue name = der.read();
+    if (!name.isConstructed())
+      throw new IOException ("malformed Name");
+    this.encoded = name.getEncoded();
+    int len = 0;
+    while (len < name.getLength())
+      {
+        DERValue rdn = der.read();
+        if (!rdn.isConstructed())
+          throw new IOException ("badly formed RDNSequence");
+        int len2 = 0;
+        while (len2 < rdn.getLength())
+          {
+            DERValue atav = der.read();
+            if (!atav.isConstructed())
+              throw new IOException ("badly formed AttributeTypeAndValue");
+            DERValue val = der.read();
+            if (val.getTag() != DER.OBJECT_IDENTIFIER)
+              throw new IOException ("badly formed AttributeTypeAndValue");
+            OID oid = (OID) val.getValue();
+            val = der.read();
+            if (!(val.getValue() instanceof String))
+              throw new IOException ("badly formed AttributeTypeAndValue");
+            String value = (String) val.getValue();
+            putComponent(oid, value);
+            len2 += atav.getEncodedLength();
+          }
+        len += rdn.getEncodedLength();
+        if (len < name.getLength())
+          newRelativeDistinguishedName();
+      }
+  }
+
+  private void newRelativeDistinguishedName()
+  {
+    currentRdn = new LinkedHashMap();
+    components.add(currentRdn);
+  }
+
+  private void putComponent(OID oid, String value)
+  {
+    currentRdn.put(oid, value);
+  }
+
+  private void putComponent(String name, String value)
+  {
+    name = name.trim().toLowerCase();
+    if (name.equals("cn"))
+      putComponent(CN, value);
+    else if (name.equals("c"))
+      putComponent(C, value);
+    else if (name.equals("l"))
+      putComponent(L, value);
+    else if (name.equals("street"))
+      putComponent(STREET, value);
+    else if (name.equals("st"))
+      putComponent(ST, value);
+    else if (name.equals("dc"))
+      putComponent(DC, value);
+    else if (name.equals("uid"))
+      putComponent(UID, value);
+    else
+      putComponent(new OID(name), value);
+  }
+
+  private static String compressWS(String str)
+  {
+    StringBuffer buf = new StringBuffer();
+    char lastChar = 0;
+    for (int i = 0; i < str.length(); i++)
+      {
+        char c = str.charAt(i);
+        if (Character.isWhitespace(c))
+          {
+            if (!Character.isWhitespace(lastChar))
+              buf.append(' ');
+          }
+        else
+          buf.append(c);
+        lastChar = c;
+      }
+    return buf.toString().trim();
+  }
+
+  private static byte[] toByteArray (String str)
+  {
+    int limit = str.length();
+    byte[] result = new byte[((limit + 1) / 2)];
+    int i = 0, j = 0;
+    if ((limit % 2) == 1)
+      {
+        result[j++] = (byte) Character.digit (str.charAt(i++), 16);
+      }
+    while (i < limit)
+      {
+        result[j  ]  = (byte) (Character.digit (str.charAt(i++), 16) << 4);
+        result[j++] |= (byte)  Character.digit (str.charAt(i++), 16);
+      }
+    return result;
   }
 }