From fd375c53818b9a062f639ffd7e16786f6ae220fd Mon Sep 17 00:00:00 2001 From: Andreas Tobler Date: Tue, 21 Sep 2004 10:35:11 +0200 Subject: [PATCH] X500Principal.java: Replaced with GNU Crypto's version. 2004-09-21 Andreas Tobler * javax/security/auth/x500/X500Principal.java: Replaced with GNU Crypto's version. From-SVN: r87796 --- .../security/auth/x500/X500Principal.java | 449 ++++++++++++++++-- 1 file changed, 420 insertions(+), 29 deletions(-) diff --git a/libjava/javax/security/auth/x500/X500Principal.java b/libjava/javax/security/auth/x500/X500Principal.java index 6c7e79ee77b..95f80fc5823 100644 --- a/libjava/javax/security/auth/x500/X500Principal.java +++ b/libjava/javax/security/auth/x500/X500Principal.java @@ -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; } } -- 2.30.2