
2005-02-17 Andrew John Hughes <gnu_andrew@member.fsf.org> Michael Koch <konqueror@gmx.de> * gnu/java/net/protocol/http/Cookie.java, gnu/java/net/protocol/http/HTTPConnection.java, gnu/java/net/protocol/http/HTTPDateFormat.java, gnu/java/net/protocol/http/Request.java, gnu/java/nio/PipeImpl.java, gnu/java/rmi/dgc/DGCImpl.java, gnu/java/rmi/server/ConnectionRunnerPool.java, gnu/java/rmi/server/UnicastConnectionManager.java, gnu/java/security/der/DERWriter.java, gnu/java/security/pkcs/SignerInfo.java, gnu/java/security/provider/EncodedKeyFactory.java, gnu/java/security/provider/GnuDHPublicKey.java, gnu/java/security/provider/GnuDSAPrivateKey.java, gnu/java/security/provider/GnuDSAPublicKey.java, gnu/java/security/provider/PKIXCertPathValidatorImpl.java, gnu/java/security/x509/X500DistinguishedName.java, gnu/java/security/x509/X509CRL.java, gnu/java/security/x509/X509CRLEntry.java, gnu/java/security/x509/X509Certificate.java, gnu/java/security/x509/ext/AuthorityKeyIdentifier.java, gnu/java/security/x509/ext/CertificatePolicies.java, gnu/java/security/x509/ext/PolicyConstraint.java, gnu/xml/dom/Consumer.java, gnu/xml/dom/DomCharacterData.java, gnu/xml/dom/DomDocument.java, gnu/xml/dom/DomDocumentBuilder.java, gnu/xml/dom/DomIterator.java, gnu/xml/dom/DomNode.java, gnu/xml/dom/DomXPathExpression.java, gnu/xml/dom/DomXPathResult.java, gnu/xml/dom/JAXPFactory.java, gnu/xml/pipeline/CallFilter.java, gnu/xml/pipeline/DomConsumer.java, gnu/xml/pipeline/LinkFilter.java, gnu/xml/pipeline/NSFilter.java, gnu/xml/pipeline/TeeConsumer.java, gnu/xml/pipeline/ValidationConsumer.java, gnu/xml/pipeline/WellFormednessFilter.java, gnu/xml/pipeline/XIncludeFilter.java, gnu/xml/pipeline/XsltFilter.java, gnu/xml/transform/ApplyImportsNode.java, gnu/xml/transform/Bindings.java, gnu/xml/transform/DocumentFunction.java, gnu/xml/transform/FormatNumberFunction.java, gnu/xml/transform/NodeNumberNode.java, gnu/xml/transform/NumberNode.java, gnu/xml/transform/Stylesheet.java, gnu/xml/transform/SystemPropertyFunction.java, gnu/xml/transform/Template.java, gnu/xml/transform/TemplatesImpl.java, gnu/xml/transform/TransformerImpl.java, gnu/xml/transform/ValueOfNode.java, gnu/xml/transform/XSLURIResolver.java, gnu/xml/util/DoParse.java, gnu/xml/util/Resolver.java, gnu/xml/xpath/Expr.java, gnu/xml/xpath/FunctionCall.java, gnu/xml/xpath/RelationalExpr.java, gnu/xml/xpath/Selector.java, gnu/xml/xpath/XPathParser.java: Reworked import statements, fixed modifier order and some little formatting issues. Co-Authored-By: Michael Koch <konqueror@gmx.de> From-SVN: r95177
550 lines
17 KiB
Java
550 lines
17 KiB
Java
/* X500DistinguishedName.java -- X.500 distinguished name.
|
|
Copyright (C) 2004 Free Software Foundation, Inc.
|
|
|
|
This file is part of GNU Classpath.
|
|
|
|
GNU Classpath is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 2, or (at your option)
|
|
any later version.
|
|
|
|
GNU Classpath is distributed in the hope that it will be useful, but
|
|
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with GNU Classpath; see the file COPYING. If not, write to the
|
|
Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
|
02111-1307 USA.
|
|
|
|
Linking this library statically or dynamically with other modules is
|
|
making a combined work based on this library. Thus, the terms and
|
|
conditions of the GNU General Public License cover the whole
|
|
combination.
|
|
|
|
As a special exception, the copyright holders of this library give you
|
|
permission to link this library with independent modules to produce an
|
|
executable, regardless of the license terms of these independent
|
|
modules, and to copy and distribute the resulting executable under
|
|
terms of your choice, provided that you also meet, for each linked
|
|
independent module, the terms and conditions of the license of that
|
|
module. An independent module is a module which is not derived from
|
|
or based on this library. If you modify this library, you may extend
|
|
this exception to your version of the library, but you are not
|
|
obligated to do so. If you do not wish to do so, delete this
|
|
exception statement from your version. */
|
|
|
|
|
|
package gnu.java.security.x509;
|
|
|
|
import java.io.EOFException;
|
|
import java.io.InputStream;
|
|
import java.io.IOException;
|
|
import java.io.Reader;
|
|
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.Map;
|
|
import java.util.Set;
|
|
|
|
import gnu.java.security.der.DER;
|
|
import gnu.java.security.der.DERReader;
|
|
import gnu.java.security.der.DERValue;
|
|
import gnu.java.security.OID;
|
|
|
|
public class X500DistinguishedName implements Principal
|
|
{
|
|
|
|
// Constants and fields.
|
|
// -------------------------------------------------------------------------
|
|
|
|
public static final OID CN = new OID("2.5.4.3");
|
|
public static final OID C = new OID("2.5.4.6");
|
|
public static final OID L = new OID("2.5.4.7");
|
|
public static final OID ST = new OID("2.5.4.8");
|
|
public static final OID STREET = new OID("2.5.4.9");
|
|
public static final OID O = new OID("2.5.4.10");
|
|
public static final OID OU = new OID("2.5.4.11");
|
|
public static final OID T = new OID("2.5.4.12");
|
|
public static final OID DNQ = new OID("2.5.4.46");
|
|
public static final OID NAME = new OID("2.5.4.41");
|
|
public static final OID GIVENNAME = new OID("2.5.4.42");
|
|
public static final OID INITIALS = new OID("2.5.4.43");
|
|
public static final OID GENERATION = new OID("2.5.4.44");
|
|
public static final OID EMAIL = new OID("1.2.840.113549.1.9.1");
|
|
public static final OID DC = new OID("0.9.2342.19200300.100.1.25");
|
|
public static final OID UID = new OID("0.9.2342.19200300.100.1.1");
|
|
|
|
private List components;
|
|
private Map currentRdn;
|
|
private boolean fixed;
|
|
private String stringRep;
|
|
private byte[] encoded;
|
|
|
|
// Constructors.
|
|
// -------------------------------------------------------------------------
|
|
|
|
public X500DistinguishedName()
|
|
{
|
|
components = new LinkedList();
|
|
currentRdn = new LinkedHashMap();
|
|
components.add(currentRdn);
|
|
}
|
|
|
|
public X500DistinguishedName(String name)
|
|
{
|
|
this();
|
|
try
|
|
{
|
|
parseString(name);
|
|
}
|
|
catch (IOException ioe)
|
|
{
|
|
throw new IllegalArgumentException(ioe.toString());
|
|
}
|
|
}
|
|
|
|
public X500DistinguishedName(byte[] encoded) throws IOException
|
|
{
|
|
this();
|
|
parseDer(new DERReader(encoded));
|
|
}
|
|
|
|
public X500DistinguishedName(InputStream encoded) throws IOException
|
|
{
|
|
this();
|
|
parseDer(new DERReader(encoded));
|
|
}
|
|
|
|
// Instance methods.
|
|
// -------------------------------------------------------------------------
|
|
|
|
public String getName()
|
|
{
|
|
return toString();
|
|
}
|
|
|
|
public void newRelativeDistinguishedName()
|
|
{
|
|
if (fixed || currentRdn.isEmpty()) return;
|
|
currentRdn = new LinkedHashMap();
|
|
components.add(currentRdn);
|
|
}
|
|
|
|
public int size()
|
|
{
|
|
return components.size();
|
|
}
|
|
|
|
public int countComponents()
|
|
{
|
|
int count = 0;
|
|
for (Iterator it = components.iterator(); it.hasNext(); )
|
|
{
|
|
count += ((Map) it.next()).size();
|
|
}
|
|
return count;
|
|
}
|
|
|
|
public boolean containsComponent(OID oid, String value)
|
|
{
|
|
for (Iterator it = components.iterator(); it.hasNext(); )
|
|
{
|
|
Map rdn = (Map) it.next();
|
|
String s = (String) rdn.get(oid);
|
|
if (s == null)
|
|
continue;
|
|
if (compressWS(value).equalsIgnoreCase(compressWS(s)))
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public String getComponent(OID oid)
|
|
{
|
|
for (Iterator it = components.iterator(); it.hasNext(); )
|
|
{
|
|
Map rdn = (Map) it.next();
|
|
if (rdn.containsKey(oid))
|
|
return (String) rdn.get(oid);
|
|
}
|
|
return null;
|
|
}
|
|
|
|
public String getComponent(OID oid, int rdn)
|
|
{
|
|
if (rdn >= size())
|
|
return null;
|
|
return (String) ((Map) components.get(rdn)).get(oid);
|
|
}
|
|
|
|
public void putComponent(OID oid, String value)
|
|
{
|
|
currentRdn.put(oid, value);
|
|
}
|
|
|
|
public 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("t"))
|
|
putComponent(T, value);
|
|
else if (name.equals("dnq"))
|
|
putComponent(DNQ, value);
|
|
else if (name.equals("name"))
|
|
putComponent(NAME, value);
|
|
else if (name.equals("givenname"))
|
|
putComponent(GIVENNAME, value);
|
|
else if (name.equals("initials"))
|
|
putComponent(INITIALS, value);
|
|
else if (name.equals("generation"))
|
|
putComponent(GENERATION, value);
|
|
else if (name.equals("email"))
|
|
putComponent(EMAIL, value);
|
|
else if (name.equals("dc"))
|
|
putComponent(DC, value);
|
|
else if (name.equals("uid"))
|
|
putComponent(UID, value);
|
|
else
|
|
putComponent(new OID(name), value);
|
|
}
|
|
|
|
public void setUnmodifiable()
|
|
{
|
|
if (fixed) return;
|
|
fixed = true;
|
|
List newComps = new ArrayList(components.size());
|
|
for (Iterator it = components.iterator(); it.hasNext(); )
|
|
{
|
|
Map rdn = (Map) it.next();
|
|
rdn = Collections.unmodifiableMap(rdn);
|
|
newComps.add(rdn);
|
|
}
|
|
components = Collections.unmodifiableList(newComps);
|
|
currentRdn = Collections.EMPTY_MAP;
|
|
}
|
|
|
|
public int hashCode()
|
|
{
|
|
int sum = 0;
|
|
for (Iterator it = components.iterator(); it.hasNext(); )
|
|
{
|
|
Map m = (Map) it.next();
|
|
for (Iterator it2 = m.entrySet().iterator(); it2.hasNext(); )
|
|
{
|
|
Map.Entry e = (Map.Entry) it2.next();
|
|
sum += e.getKey().hashCode();
|
|
sum += e.getValue().hashCode();
|
|
}
|
|
}
|
|
return sum;
|
|
}
|
|
|
|
public boolean equals(Object o)
|
|
{
|
|
if (!(o instanceof X500DistinguishedName))
|
|
return false;
|
|
if (size() != ((X500DistinguishedName) 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 = ((X500DistinguishedName) o).getComponent(oid, i);
|
|
if (!compressWS(v1).equalsIgnoreCase(compressWS(v2)))
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
public String toString()
|
|
{
|
|
if (fixed && stringRep != null)
|
|
return stringRep;
|
|
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(T))
|
|
str.append("T");
|
|
else if (oid.equals(DNQ))
|
|
str.append("DNQ");
|
|
else if (oid.equals(NAME))
|
|
str.append("NAME");
|
|
else
|
|
str.append(oid.toString());
|
|
str.append('=');
|
|
str.append(value);
|
|
if (it2.hasNext())
|
|
str.append("+");
|
|
}
|
|
if (it.hasNext())
|
|
str.append(',');
|
|
}
|
|
return (stringRep = str.toString());
|
|
}
|
|
|
|
public byte[] getDer()
|
|
{
|
|
if (fixed && encoded != null)
|
|
return (byte[]) encoded.clone();
|
|
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);
|
|
return (byte[]) (encoded = val.getEncoded()).clone();
|
|
}
|
|
|
|
// Own methods.
|
|
// -------------------------------------------------------------------------
|
|
|
|
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();
|
|
}
|
|
setUnmodifiable();
|
|
}
|
|
|
|
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(Util.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(DERReader der) throws IOException
|
|
{
|
|
DERValue name = der.read();
|
|
if (!name.isConstructed())
|
|
throw new IOException("malformed Name");
|
|
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();
|
|
}
|
|
setUnmodifiable();
|
|
}
|
|
|
|
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();
|
|
}
|
|
}
|